what is jdbc

220
What Is JDBC? JDBC is an API (Application Programming Interface) that provides universal database access for the Java programming language. JDBC is the trademarked name and is not an acronym. But JDBC is often thought of as standing for "Java Database Connectivity." The current version of JDBC API is JDBC 4.0 API implemented in Java SE 6. It includes two packages: JDBC 4.0 Core API - The java.sql package, which is distributed as part of Java SE 6. The core API is good enough for normal database applications. JDBC 4.0 Standard Extension API - The javax.sql package, which is distributed as part of Java Se 6. The standard extension API, javax.sql, can also be downloaded from http://java.sun.com/products/jdbc. It is required for applications that uses connection pooling, distributed transactions, Java Naming and Directory Interfacetm (JNDI), and RowSet API. The specificatin of JDBC 4.0 API is documented in JSR (Java Specification Request) 221 maintained by JCP (Java Community Process). See JSR 221: JDBC 4.0 API Specification . In order to use JDBC to connect Java applications to a specific database server, you need to have a JDBC driver that supports JDBC API for that database server. For example, Microsoft JDBC Drive allows you to access Microsoft SQL Server through the JDBC API. JDBC Version and History The following table lists JDBC past versions and implementations: Year JDBC Version JSR Specification JDK Implementation 2006 JDBC 4.0 JSR 221 Java SE 6 2001 JDBC 3.0 JSR 54 JDK 1.4 1999 JDBC 2.1 JDK 1.2? 1997 JDBC 1.2 JDK 1.1?

Upload: md-imam

Post on 28-Mar-2015

2.068 views

Category:

Documents


8 download

TRANSCRIPT

What Is JDBC?JDBC is an API (Application Programming Interface) that provides universal database access for the Java programming language. JDBC is the trademarked name and is not an acronym. But JDBC is often thought of as standing for "Java Database Connectivity."

The current version of JDBC API is JDBC 4.0 API implemented in Java SE 6. It includes two packages:

JDBC 4.0 Core API - The java.sql package, which is distributed as part of Java SE 6. The core API is good enough for normal database applications.

JDBC 4.0 Standard Extension API - The javax.sql package, which is distributed as part of Java Se 6. The standard extension API, javax.sql, can also be downloaded from http://java.sun.com/products/jdbc. It is required for applications that uses connection pooling, distributed transactions, Java Naming and Directory Interfacetm (JNDI), and RowSet API.

The specificatin of JDBC 4.0 API is documented in JSR (Java Specification Request) 221 maintained by JCP (Java Community Process). See JSR 221: JDBC 4.0 API Specification.

In order to use JDBC to connect Java applications to a specific database server, you need to have a JDBC driver that supports JDBC API for that database server. For example, Microsoft JDBC Drive allows you to access Microsoft SQL Server through the JDBC API.

JDBC Version and HistoryThe following table lists JDBC past versions and implementations:

Year JDBC Version JSR Specification JDK Implementation

2006 JDBC 4.0 JSR 221 Java SE 62001 JDBC 3.0 JSR 54 JDK 1.41999 JDBC 2.1 JDK 1.2?1997 JDBC 1.2 JDK 1.1?

Main new features introduced in JDBC 4.0 API are:

Automatic loading of java.sql.Driver ROWID data type support National Character Set Conversion Support SQL/XML and XML Support

Note that JDBC 4.0 API has been implemented in Java SE 6. But JDBC drivers of specific database servers for JDBC 4.0 API may still not available. Since JDBC 4.0 API is backward compatible, there is no problem with using Java SE 6 with JDBC 3.0 drivers, as long as you do not use the new methods or classes there were introduced in JDBC 4.0 API.

JDBC Driver TypesJDBC drivers can be implemented in 4 ways. So JDBC drivers are divided into 4 types:

JDBC Type 1: JDBC-ODBC Bridge plus ODBC Driver. See the left side of the first picture shown below. This combination provides JDBC access via ODBC drivers. ODBC binary code, and in many cases, database client code, must be loaded on each client machine that uses a JDBC-ODBC Bridge. Sun provides a JDBC-ODBC Bridge driver, which is appropriate for experimental use and for situations in which no other driver is available.

JDBC Type 2: A native API partly Java technology-enabled driver. See the right side of the first picture shown below. This type of driver converts JDBC calls into calls on the client API for Oracle, Sybase, Informix, DB2, or other DBMS. Note that, like the bridge driver, this style of driver requires that some binary code be loaded on each client machine.

JDBC Type 3: Pure Java Driver for Database Middleware. See the right side of the second picture shown below. This style of driver translates JDBC calls into the middleware vendor's protocol, which is then translated to a DBMS protocol by a middleware server. The middleware provides connectivity to many different databases.

JDBC Type 4: Direct-to-Database Pure Java Driver. See the left side of the second picture shown below. This style of driver converts JDBC calls into the network protocol used directly by DBMSs, allowing a direct call from the client machine to the DBMS server and providing a practical solution for intranet access. For example, Microsoft JDBC Driver 1.0 is a Type 4 JDBC driver.

Establishing Connections from JDBC to DatabasesJDBC 4.0 API offers two different ways to establish a connection to the database server:

1. Using DrirverManager Class: DriverManager.getConnection(connection_url) - The driver manager passes the connection URL to all loaded JDBC drivers, hoping that one of them will recognize the URL and creates a connection. See sample code below:

// Loading a JDBC driver Class.forName("acme.db.Driver");

// Creating a connection String url = "jdbc:odbc:fred"; Connection con = DriverManager.getConnection(url,"user","pass");

2. Using DataSource Object: ds.getConnection() - A DataSource object should be configured and registered with a JNDI (Java Naming and Directory Interface) directory service only once. When a connection is needed, the registered DataSource object can be retrieved back from JNDI. A connection can be then created from the retrieved DataSource object.

// Registering a DataSource VendorDataSource vds = new VendorDataSource(); vds.setServerName("my_database_server"); vds.setDatabaseName("my_database"); vds.setDescription("the data source for inventory and personnel"); Context ctx = new InitialContext(); ctx.bind("jdbc/AcmeDB", vds);

// Creating a connection Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("jdbc/AcmeDB"); Connection con = ds.getConnection("genius", "abracadabra");

JDK documentation suggests to use Database object to create connection objects whenever possible.

DriverManager - Loading JDBC DriverIf you want to use DriverManager class to create a connection to a database server, you need to load a JDBC driver that knows how to create a connection to that database server. The loaded JDBC driver class will be automatically registered to DriverManager.

There are two ways to load a JDBC driver:

Using the Class.forName() method - Loading the specified driver class when you need it.

Using the java.lang.System property jdbc.drivers setting - Loading the specified driver classes when the first call to a DriverManager method is made.

I wrote the following program to test both ways of loading JDBC drivers. To test this program, you need to download Microsoft JDBC Driver 1.0 as described in another tutorial in this book.

/** * LoadJdbcDriver.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;import java.util.*;public class LoadJdbcDriver { public static void main(String [] args) { Connection con = null; try { System.out.println("Before loading SQLServerDriver:"); listDrivers();

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); System.out.println("After loading SQLServerDriver:"); listDrivers(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } } private static void listDrivers() { Enumeration driverList = DriverManager.getDrivers(); while (driverList.hasMoreElements()) { Driver driverClass = (Driver) driverList.nextElement(); System.out.println(" "+driverClass.getClass().getName()); } }}

Test 1: Load Microsoft JDBC Driver 1.0 with Class.forName() by follow the commands below:

C:\>javac LoadJdbcDriver.java

C:\>java -cp .;\local\lib\sqljdbc.jar LoadJdbcDriverBefore loading SQLServerDriver: sun.jdbc.odbc.JdbcOdbcDriverAfter loading SQLServerDriver: sun.jdbc.odbc.JdbcOdbcDriver com.microsoft.sqlserver.jdbc.SQLServerDriver

Test 2: Load Microsoft JDBC Driver 1.0 with jdbc.drivers property by follow the commands below:

C:\>javac LoadJdbcDriver.java

C:\>java -cp .;\local\lib\sqljdbc.jar -Djdbc.drivers="com.microsoft.sqlserver.jdbc.SQLServerDriver" LoadJdbcDriver

Before loading SQLServerDriver: sun.jdbc.odbc.JdbcOdbcDriver com.microsoft.sqlserver.jdbc.SQLServerDriverAfter loading SQLServerDriver: sun.jdbc.odbc.JdbcOdbcDriver com.microsoft.sqlserver.jdbc.SQLServerDriver

What I learned from the outputs of the two tests:

sun.jdbc.odbc.JdbcOdbcDriver, the JDBC-ODBC Bridge driver is loaded automatically by the JVM, because it showed up in the driver list automatically.

The loaded driver class is automatically registered to the DriverManager class, because I didn't call any DriverManager method to register any class.

The "-Djdbc.drivers=*" option worked correctly to bring driver classes to the DriverManager class, because the Microsoft class specified in the option showed in the driver list before the call of the forName() method.

DriverManager - Connection URLAs we learned earlier, the traditional way to create a connection object is to use the DriverManager class with a connection URL in the following format:

jdbc:<subprotocol>:<subname>

<subprotocol> in the URL is used to identify the JDBC driver class which will create a connection object based on information provided in <subname>. For example, "odbc" in the connection URL "jdbc:odbc:HY_FLAT" identifies the JDBC-ODBC Bridge driver. "sqlserver" in "jdbc:sqlserver://localhost:1269" identifies the Microsoft JDBC Driver.

<subname> in the URL is used to provide additional information to help the JDBC driver to identify the database server. If the database server is on remote host on the Internet, <subname> should have the following format:

jdbc:<subprotocol>://<hostname>:port<subname>

For example, "HY_FLAT" in the connection URL "jdbc:odbc:HY_FLAT" provides the data source name to help JDBC-ODBC Bridge driver to create a connection object. "//localhost:1269" in "jdbc:sqlserver://localhost:1269" provides the host name and the port number to help Microsoft JDBC Driver to create a connection object.

The DriverManager class offers 3 methods for you to create a connection object using the specified connection URL:

Connection con = DriverManager.getConnection(String url);

Connection con = DriverManager.getConnection(String url, Properties info)

Connection con = DriverManager.getConnection(String url, String user, String password)

Tutorials of using connection URLs are included in other sections in this book.

Java SE 1.6 Update 2 InstallationTo learn JDBC, you have to a copy of JDK (Java Development Kit) installed on your machine. The latest version of JDK is JDK 6u2 (Java(TM) SE Development Kit 6 Update 2), which is also called Java SE 6 (Java Standard Edition 6). Here is what I did to download and install JDK 6u2 on my local machine.

Open the Java SE Download page with this URL: http://java.sun.com/javase/downloads/.

Click the download button next to "JDK 6u2". You will see a new page with a list of different download files of JDK 6u2.

Locate the "Windows Platform - Java(TM) SE Development Kit 6 Update 2" section.

Click the hyper link of "Windows Offline Installation (build 06), Multi-language", which links to jdk-6u2-windows-i586-p.exe with size of 65.57 MB.

Save jdk-6u2-windows-i586-p.exe to a temporary directory. Double-click on jdk-6u2-windows-i586-p.exe to start the installation wizard. The installation wizard will guide you to finish the installation.

To test the installation, open a command window to try the java command. If you are getting the following output, your installation was ok:

C:\>\progra~1\java\jdk1.6.0_02\bin\java -versionjava version "1.6.0_02"Java(TM) SE Runtime Environment (build 1.6.0_02-b06)Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode, sharing)

JDK Documentation InstallationTo learn JDBC more effectively, you definitely need to have a copy of the JDK Documentation installed on your own machine. JDK Documentation provides you

detailed information of all classes and methods used in JDBC. Here is what I did to download and install JDK 6 Documentation on my local machine.

Open the Java SE Download page with this URL: http://java.sun.com/javase/downloads/.

Click the download button next to "Java SE 6 Documentation". You will see Java(TM) SE Development Kit Documentation 6 page showing up.

Check the Accept License Agreement radio button. Click the hyper link of "Java(TM) SE Development Kit Documentation 6,

English", which links to jdk-6-doc.zip with size of 52.7 MB. Save jdk-6-doc.zip to a temporary directory. Double-click on jdk-6-doc.zip to extract all files to C:\Program Files\Java\

jdk1.6.0_02 directory. The installation of JDK 6 Documentation is done.

To verify the installation, I opened the C:\Program Files\Java\jdk1.6.0_02\docs\index.html with IE (Internet Explorer). You should see the JDK 5 Documentation starting page as shown in this picture:

MySQL Installation on WindowsThis chapter provides tutorial notes on MySQL installation on Windows systems. Topics include downloading, installing and starting MySQL database server; using mysqladmin tool; using command line tool; creating new database and new user.

MySQL 5.0 Download, Installation and Start

mysqladmin - MySQL Admin Tool

mysql - Command Line Tool

Creating Database and User with MySQL Monitor

MySQL 5.0 Download, Installation and StartMySQL is an open source database management system developed by MySQL AB, http://www.mysql.com. You can download a copy and install it on your local Windows system very easily. Here is what I did to download and install MySQL 5.0 Community Server on my Windows system:

Go to MySQL 5.0 MySQL 5.0 Community Server download page. Select the "Windows" and "Without installer" version. Find a mirror site and download "mysql-noinstall-5.0.45-win32.zip". The file size

is about 51 MB. Unzip the file, you will get a new sub-directory, ".\mysql-5.0.45-win32". Move and rename this sub-directory to \local\mysql. The installation is done and your MySQL server is ready.

To start the MySQL server, you can run the "mysqld" program in a command window as shown in the following tutorial:

C:\>cd \local\mysql\binC:\local\mysql\bin>mysqld

"mysqld" will run quietly without printing any message in you command window. So you will see nothing after entering the "mysqld" command. You should trust "mysqld" and believe that MySQL server is running ok on your local computer now.

Another way to start the MySQL server is double-click \mysql\bin\mysqld.exe on your file explorer window.

mysqladmin - MySQL Admin ToolIf you want to know whether your MySQL server is alive, you can use the "mysqladmin" program in a command window as shown in the following tutorial:

C:\>cd \local\mysql\binC:\local\mysql\bin>mysqladmin -u root pingmysqld is alive

The "mysqld is alive" message tells you that your MySQL server is running ok. If your MySQL server is not running, you will get a "connect ... failed" message.

You can also use "mysqladmin" program to check version number of your MySQL server in a command window as shown in the following tutorial:

C:\>cd \local\mysql\binC:\local\mysql\bin>mysqladmin -u root version\local\mysql\bin\mysqladmin Ver 8.41 Distrib 5.0.45, for Win32 on ia32Copyright (C) 2000-2006 MySQL ABThis software comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to modify and redistribute it under the GPL license

Server version 5.0.45-community-ntProtocol version 10Connection localhost via TCP/IPTCP port 3306Uptime: 10 min 38 sec

Threads: 1 Questions: 2 Slow queries: 0 Opens: 12 Flush tables: 1Open tables: 6 Queries per second avg: 0.003

The output in the above example tells you that the version number is 5.0.45.

If you want to know what else you can do with "mysqladmin", you should run the "-?" option:

C:\>cd \local\mysql\bin

C:\local\mysql\bin>mysqladmin -?...

Administration program for the mysqld daemon.Usage: \local\mysql\bin\mysqladmin [OPTIONS] command command.... -c, --count=# Number of iterations to make. This works with -i (--sleep) only....

Where command is a one or more of: (Commands may be shortened) create databasename Create a new database debug Instruct server to write debug information drop databasename Delete a database and all its tables extended-status Gives an extended status message flush-hosts Flush all cached hosts flush-logs Flush all logs flush-status Clear status variables flush-tables Flush all tables flush-threads Flush the thread cache flush-privileges Reload grant tables (same as reload) kill id,id,... Kill mysql threads password new-password Change old password to new-password, MySQL 4.1 hashing. old-password new-password Change old password to new-password in old format. ping Check if mysqld is alive processlist Show list of active threads in server reload Reload grant tables refresh Flush all tables and close and open logfiles shutdown Take server down status Gives a short status message from the server start-slave Start slave stop-slave Stop slave variables Prints variables available version Get version info from server

mysql - Command Line Tool"mysql", official name is "MySQL monitor", is a command-line interface for end users to manage user data objects. "mysql" has the following main features:

"mysql" is command line interface. It is not a Graphical User Interface (GUI). "mysql" supports all standard SQL Data Definition Language (DDL) commands

for the server to execute. "mysql" supports all standard SQL Data Manipulation Language (DML)

commands for the server to execute. "mysql" supports many of non-SQL commands that "mysql" will execute by

itself. "mysql" provides access to the server-side help system. "mysql" allows command files to be executed in a batch mode. "mysql" allows query output to be formatted as HTML tables. "mysql" allows query output to be formatted as XML elements.

Here is how I run "mysql" and get the "help" information:

C:\>\local\mysql\bin\mysql -u rootWelcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 2Server version: 5.0.45-community-nt MySQL Community Edition (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> help;...

List of all MySQL commands:Note that all text commands must be first on line and end with ';'? (\?) Synonym for `help'.clear (\c) Clear command.connect (\r) Reconnect to the server. Optional arguments are db and host.delimiter (\d) Set statement delimiter. NOTE: Takes the rest of the line as new delimiter.ego (\G) Send command to mysql server, display result vertically.exit (\q) Exit mysql. Same as quit.go (\g) Send command to mysql server.help (\h) Display this help.notee (\t) Don't write into outfile.print (\p) Print current command.prompt (\R) Change your mysql prompt.quit (\q) Quit mysql.rehash (\#) Rebuild completion hash.source (\.) Execute an SQL script file. Takes a file name as an argument.status (\s) Get status information from the server.tee (\T) Set outfile [to_outfile]. Append everything into given outfile.use (\u) Use another database. Takes database name as argument.charset (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.warnings (\W) Show warnings after every statement.nowarning (\w) Don't show warnings after every statement.

For server side help, type 'help contents'

Creating Database and User with MySQL MonitorMySQL server organizes database objects into databases. In order to test JDBC driver for MySQL server, I created a new database called, HerongDB, and a new user, Herong, with the MySQL command line interface:

C:\>\local\mysql\bin\mysql -u root

mysql> CREATE DATABASE HerongDB;Query OK, 1 row affected (0.03 sec)

mysql> SHOW DATABASES;+--------------------+

| Database |+--------------------+| information_schema || herongdb || mysql || test |+--------------------+4 rows in set (0.00 sec)

mysql> CREATE USER Herong IDENTIFIED BY 'TopSecret';Query OK, 0 rows affected (2.75 sec)

mysql> GRANT ALL ON HerongDB.* TO Herong;Query OK, 0 rows affected (0.34 sec)

Now I am ready to login to the MySQL server with "Herong" and use "HerongDB" through Java JDBC interface.

MySQL JDBC Driver (MySQL Connector/J)This chapter provides tutorial notes on MySQL JDBC driver. Topics include installing MySQL JDBC driver; connection URL and DriverManager class; DataSource and Connection objects; server and driver information; creating tables with auto-increment columns.

MySQL Connector/J - Download and Installation

Loading JDBC Driver Class - mysql-connector-java-5.0.7-bin.jar

JDBC Driver Connection URL

Creating Connections with DataSource Class

Getting Driver and Server Information

Creating Tables with AUTO_INCREMENT Columns

"INSERT INTO" Statements

MySQL Connector/J - Download and InstallationWith MySQL server installed and running, I am ready to try the JDBC driver to access my MySQL server. Here is what I did to download and install MySQL JDBC driver:

Go to the MySQL Drivers page. Click Download link next to "JDBC Driver for MySQL (Connector/J)" Click the Download link next to "Source and Binaries (zip) 5.0.7 8.3M" Save the download file, mysql-connector-java-5.0.7.zip, to a temporary directory.

The file size is about 8.5MB. Unzip this file. I got a directory called mysql-connector-java-5.0.7. I moved and

renamed this directory to \local\mysql-connector-java.

Finally, copy \local\mysql-connector-java\mysql-connector-java-5.0.7-bin.jar to \local\lib\mysql-connector-java-5.0.7-bin.jar.

The installation is done. You should read the documentation at \local\mysql-connector-java\docs\connector-j.pdf. Some version information of MySQL Connctor/J 5.0.7 is summarized here:

MySQL Connctor/J 5.0.7 implements version 3.0 of the JDBC specification. MySQL Connctor/J 5.0.7 is a Type 4 driver - Pure Java and direct connection to

the server. MySQL Connctor/J 5.0.7 supports JDK 1.5.x environments.

Loading JDBC Driver Class - mysql-connector-java-5.0.7-bin.jarMySQL JDBC driver 5.0.7 is a JDBC 3.0 driver. The driver class, com.mysql.jdbc.Driver, needs to be loaded, if you want to use the DriverManager class to create Connection objects. The simplest way to load a driver class is to call the Class.forName() method as shown in the following sample program. Of course, you have to include the mysql-connector-java-5.0.7-bin.jar file in the classpath when you execute this program:

/** * MySqlLoadDriver.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlLoadDriver { public static void main(String [] args) { Connection con = null; try {

// Load the MySQL JDBC driver Class.forName("com.mysql.jdbc.Driver") ; System.out.println("MySQL JDBC driver loaded ok."); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

The compilation and execution tests were recorded below. Notice the exception error I got without mysql-connector-java-5.0.7-bin.jar in the classpath:

C:\>javac MySqlLoadDriver.java

C:\>java MySqlLoadDriverException: com.mysql.jdbc.Driver

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlLoadDriver

MySQL JDBC driver loaded ok.

JDBC Driver Connection URLIf you want to use the DriverManager class to create connection objects, you need to know how to make a connection URL that provides access information to the MySQL server. The MySQL connection URL has the following format:

jdbc:mysql://[host][:port]/[database][?property1][=value1]...

host - The host name where MySQL server is running. Default is 127.0.0.1 - the IP address of localhost.

port - The port number where MySQL is listening for connection. Default is 3306.

Database - The name of an existing database on MySQL server. If not specified, the connection starts no current database.

Property - The name of a supported connection properties. "user" and "password" are 2 most important properties. Value - The value for the specified connection property.

Here are some example connection URLs:

jdbc:mysql://localhost:3306/HerongDB?user=Herong&password=TopSecretjdbc:mysql://:3306/HerongDB?user=Herong&password=TopSecretjdbc:mysql://localhost/HerongDB?user=Herong&password=TopSecretjdbc:mysql://localhost:3306/?user=Herong&password=TopSecretjdbc:mysql://localhost/?user=Herong&password=TopSecretjdbc:mysql://:3306/?user=Herong&password=TopSecretjdbc:mysql:///HerongDB?user=Herong&password=TopSecretjdbc:mysql:///?user=Herong&password=TopSecret

I wrote the following program to validate some of the connection URLs listed above:

/** * MySqlConnectionUrl.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlConnectionUrl { public static void main(String [] args) { Connection con = null; try { Class.forName("com.mysql.jdbc.Driver") ; System.out.println("MySQL JDBC driver loaded ok."); con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/HerongDB?" + "user=Herong&password=TopSecret"); System.out.println("Connected with host:port/database."); con.close();

con = DriverManager.getConnection( "jdbc:mysql://:3306/HerongDB?" + "user=Herong&password=TopSecret"); System.out.println("Connected with default host."); con.close();

con = DriverManager.getConnection( "jdbc:mysql://localhost/HerongDB?" + "user=Herong&password=TopSecret"); System.out.println("Connected with default port."); con.close();

con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/?" + "user=Herong&password=TopSecret"); System.out.println("Connected with no database."); con.close();

con = DriverManager.getConnection( "jdbc:mysql:///?" + "user=Herong&password=TopSecret"); System.out.println("Connected with properties only."); con.close();

} catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Here is the output:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlConnectionUrl

MySQL JDBC driver loaded ok.Connected with host:port/database.Connected with default host.Connected with default port.Connected with no database.Connected with properties only.

Creating Connections with DataSource ClassIt is recommended now that connection objects are created by the DataSource implementation class, com.mysql.jdbc.jdbc2.optional.MysqlDataSource. Here is a sample program that creates a connection object using the DataSource class without using JNDI services:

/** * MySqlDataSource.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;import javax.sql.*;

public class MySqlDataSource { public static void main(String [] args) { Connection con = null; try {

// Setting up the DataSource object com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret");

// Getting a connection object con = ds.getConnection(); // Getting database info DatabaseMetaData meta = con.getMetaData(); System.out.println("Server name: " + meta.getDatabaseProductName()); System.out.println("Server version: " + meta.getDatabaseProductVersion());

// Closing the connection con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

The output confirms that I got a good connection. Remember to include mysql-connector-java-5.0.7-bin.jar in the classpath for compilation and execution:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlDataSource.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlDataSource

Server name: MySQLServer version: 5.0.45-community-nt

Getting Driver and Server InformationOnce you have created a database connection object, you can obtain some version information about the JDBC driver and database server through the DatabaseMetaData object as shown in the following program:

/** * MySqlDatabaseInfo.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;

import javax.sql.*;public class MySqlDatabaseInfo { public static void main(String [] args) { Connection con = null; try {

// Setting up the DataSource object com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret");

// Getting a connection object con = ds.getConnection(); // Getting driver and database info DatabaseMetaData meta = con.getMetaData(); System.out.println("Server name: " + meta.getDatabaseProductName()); System.out.println("Server version: " + meta.getDatabaseProductVersion()); System.out.println("Driver name: " + meta.getDriverName()); System.out.println("Driver version: " + meta.getDriverVersion()); System.out.println("JDBC major version: " + meta.getJDBCMajorVersion()); System.out.println("JDBC minor version: " + meta.getJDBCMinorVersion());

// Closing the connection con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

The output confirms that JDBC driver mysql-connector-java-5.0.7 a JDBC 3.0 driver:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlDatabaseInfo.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlDatabaseInfo

Server name: MySQLServer version: 5.0.45-community-ntDriver name: MySQL-AB JDBC DriverDriver version: mysql-connector-java-5.0.7 ( $Date: 2007-03-09 22:13:57 +0100 (Fri, 09 Mar 2007) $, $Revision: 6341 $ )JDBC major version: 3

JDBC minor version: 0

Creating Tables with AUTO_INCREMENT ColumnsDifferent database servers have different ways to support columns with automatically incremented values. MySQL uses the key word AUTO_INCREMENT to specify an auto-incrementing column.

In order to try AUTO_INCREMENT columns and provide a test table for testing DML (Data Manipulation Statements), I wrote the following program to create a table called "Profile" with the primary key column defined as an AUTO_INCREMENT column:

/** * MySqlAutoIncrement.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;import javax.sql.*;public class MySqlAutoIncrement { public static void main(String [] args) { Connection con = null; try { // Setting up the DataSource object com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret");

// Getting a connection object con = ds.getConnection();

// Creating a database table Statement sta = con.createStatement(); int count = sta.executeUpdate( "CREATE TABLE Profile (" + " ID INTEGER PRIMARY KEY AUTO_INCREMENT," + " FirstName VARCHAR(20) NOT NULL," + " LastName VARCHAR(20)," + " Point REAL DEFAULT 0.0," + " BirthDate DATE DEFAULT '1988-12-31'," + " ModTime TIMESTAMP DEFAULT '2006-12-31 23:59:59.999')"); System.out.println("Table created."); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

When you run this program, table Profile will be created. But you run it again, you will get an exception:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar DerbyIdentityColumn.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar DerbyIdentityColumn

Table created.

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar DerbyIdentityColumn

Exception: Table 'profile' already exists

"INSERT INTO" StatementsINSERT statements are used very often by database applications to insert data rows into tables. The syntax of INSERT statements is very simple. You need to provide a list of column names and a list of values for those columns. There are a couple of simple rules about INSERT statements:

You don't have to provide values to columns that have default values defined. You don't have to provide values to columns that allow null values. You should not provide values to AUTO_INCREMENT columns, mainly used as

primary key columns.

INSERT statements should be executed with the executeUpdate() method. Here is a simple program that insert some rows into my table Profile:

/** * MySqlMultipleInserts.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.util.*;import java.sql.*;public class MySqlMultipleInserts { public static void main(String [] args) { Connection con = null; try {

// Setting up the DataSource object com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret");

// Getting a connection object and statement object con = ds.getConnection(); Statement sta = con.createStatement();

int count = 0;

// insert a single row using default values count += sta.executeUpdate( "INSERT INTO Profile" + " (FirstName)" + " VALUES ('Herong')");

// insert a single row using provided values count += sta.executeUpdate( "INSERT INTO Profile" + " (FirstName, LastName, Point, BirthDate)" + " VALUES ('Janet', 'Gates', 999.99, '1984-10-13')");

// insert rows with loop with random values Random r = new Random(); for (int i=0; i<10; i++) { Float points = 1000*r.nextFloat(); String firstName = Integer.toHexString(r.nextInt(9999)); String lastName = Integer.toHexString(r.nextInt(999999)); count += sta.executeUpdate( "INSERT INTO Profile" + " (FirstName, LastName, Point)" + " VALUES ('"+firstName+"', '"+lastName+"', "+points+")"); }

// How many rows were inserted System.out.println("Number of rows inserted: "+count);

// Checking inserted rows ResultSet res = sta.executeQuery( "SELECT * FROM Profile"); System.out.println("List of Profiles: "); while (res.next()) { System.out.println( " "+res.getInt("ID") + ", "+res.getString("FirstName") + ", "+res.getString("LastName") + ", "+res.getDouble("Point") + ", "+res.getDate("BirthDate") + ", "+res.getTimestamp("ModTime")); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Notice that Random class was used to generate some random strings and numbers. The output confirms that the insert statement was executed correctly:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar

MySqlMultipleInserts

Number of rows inserted: 12List of Profiles: 1, Herong, null, 0.0, 1988-12-31, 2006-12-31 23:59:59.0 2, Janet, Gates, 999.99, 1984-10-13, 2006-12-31 23:59:59.0 3, 1093, 9c94a, 694.96106, 1988-12-31, 2006-12-31 23:59:59.0 4, 2556, 7c501, 654.16656, 1988-12-31, 2006-12-31 23:59:59.0 5, 2514, ee0b8, 134.08804, 1988-12-31, 2006-12-31 23:59:59.0 6, 11b1, 91714, 614.86383, 1988-12-31, 2006-12-31 23:59:59.0 7, 9ac, 608cc, 941.5479, 1988-12-31, 2006-12-31 23:59:59.0 8, 1b37, ec682, 290.13306, 1988-12-31, 2006-12-31 23:59:59.0 9, 16a8, dabd2, 251.7339, 1988-12-31, 2006-12-31 23:59:59.0 10, eec, 7d583, 674.99347, 1988-12-31, 2006-12-31 23:59:59.0 11, ab1, c465f, 566.3607, 1988-12-31, 2006-12-31 23:59:59.0 12, 1914, 4f9a4, 366.74844, 1988-12-31, 2006-12-31 23:59:59.0

MySQL – PreparedStatementThis chapter provides tutorial notes on JDBC PreparedStatement with MySQL JDBC Driver. Topics include creating PreparedStatement objects; setting PreparedStatement parameters; running PreparedStatement in batch mode; comparing PreparedStatement performance.

Tutorial programs in this chapter use DataSource objects retrieved from a JNDI directory service to create connections. Read previous chapters to learn how store DataSource objects into a JNDI directory service.

PreparedStatement Overview

PreparedStatement with Parameters

PreparedStatement in Batch Mode

Performance of Inserting Rows with a PreparedStatement

Performance of Inserting Rows with a Regular Statement

Performance of Inserting Rows with a ResultSet

PreparedStatement OverviewIf you have a SQL statement that needs to be executed multiple times, it is more efficient to use a JDBC PreparedStatement object to run it. JDBC PreparedStatement class supports the following main features:

SQL statements PreparedStatement objects are pre-compiled on the database server side.

IN parameters are supported in SQL statements in PreparedStatement objects. Batch execution mode is supported to run the run SQL statement multiple times in

a single transaction.

A PreparedStatement object should be created from a Connection object with the prepareStatement() method and executed like a regular Statement object as shown in the following program:

/** * MySqlPreparedSelect.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.util.*;import java.sql.*;import javax.sql.*;import javax.naming.*;public class MySqlPreparedSelect { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// PreparedStatement for SELECT statement PreparedStatement sta = con.prepareStatement(

"SELECT * FROM Profile WHERE ID = 2");

// Execute the PreparedStatement as a query ResultSet res = sta.executeQuery();

// Get values out of the ResultSet res.next(); String firstName = res.getString("FirstName"); String lastName = res.getString("LastName"); System.out.println("User ID 2: "+firstName+' '+lastName);

// Close ResultSet and PreparedStatement res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output looks correct:

User ID 2: Janet Gates

PreparedStatement with ParametersTo make a PreparedStatement object more flexible, you add parameters to the embedded SQL statement with question marks (?). Real values should be added before executing the PreparedStatement object.

Adding values to PreparedStatement parameters should be done by calling setXXX() methods in this format:

ps.setXXX(1, value);ps.setXXX(2, value);...ps.setXXX(n, value); // Sets value to the n-th parameter.

JDBC supports many setXXX() methods, one for each Java data type, so that you can set parameter values directly with the desired Java data types without any conversion. Here is a list of setXXX() methods:

setArray() - Sets the value of the specified parameter as Java Array type. setAsciiStream() - Sets the value of the specified parameter as a stream of ASCII

characters. setBigDecimal() - Sets the value of the specified parameter as

java.sql.BigDecimal type. setBinaryStream() - Sets the value of the specified parameter as a stream of bytes. setByte() - Sets the value of the specified parameter as Java byte type. setBlob() - Sets the value of the specified parameter as Java Blob type. setBoolean() - Sets the value of the specified parameter as Java boolean type. setBytes() - Sets the value of the specified parameter as Java byte[] type. setCharacterStream() - Sets the value of the specified parameter as java.io.Reader

type. setClob() - Sets the value of the specified parameter as Java Clob type. setDate() - Sets the value of the specified parameter as java.sql.Date type. setDouble() - Sets the value of the specified parameter as Java double type. setFloat() - Sets the value of the specified parameter as Java float type. setInt() - Sets the value of the specified parameter as Java int type. setLong() - Sets the value of the specified parameter as Java long type. setNull() - Sets the value of the specified parameter as a null value. setObject() - Sets the value of the specified parameter as Java Object type. setRef() - Sets the value of the specified parameter as Java Ref type. setShort() - Sets the value of the specified parameter as Java short type. setString() - Sets the value of the specified parameter as Java String type. setTime() - Sets the value of the specified parameter as java.sql.Time type. setTimestamp() - Sets the value of the specified parameter as java.sql.Timestamp

type.

Here is a sample program that created a PreparedStatement object with one parameter:

/** * MySqlPreparedStatementParameter.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.util.*;import java.sql.*;import javax.sql.*;import javax.naming.*;public class MySqlPreparedStatementParameter { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// PreparedStatement for SELECT statement with one parameter PreparedStatement sta = con.prepareStatement(

"SELECT * FROM Profile WHERE ID = ?");

// Provide a value to the parameter int id = 9; sta.setInt(1,id);

// Execute the PreparedStatement as a query ResultSet res = sta.executeQuery();

// Get values out of the ResultSet res.next(); String firstName = res.getString("FirstName"); String lastName = res.getString("LastName"); System.out.println("User ID "+id+": "+firstName+' '+lastName);

// Close ResultSet and PreparedStatement res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Output of the program confirms that the PreparedStatement object worked correctly:

User ID 9: 16a8 dabd2

PreparedStatement in Batch ModeIf you want to execute a PreparedStatement object multiple times in a single transaction, you can use the batch feature of the PreparedStatement object. Each time the addBatch() method is called, a copy of the embedded SQL statement will be created, but not executed until the execution method call. Here is sample sequence of method calls to execute a PreparedStatement object in batch mode:

ps.setXXX(...); // Set parameters for the first copy...ps.addBatch(); // Create the first copy of the SQL statementps.setXXX(...); // Set parameters for the second copy ...ps.addBatch(); // Create the second copy of the SQL statementps.setXXX(...); // Set parameters for the third copy...ps.addBatch(); // Create the third copy of the SQL statement

ps.executeBatch(); // Execute all copies together as a batch

Here is a sample program that creates a PrepareStatement object and executes it in batch mode to run an INSERT statement 4 times:

/** * MySqlPreparedStatementBatch.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.util.*;import java.sql.*;import javax.sql.*;import javax.naming.*;public class MySqlPreparedStatementBatch { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// PreparedStatement PreparedStatement ps = con.prepareStatement(

"INSERT INTO Profile (FirstName, LastName) VALUES (?, ?)");

// Provide values to parameters for copy 1 ps.setString(1,"John"); ps.setString(2,"First");

// Create copy 1 ps.addBatch();

// Provide values to parameters for copy 2 ps.setString(1,"Bill"); ps.setString(2,"Second");

// Create copy 2 ps.addBatch();

// Provide values to parameters for copy 3 ps.setString(1,"Mark"); ps.setString(2,"Third");

// Create copy 3 ps.addBatch();

// Provide values to parameters for copy 4 ps.setString(1,"Jack"); ps.setString(2,"Last");

// Create copy 4 ps.addBatch();

// Execute all 4 copies int[] counts = ps.executeBatch(); int count = 0; for (int i=0; i<counts.length; i++) { count += counts[i]; } System.out.println("Total effected rows: "+count);

// Close the PreparedStatement object ps.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Here is the output:

Total effected rows: 4

Performance of Inserting Rows with a PreparedStatementRunning SQL statements using PreparedStatement objects is supposed to be faster than using regular Statement objects. To test this, I wrote the following Java program to measure the performance of inserting rows using a PreparedStatement object into an empty table:

/** * MySqlPerformancePreparedStatement.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved.

*/import java.util.*;import java.sql.*;import javax.sql.*;public class MySqlPerformancePreparedStatement { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Delete all rows from the table Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Profile");

// Start the test int count = 10000; long t1 = System.currentTimeMillis();

// PreparedStatement to insert rows PreparedStatement ps = con.prepareStatement(

"INSERT INTO Profile (FirstName, LastName) VALUES (?, ?)"); Random r = new Random(); for (int i = 0; i < count; i++) { ps.setString(1,Integer.toHexString(r.nextInt(9999))); ps.setString(2,Integer.toHexString(r.nextInt(999999))); ps.executeUpdate(); } ps.close();

// End the test long t2 = System.currentTimeMillis(); System.out.println("PreparedStatement insert "+count +" rows with "+(t2 -t1) +" milliseconds");

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Here is the result on a Windows XP system with a 997MHz processor. You should compare this with the result the next tutorial.

PreparedStatement insert 10000 rows with 1281 milliseconds

Performance of Inserting Rows with a Regular Statement

To compare the performance result from the previous result with regular Statement objects. I wrote the following Java program to measure the performance of inserting rows using a regular Statement object into an empty table:

/** * MySqlPerformanceRegularStatement.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.util.*;import java.sql.*;import javax.sql.*;public class MySqlPerformanceRegularStatement { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Delete all rows from the table Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Profile");

// Start the test int count = 10000; long t1 = System.currentTimeMillis();

// Regular Statement to insert rows Statement rs = con.createStatement(); Random r = new Random(); for (int i = 0; i < count; i++) { rs.executeUpdate("INSERT INTO Profile (FirstName, LastName)" +" VALUES ('"+Integer.toHexString(r.nextInt(9999)) +"', '"+Integer.toHexString(r.nextInt(999999))+"')"); } rs.close();

// End the test long t2 = System.currentTimeMillis(); System.out.println("Regular Statement insert "+count +" rows with "+(t2 -t1) +" milliseconds");

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Here is the result on a Windows XP system with a 997MHz processor. You should compare this with the result from other performance tutorials.

Regular Statement insert 10000 rows with 1297 milliseconds

Comparing with PreparedStatement, regular Statement performs identically as PreparedStatement, see the table below:

Statement # of inserts Time in ms. ComparisonPreparedStatement 10000 1281 100%Regular Statement 10000 1297 101%

Performance of Inserting Rows with a ResultSetSince ResultSet objects can also be used to insert rows, I wrote the following Java sample program to measure the performance of inserting multiple rows using a ResultSet object:

/** * MySqlPerformanceResultSet.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.util.*;import java.sql.*;import javax.sql.*;public class MySqlPerformanceResultSet { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Delete all rows from the table Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Profile");

// Start the test int count = 10000; long t1 = System.currentTimeMillis();

// ResultSet to insert rows Statement rs = con.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet res = rs.executeQuery("SELECT * FROM Profile"); res.moveToInsertRow(); Random r = new Random(); for (int i = 0; i < count; i++) { res.updateString("FirstName",

Integer.toHexString(r.nextInt(9999))); res.updateString("LastName", Integer.toHexString(r.nextInt(999999))); res.insertRow(); } rs.close();

// End the test long t2 = System.currentTimeMillis(); System.out.println("ResultSet insert "+count +" rows with "+(t2 -t1) +" milliseconds");

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Here is the result on a Windows XP system with a 997MHz processor. You should compare this with the result from other performance tutorials.

Regular Statement insert 10000 rows with 3062 milliseconds

No surprise here, ResultSet is about 2 times slower than PreparedStatement, see the table below:

Statement # of inserts Time in ms. ComparisonPreparedStatement 10000 1281 100%Regular Statement 10000 1297 101%ResultSet 10000 3062 239%

MySQL - Reference Implementation of JdbcRowSetThis chapter provides tutorial notes on Sun's Reference Implementation of JdbcRowSet working with MySQL database. Topics include installing JdbcRowSet reference implementation; connecting JdbcRowSet objects to database server with connection URL, DataSource name, ResultSet objects or Connection objects; query parameters; inserting rows.

Overview of RowSet Objects

Installation of JdbcRowSet Reference Implementation

Connecting JdbcRowSet to Database Servers

Connecting JdbcRowSet with a Connection URL

Connecting JdbcRowSet with a Predefined Connection Object

Connecting JdbcRowSet with a Predefined ResultSet Object

Connecting JdbcRowSet with JNDI Directory Service

JdbcRowSet Query Statement with Parameters

Inserting Rows with JdbcRowSet Objects

Overview of RowSet ObjectsRowSet is a new concept introduced in the JDBC specification. A RowSet object is designed to represent a container of tabular data from any data source. A RowSet object may obtain data from a database server through a JDBC driver, it may also obtain data from a spreadsheet, a flat file, or an XML document.

A RowSet object extends the ResultSet interface, so that it has ResultSet methods to retrieve column values or update rows back to the data source.

A RowSet object is also a JavaBeans component. It is designed to combine functionalities of Connect, Statement and ResultSet objects into a single object.

There 5 standard implementations of RowSet interface:

JdbcRowSet - A connected RowSet that is always connected to a database through a JDBC driver.

CachedRowSet - A disconnected RowSet that is not connected to the data source except when reading data from it or writing data to it.

WebRowSet - A disconnected RowSet extended from CachedRowSet. It can write itself as an XML document and read it back.

FilteredRowSet - A disconnected RowSet extended from WebRowSet. It can have filtering criteria to select data without using query statements.

JoinRowSet - A disconnected RowSet extended from WebRowSet. It can perform query join functions.

Sun has developed reference implementations of all 5 types or RowSet objects. But they are not included in standard JDK packages. They are packaged under com.sun.rowset. You need to down them separately. See the next tutorial for details.

Installation of JdbcRowSet Reference ImplementationIn order to play with RowSet interface and its implementations, I following these steps to download and install Sun's reference implementation package:

Go to JDBC Rowset Implementations page. Click the Download button. You will see the JSR-000114 JDBC Rowset

Implementations Specification Interface Classes 1.0 Public Review Draft 2 page. Check the radio button on "Accept License Agreement". Click the download icon next to "jdbc_rowset-1_0-prd2-ri.zip 259.75 KB". Save the jdbc_rowset-1_0-prd2-ri.zip file to a temporary directory.

Unzip jdbc_rowset-1_0-prd2-ri.zip. You will see a new directory jdbc_rowset1.0prd2-ri.

Move that directory, jdbc_rowset1.0prd2-ri, to \local\jdbc_rowset. Copy \local\jdbc_rowset\rowset.jar to \local\lib\rowset.jar.

The installation is done. But don't forget to read the API documentation at \local\jdbc_rowset\doc\index.html.

Connecting JdbcRowSet to Database ServersThere are a number of ways you can connect a JdbcRowSet object to a database server through a JDBC driver:

1. Auto-connecting with DriverManager. A JdbcRowSet object is created without a connection object. But you provide a connection URL before executing any SQL statements as shown below:

JdbcRowSet jrs = new JdbcRowSetImpl(); jrs.setUrl("connection_url"); jrs.setCommand("SQL_statement"); jrs.execute(); jrs.next(); String val = jrs.getString(1)

2. Predefined Connection object. A JdbcRowSet object is created with a predefined Connection object. See the sample statements below:

Connection con = DriverManager.getConnection("connection_url"); JdbcRowSet jrs = new JdbcRowSetImpl(con); jrs.setCommand("SQL_statement"); jrs.execute(); jrs.next(); String val = jrs.getString(1)

3. Predefined ResultSet object. A JdbcRowSet object is created with a predefined ResultSet object. See the sample statements below:

Connection con = DriverManager.getConnection("connection_url"); Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SQL_statement"); JdbcRowSet jrs = new JdbcRowSetImpl(res); jrs.next(); String val = jrs.getString(1)

4. DataSource name lookup. A JdbcRowSet object is created without any connection object. But a DataSource name is provided. The JdbcRowSet object will do a JNDI lookup to obtain a DataSource object and create a Connection object when excute() method is called. See the sample statements below:

JdbcRowSet jrs = new JdbcRowSetImpl(); jrs.setDataSourceName("DataSource_name");

jrs.setCommand("SQL_statement"); jrs.execute(); jrs.next(); String val = jrs.getString(1)

Connecting JdbcRowSet with a Connection URLIf a JdbcRowSet object is created without a connection to the database server, you can provide a connection URL string by calling the setUrl() method. A connection object will be created later by the JdbcRowSet object with DriverManager when you call the execute() method. Here is a sample sequence of method calls:

// Creating a JdbcRowSet object wihout connection JdbcRowSet jrs = new JdbcRowSetImpl();

// Provide a connection URL for future use jrs.setUrl("connection_url");

// Provide a SQL statement for future use jrs.setCommand("SQL_statement");

// Create a connnection using DriverManager// Execute the SQL statement jrs.execute();

Note that the name of method setUrl() is very confusing with another method setURL(), which sets a URL value to a parameter.

Note also that the connection URL and user information can also be provided through another constructor:

JdbcRowSet jrs = new JdbcRowSetImpl("connection_url", "user", "password");

To test connection method, I wrote the following tutorial program:

/** * MySqlRowSetConnectionUrl.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlRowSetConnectionUrl { public static void main(String [] args) { try {

// Load the JDBC driver class. Needed for JDBC 3.0 drivers Class.forName("com.mysql.jdbc.Driver") ;

// Get a new JdbcRowSet object with Run's implementation javax.sql.rowset.JdbcRowSet jrs = new com.sun.rowset.JdbcRowSetImpl();

// Set the connection URL for the DriverManager jrs.setUrl("jdbc:mysql://localhost/HerongDB"

+"?user=Herong&password=TopSecret");

// Set a SQL statement jrs.setCommand("SELECT 'Hello world!'");

// Connect and run the statement jrs.execute();

// Get the result jrs.next(); System.out.println("Result: "+jrs.getString(1)); // Close resource jrs.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Make sure the MySQL server is running on the local machine, then run this program. The result confirms that the connection is working.

C:\>javac -cp .;\local\lib\rowset.jar MySqlRowSetConnectionUrl.java

MySqlRowSetConnectionUrl.java:16: warning: com.sun.rowset.JdbcRowSetImpl is Sun proprietary API and may be removed in a future release = new com.sun.rowset.JdbcRowSetImpl(); ^1 warning

C:\>java -cp .;\local\lib\rowset.jar; \local\lib\mysql-connector-java-5.0.7-bin.jar MySqlRowSetConnectionUrl

Result: Hello world!

Notice that a warning message was given at the compilation time.

Connecting JdbcRowSet with a Predefined Connection ObjectAnother way to connect a JdbcRowSet object to a database server is to pass a predefined connection object to the JdbcRowSetImpl constructor. Here is a sample sequence of method calls:

// Create a Connection object Connection con = DriverManager.getConnection("connection_url");

// Creating a JdbcRowSet object with a connection object JdbcRowSet jrs = new JdbcRowSetImpl(con);

// Provide a SQL statement for future use jrs.setCommand("SQL_statement");

// Create a connnection using DriverManager// Execute the SQL statement jrs.execute();

Here is a sample program that connects a JdbcRowSet object to MySQL server with a predefined connection object:

/** * MySqlRowSetConnectionObject.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlRowSetConnectionObject { public static void main(String [] args) { Connection con = null; try {

// Create a Connection object Class.forName("com.mysql.jdbc.Driver") ; con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/HerongDB" +"?user=Herong&password=TopSecret");

// Pass the Connection object to the new JdbcRowSet object javax.sql.rowset.JdbcRowSet jrs = new com.sun.rowset.JdbcRowSetImpl(con);

// Set a SQL statement jrs.setCommand("SELECT 'Hello world!'");

// Connect and run the statement jrs.execute();

// Get the result jrs.next(); System.out.println("Result: "+jrs.getString(1));

// Close resources jrs.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Output of compilation and execution was recorded here:

C:\>javac -cp .;\local\lib\rowset.jar MySqlRowSetConnectionObject.java

MySqlRowSetConnectionObject.java:19: warning: com.sun.rowset.JdbcRowSetImpl is Sun proprietary API and may be removed in a future release = new com.sun.rowset.JdbcRowSetImpl(con);

^1 warning

C:\>java -cp .;\local\lib\rowset.jar; \local\lib\mysql-connector-java-5.0.7-bin.jar MySqlRowSetConnectionObject

Result: Hello world!

Connecting JdbcRowSet with a Predefined ResultSet ObjectAnother way to connect a JdbcRowSet object to a database server is to pass a predefined ResultSet object to the JdbcRowSetImpl constructor. Here is a sample sequence of method calls:

// Create a Connection object Connection con = DriverManager.getConnection("connection_url");

// Execute a SQL statement to generate a Result object Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SQL_statement");

// Creating a JdbcRowSet object with a connection object JdbcRowSet jrs = new JdbcRowSetImpl(res);

// Ready to retrieve column values jrs.next(); String val = jrs.getString(1)

Here is a sample program that connects a JdbcRowSet object to MySQL server with a predefined ResultSet object:

/** * MySqlRowSetResultSet.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlRowSetResultSet { public static void main(String [] args) { Connection con = null; try {

// Create a Connection object Class.forName("com.mysql.jdbc.Driver") ; con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/HerongDB" +"?user=Herong&password=TopSecret");

// Execute a SQL statement to generate a Result object Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT 'Hello world!'");

// Pass the Connection object to the new JdbcRowSet object javax.sql.rowset.JdbcRowSet jrs = new com.sun.rowset.JdbcRowSetImpl(res);

// Get the result jrs.next(); System.out.println("Result: "+jrs.getString(1)); // Close resources jrs.close(); res.close(); sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Output of compilation and execution was recorded here:

C:\>javac -cp .;\local\lib\rowset.jar MySqlRowSetResultSet.java

MySqlRowSetResultSet.java:23: warning: com.sun.rowset.JdbcRowSetImpl is Sun proprietary API and may be removed in a future release = new com.sun.rowset.JdbcRowSetImpl(res); ^1 warning

C:\>java -cp .;\local\lib\rowset.jar;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlRowSetResultSet

Result: Hello world!

Connecting JdbcRowSet with JNDI Directory Service

If a JdbcRowSet object is created without a Connection object, you can provide a DataSource name to the JdbcRowSet object so that it can use a JNDI directory service to lookup a DataSource object by name. The DataSource object will be then used to create a connection object. See the sample statements below:

// Create a JdbcRowSet object without any connection JdbcRowSet jrs = new JdbcRowSetImpl();

// Set the DataSource name for JNDI lookup jrs.setDataSourceName("jdbc/mysqlDS");

// Set a SQL statement jrs.setCommand("SELECT 'Hello world!'");

// Lookup, connect and run the statement jrs.execute();

I wrote the following sample to connect a JdbcRowSet object using a JNDI directory service:

/** * MySqlRowSetDataSource.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlRowSetDataSource { public static void main(String [] args) { Connection con = null; try {

// Get a new JdbcRowSet object with Run's implementation javax.sql.rowset.JdbcRowSet jrs = new com.sun.rowset.JdbcRowSetImpl();

// Set the DataSource name for JNDI lookup jrs.setDataSourceName("jdbc/mysqlDS");

// Set a SQL statement jrs.setCommand("SELECT 'Hello world!'");

// Connect and run the statement jrs.execute();

// Get the result jrs.next(); System.out.println("Result: "+jrs.getString(1)); // Close resources jrs.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); }

}}

But I failed to set up the JNDI directory service. So I got an exception when running this program:

C:\>javac -cp .;\local\lib\rowset.jar MySqlRowSetDataSource.java

MySqlRowSetDataSource.java:13: warning: com.sun.rowset.JdbcRowSetImpl is Sun proprietary API and may be removed in a future release = new com.sun.rowset.JdbcRowSetImpl(); ^1 warning

C:\>java -cp .;\local\lib\rowset.jar; \local\lib\mysql-connector-java-5.0.7-bin.jar MySqlRowSetDataSource

Exception: JdbcRowSet (connect) JNDI unable to connectjava.sql.SQLException: JdbcRowSet (connect) JNDI unable to connect at com.sun.rowset.JdbcRowSetImpl.connect(JdbcRowSetImpl.java:634) at com.sun.rowset.JdbcRowSetImpl.prepare(JdbcRowSetImpl.java:654) at com.sun.rowset.JdbcRowSetImpl.execute(JdbcRowSetImpl.java:551) at MySqlRowSetDataSource.main(MySqlRowSetDataSource.java:22)

JdbcRowSet Query Statement with ParametersWhen you call the setCommand() method to provide the SELECT query statement, you can define some parameters in the same way as a PreparedStatement. Here is a sample program showing you set parameters and provide parameter values:

/** * MySqlRowSetParameter.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlRowSetParameter { public static void main(String [] args) { try {

// Load the JDBC driver class. Needed for JDBC 3.0 drivers Class.forName("com.mysql.jdbc.Driver") ;

// Get a new JdbcRowSet object with Run's implementation javax.sql.rowset.JdbcRowSet jrs = new com.sun.rowset.JdbcRowSetImpl();

// Set the connection URL for the DriverManager jrs.setUrl("jdbc:mysql://localhost/HerongDB" +"?user=Herong&password=TopSecret");

// Set a SQL statement with a parameter jrs.setCommand("SELECT * FROM Profile WHERE ID = ?");

// Set parameter values jrs.setInt(1, 90028);

// Connect and run the statement jrs.execute(); jrs.next(); System.out.println("User #90028: " +jrs.getString("FirstName")+" "+jrs.getString("LastName")); // Repeating for another parameter value jrs.setInt(1, 90027); jrs.execute(); jrs.next(); System.out.println("User #90027: " +jrs.getString("FirstName")+" "+jrs.getString("LastName"));

// Close resource jrs.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

The parameter in this program represents the ID value in the Profile table. You may need to change the parameter values if your Profile table has different ID values. Here is the output of the program:

C:\>javac -cp .;\local\lib\rowset.jar MySqlRowSetParameter.java

MySqlRowSetParameter.java:15: warning: com.sun.rowset.JdbcRowSetImpl is Sun proprietary API and may be removed in a future release = new com.sun.rowset.JdbcRowSetImpl(); ^1 warning

C:\>java -cp .;\local\lib\rowset.jar;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlRowSetParameterUser #90028: 545 728d4User #90027: 169b 1f61d

Inserting Rows with JdbcRowSet ObjectsBased on the JDBC documentation, a JdbcRowSet object is extended from a ResultSet object and defined to be updatable by default. This means that you can use JdbcRowSet objects to update, delete or insert rows back to target tables in the database server. I wrote a sample program to use a JdbcRowSet object to insert 2 data rows back to the Profile table in MySQL server:

/** * MySqlRowSetInsert.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;

public class MySqlRowSetInsert { public static void main(String [] args) { try {

// Load the JDBC driver class. Needed for JDBC 3.0 drivers Class.forName("com.mysql.jdbc.Driver") ;

// Get a new JdbcRowSet object with Run's implementation javax.sql.rowset.JdbcRowSet jrs = new com.sun.rowset.JdbcRowSetImpl();

// Set the connection URL for the DriverManager jrs.setUrl("jdbc:mysql://localhost/HerongDB" +"?user=Herong&password=TopSecret");

// Set a SQL statement with parameters jrs.setCommand("SELECT * FROM Profile WHERE 1=2");

// Connect and run the statement jrs.execute();

// Move to the insert row jrs.moveToInsertRow();

// Set column values and insert jrs.updateString("FirstName", "Herong"); jrs.updateString("LastName", "Yang"); jrs.insertRow(); // Repeat for another row jrs.updateString("FirstName", "Bush"); jrs.updateString("LastName", "Gate"); jrs.insertRow();

System.out.println("2 rows inserted."); // Close resource jrs.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

The program executed correctly:

C:\>javac -cp .;\local\lib\rowset.jar MySqlRowSetInsert.java

MySqlRowSetInsert.java:15: warning: com.sun.rowset.JdbcRowSetImpl is Sun proprietary API and may be removed in a future release = new com.sun.rowset.JdbcRowSetImpl(); ^1 warning

C:\>java -cp .;\local\lib\rowset.jar;

\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlRowSetInsert

2 rows inserted.

MySQL - JBDC CallableStatement

This chapter provides tutorial notes on JDBC CallableStatement objects for stored procedures. Topics include CallableStatement overview; creating stored procedures with parameters and multiple result sets; using place holders in CallableStatement objects; register output parameters; retrieving multiple result sets.

Overview of CallableStatement Objects

"CREATE PROCEDURE" - Creating a Simple Procedure

Creating Procedures with IN and OUT Parameters

Creating Procedures with INOUT Parameters

Creating Procedures with Multiple Queries

Creating CallableStatement Objects with prepareCall()

Capturing ResultSet with executeQuery()

Creating CallableStatement Objects with Parameters

Common Errors with CallableStatement Parameters

Creating CallableStatement Objects with INOUT Parameters

Retrieving Multiple ResultSet Objects

Executing Stored Procedures without Permission

getProcedures() - Listing Stored Procedures

Overview of CallableStatement ObjectsJDBC CallableStatement objects are designed to represent calls to stored procedures in the database server. Several things you need to remember about CallableStatement objects:

1. A CallableStatement object is created by calling the prepareCall() method on a Connection object like:

// Calling a procedure with no procedure parameters CallableStatement cs1 = con.prepareCall("CALL Abc()");

// Calling a procedure with some literal parameters CallableStatement cs2 = con.prepareCall("CALL Abc(v1, v2, ...)");

2. Place holders (question marks "?") can be used in call statements in the same way as in prepared statements. OUT (output) parameters, INOUT (input and output) parameters, and return parameter must be represented by place holders:

// Calling a procedure with place-holder parameters CallableStatement cs3 = con.prepareCall( "CALL Abc(v1, v2, ?, ?, ...)");

// Calling a procedure with a return value CallableStatement cs4 = con.prepareCall( "? = CALL Abc(v1, v2, ?, ?, ...)");

The last format "? = CALL Abc(v1, v2, ?, ?, ...)" does not apply to MySQL, since MySQL procedures are not allowed to return values.

3. If an IN parameter is represented by a place holder, its value should be provided by calling the setXXX() method before the execution of the call statement:

// providing a string value to the first place holder cs3.setString(1, "North Pole"); // providing a double value to the second place holder cs3.setDouble(2, 3.14);

4. Place holders for OUT, INOUT and return parameters must registered as output parameters with JDBC data types before the execution of the call statement:

// Registering the first place holder as a INTEGER output cs4.registerOutParameter(1, java.sql.Types.INTEGER);

// Registering the fourth place holder as a DATE output cs4.registerOutParameter(4, java.sql.Types.DECIMAL, 3);

5. If no ResultSet objects is returned from the stored procedure, the call statement should be executed by calling the cs.executeUpdate() method.

// Executing the call statement cs1.executeUpdate();

6. If one or more ResultSet objects are returned from the store procedure, the call statement should be executed by calling the cs.executeQuery() method. It will return the first ResultSet object, representing the output of the first query statement executed in the stored procedure:

// Executing the call statement ResultSet res = cs1.executeQuery();

7. A CallableStatement can also be executed in batch mode similar to a PreparedStatement:

// Close one copy of the call statement cs3.setXXX(...); cs3.addBatch();

// Close onather copy of the call statement cs3.setXXX(...); cs3.addBatch();

// Execute all copies of the call statement in batch mode cs3.executeBatch();

8. After the execution of the call statement, all rows of all returning ResultSet objects should be processed before retrieving any registered output parameters.

// Executing the call statement ResultSet res = cs1.executeQuery(); while (res.next()) { ... }

9. After finishing all ResultSet objects, registered output parameters can be retrieved by using the getXXX() methods:

// Retrieving registered output parameters int first = cs4.getInt(1); Date four = cs4.getDate(4);

"CREATE PROCEDURE" - Creating a Simple ProcedureIn order to test CallableStatement objects, I created a very simple stored procedure that runs a query to return "Hello world!":

C:\>\local\mysql\bin\mysql -u Herong -pTopSecret

mysql> USE HerongDB;Database changed

mysql> DELIMITER '/';

mysql> CREATE PROCEDURE HelloWorld() -> BEGIN -> SELECT 'Hello world!' AS Message; -> END/Query OK, 0 rows affected (0.00 sec)

mysql> CALL HelloWorld()/+--------------+| Message |+--------------+| Hello world! |+--------------+

1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Note that "DELIMITER '/';" was used to change the command delimiter from ";" to "/", so that I can use ";" as part of the stored procedure definition statement.

"CALL HelloWorld()/" is a command to test the stored procedure.

If made a mistake in the stored procedure, you can delete it from the server by the "DROP PROCEDURE HelloWorld" command.

Creating Procedures with IN and OUT ParametersTo do more tests on CallableStatement objects, I created another stored procedure with IN and OUT parameters:

C:\>\local\mysql\bin\mysql -u Herong -pTopSecret

mysql> USE HerongDB;Database changed

mysql> -- Changing the command delimiter to '/'mysql> DELIMITER '/';

mysql> -- Creating the proceduremysql> CREATE PROCEDURE ReverseProcedure(IN String VARCHAR(80), -> OUT Reversed VARCHAR(80), OUT Length INTEGER) -> BEGIN -> SET Reversed = REVERSE(String); -> SET Length = LENGTH(String); -> SELECT String, Reversed, Length; -> END/Query OK, 0 rows affected (0.00 sec)

mysql> -- Testing the proceduremysql> CALL ReverseProcedure('Herong', @Reversed, @Length)/+--------+----------+--------+| String | Reversed | Length |+--------+----------+--------+| Herong | gnoreH | 6 |+--------+----------+--------+1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> -- Checking the OUT parametersmysql> SELECT @Reversed, @Length/+-----------+---------+| @Reversed | @Length |+-----------+---------+| gnoreH | 6 |+-----------+---------+1 row in set (0.02 sec)

Creating Procedures with INOUT Parameterso do more tests on CallableStatement objects, I created another stored procedure with INOUT parameters:

C:\>\local\mysql\bin\mysql -u Herong -pTopSecret

mysql> USE HerongDB;Database changed

mysql> -- Changing the command delimiter to '$'mysql> DELIMITER '$';

mysql> -- Creating the proceduremysql> CREATE PROCEDURE C2F(INOUT C REAL, INOUT F REAL) -> BEGIN -> IF C IS NOT NULL THEN -> SET F = 1.8*C + 32.0; -> ELSEIF F IS NOT NULL THEN -> SET C = (F-32.0)/1.8; -> ELSE -> SET C = 0.0; -> SET F = 32.0; -> END IF; -> SELECT C, F; -> END$Query OK, 0 rows affected (0.00 sec)

mysql> -- Testing the proceduremysql> SET @C = 20$Query OK, 0 rows affected (0.00 sec)

mysql> CALL C2F(@C,@F)$+------+------+| C | F |+------+------+| 20 | 68 |+------+------+1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> -- Checking output parametersmysql> SELECT @C, @F$+------+------+| @C | @F |+------+------+| 20 | 68 |+------+------+1 row in set (0.00 sec)

Creating Procedures with Multiple QueriesTo do more tests on CallableStatement objects, I created another stored procedure with multiple queries:

C:\>\local\mysql\bin\mysql -u Herong -pTopSecret

mysql> USE HerongDB;Database changed

mysql> -- Changing the command delimiter to '$'mysql> DELIMITER '$';

mysql> -- Creating the proceduremysql> CREATE PROCEDURE HeadTail(OUT Size INTEGER) -> BEGIN -> SELECT ID, FirstName, LastName, BirthDate, ModTime -> FROM Profile ORDER BY ID LIMIT 0, 10; -> SELECT ID, FirstName, LastName, BirthDate, ModTime -> FROM Profile ORDER BY ID DESC LIMIT 0, 10; -> SELECT COUNT(*) INTO Size FROM Profile; -> END$Query OK, 0 rows affected (0.00 sec)

mysql> -- Testing the proceduremysql> CALL HeadTail(@Size)$+-------+-----------+----------+------------+---------------------+| ID | FirstName | LastName | BirthDate | ModTime |+-------+-----------+----------+------------+---------------------+| 80029 | 2265 | 9e559 | 1988-12-31 | 2006-12-31 23:59:59 || 80030 | 378 | 48189 | NULL | 2007-04-01 21:35:14 || 80031 | 1a46 | 108f3 | NULL | 2007-04-01 21:35:14 || 80032 | 44f | c8511 | NULL | 2007-04-01 21:35:14 || 80033 | 1e9a | 51ca6 | NULL | 2007-04-01 21:35:14 || 80034 | 665 | 6eb12 | NULL | 2007-04-01 21:35:14 || 80035 | 1b97 | 30e6d | NULL | 2007-04-01 21:35:14 || 80036 | 3d8 | 270d6 | NULL | 2007-04-01 21:35:14 || 80037 | 26a3 | b2290 | NULL | 2007-04-01 21:35:14 || 80038 | 1d65 | d9d6d | NULL | 2007-04-01 21:35:14 |+-------+-----------+----------+------------+---------------------+10 rows in set (0.00 sec)

+-------+-----------+----------+------------+---------------------+| ID | FirstName | LastName | BirthDate | ModTime |+-------+-----------+----------+------------+---------------------+| 90029 | Herong | Yang | 1988-12-31 | 2006-12-31 23:59:59 || 90028 | 545 | 728d4 | NULL | 2007-04-01 21:35:17 || 90027 | 169b | 1f61d | NULL | 2007-04-01 21:35:17 || 90026 | 2559 | 737b4 | NULL | 2007-04-01 21:35:17 || 90025 | 1db2 | 48b31 | NULL | 2007-04-01 21:35:17 || 90024 | 149 | cdf71 | NULL | 2007-04-01 21:35:17 || 90023 | 12a5 | 8a909 | NULL | 2007-04-01 21:35:17 || 90022 | 1b67 | c6f96 | NULL | 2007-04-01 21:35:17 || 90021 | 12a9 | 20ac8 | NULL | 2007-04-01 21:35:17 || 90020 | 18a4 | 1ffb4 | NULL | 2007-08-31 21:35:17 |+-------+-----------+----------+------------+---------------------+10 rows in set (0.03 sec)

Query OK, 0 rows affected (0.06 sec)

mysql> -- Check the output parametermysql> SELECT @Size$+-------+

| @Size |+-------+| 10001 |+-------+1 row in set (0.00 sec)

Creating CallableStatement Objects with prepareCall()The first test I did was to create a CallableStatement with the prepareCall() method to call my simple procedure, HelloWorld():

/** * MySqlCallableStatement.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallableStatement { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Create CallableStatement CallableStatement cs = con.prepareCall("CALL HelloWorld()");

// Execute the call statement cs.executeUpdate();

// Close resource cs.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Compilation and execution went ok. There was no output from the program as expected:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallableStatement.java

C:\java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallableStatement

Capturing ResultSet with executeQuery()There two ways to execute the CALL statement enclosed in the CallableStatement object:

rs.executeUpdate() - Execute the CALL statement without collecting any returning ResultSet. Use this method when the stored procedure is not returning any result sets or you want to ignore them.

res = rs.executQuery() - Execute the CALL statement and collect the returning ResultSet objects. Use this method when the stored procedure is returning result sets and you are want to collect them.

Here is a sample program that show you how to collect the returning ResultSet of the HelloWorld() procedure:

/** * MySqlCallExecuteQuery.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallExecuteQuery { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Create CallableStatement CallableStatement cs = con.prepareCall("CALL HelloWorld()");

// Execute the call statement and collect the ResultSet ResultSet res = cs.executeQuery();

// Retrieving the result res.next(); System.out.println("Result: "+res.getString("Message"));

// Close resource cs.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

This time the result of the query "SELECT 'Hello world!' AS Message;" in HelloWorld() procedure was successfully collected:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallExecuteQuery.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallExecuteQuery

Result: Hello world!

Creating CallableStatement Objects with Parametersor an IN parameter defined in a stored procedure, you can put a static value or a place holder in the CALL statement when creating the CallableStatement object. A value for that place holder must be provided by the setXXX() method.

For an OUT parameter defined in a stored procedure, you must put a place holder in the CALL statement when creating the CallableStatement object. That place holder must be registered with the registerOutParameter() method.

In a previous tutorial, I defined a store procedure called, ReverseProcedure(), with 1 IN parameter and 2 OUT parameters. The program below shows you to create a CallableStatement object to execute this stored procedure:

/** * MySqlCallParameter.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallParameter { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Create CallableStatement CallableStatement cs = con.prepareCall( "CALL ReverseProcedure(?,?,?)");

// Provide values for IN parameters String word = "Herong"; cs.setString(1,word);

// Register OUT parameters cs.registerOutParameter(2, java.sql.Types.VARCHAR); cs.registerOutParameter(3, java.sql.Types.INTEGER);

// Execute the CALL statement and ignore result sets cs.executeUpdate();

// Retrieve values from OUT parameters String reversed = cs.getString(2); int length = cs.getInt(3); System.out.println("Input word: "+word); System.out.println("Output word: "+reversed); System.out.println("Word length: "+length);

// Close resource cs.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output of the program confirms that I handled IN and OUT parameters correctly:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallParameter.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallParameter

Input word: HerongOutput word: gnoreHWord length: 6

Common Errors with CallableStatement ParametersWhile testing CallableStatement objects with place holder parameters, I noticed two common errors as shown in this program:

/** * MySqlCallParameterError.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallParameterError { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Create CallableStatement CallableStatement cs = con.prepareCall( "CALL ReverseProcedure(?,?,?)");

// Provide values for IN parameters// Error 2 - Forget to provide values for IN parameters String word = "Herong";// cs.setString(1,word);

// Register OUT parameters// Error 1 - Can not register OUT parameters cs.registerOutParameter(1, java.sql.Types.VARCHAR); cs.registerOutParameter(2, java.sql.Types.VARCHAR); cs.registerOutParameter(3, java.sql.Types.INTEGER);

// Execute the CALL statement and ignore result sets cs.executeUpdate();

// Retrieve values from OUT parameters String reversed = cs.getString(2); int length = cs.getInt(3); System.out.println("Input word: "+word); System.out.println("Output word: "+reversed); System.out.println("Word length: "+length);

// Close resource cs.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Compile and run this program as is, you will get this error message at line 28, because registering an IN parameter as statement output parameter is not allowed:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallParameterError.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallParameterError

Exception: Parameter number 1 is not an OUT parameterjava.sql.SQLException: Parameter number 1 is not an OUT parameter at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910) at com.mysql.jdbc.CallableStatement.checkIsOutputParam(...) at com.mysql.jdbc.CallableStatement.registerOutParameter(...) at MySqlCallParameterMissing.main(MySqlCallParameterError.java:30)

If you commented out this line, compile and run this program again, you will get a different error when calling cr.executeUpdate() at line 35. This time, no value was provided for the first parameter:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallParameterError.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallParameterError

Exception: No value specified for parameter 1java.sql.SQLException: No value specified for parameter 1 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910) at com.mysql.jdbc.PreparedStatement.fillSendPacket(...) at com.mysql.jdbc.PreparedStatement.executeUpdate(...) ... at MySqlCallParameterError.main(MySqlCallParameterError.java:35)

Creating CallableStatement Objects with INOUT Parameters

MySQL procedures do support INOUT parameters, which should be handled like IN parameters and OUT parameters as shown in the previous tutorial.

In a previous tutorial, I defined a store procedure called, C2F(), with 2 INOUT parameters. The program below shows you to create a CallableStatement object to execute this stored procedure:

/** * MySqlCallInoutParameter.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallInoutParameter { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Create CallableStatement CallableStatement cs = con.prepareCall("CALL C2F(?,?)");

// Register INOUT parameters cs.registerOutParameter(1, java.sql.Types.REAL); cs.registerOutParameter(2, java.sql.Types.REAL);

// Provide values for INOUT parameters double celsius = 100.0; cs.setDouble(1,celsius);

// Execute the CALL statement and ignore result sets cs.executeUpdate();

// Retrieve values from INOUT parameters double fahrenheit = cs.getDouble(2); System.out.println("First test:"); System.out.println(" Celsius = "+celsius); System.out.println(" Fahrenheit = "+fahrenheit);

// Second test celsius = 0.0; cs.setDouble(1,celsius); cs.executeUpdate(); fahrenheit = cs.getDouble(2); System.out.println("First test:"); System.out.println(" Celsius = "+celsius); System.out.println(" Fahrenheit = "+fahrenheit);

// Close resource cs.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The execution gave me one surprise. The second test failed with an error that I don' understand:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallInoutParameter.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallInoutParameter

First test: Celsius = 100.0 Fahrenheit = 212.0

Exception: Data truncated for column 'F' at row 1java.sql.SQLException: Data truncated for column 'F' at row 1 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:946) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2985) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1631) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1723) at com.mysql.jdbc.Connection.execSQL(Connection.java:3256) at com.mysql.jdbc.PreparedStatement.executeInternal(...) at com.mysql.jdbc.PreparedStatement.executeUpdate(...) ... at MySqlCallInoutParameter.main(MySqlCallInoutParameter.java:42)

Retrieving Multiple ResultSet ObjectsIf a stored procedure is returning multiple result sets, you should execute its CallableStatement object with the execute() method. In the case, an internal pointer will be maintained inside the CallableStatement object. This pointer is pointing the current result, which could be a result set or a update count.

If the current result is a result set, you can use the getResultSet() method to retrieve it into a ResultSet object.

If the current result is an update count, you can use the getUpdateCount() method to retrieve it as an integer.

To move the internal pointer to the next result, you can use the getMoreResult() method.

In a previous tutorial, I defined a store procedure called, HeadTail(), which returns 2 result sets. The program below shows you to create a CallableStatement object to execute this stored procedure:

/** * MySqlCallMultipleResultSet.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallMultipleResultSet { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Create CallableStatement CallableStatement cs = con.prepareCall("CALL HeadTail(?)");

// Register OUT parameters cs.registerOutParameter(1, java.sql.Types.INTEGER);

// Execute the CALL statement and expecting multiple result sets boolean isResultSet = cs.execute();

// First ReulstSet object if (!isResultSet) { System.out.println("The first result is not a ResultSet."); return; }

// First ReulstSet object System.out.println("Head of the table:"); ResultSet res = cs.getResultSet(); while (res.next()) { System.out.println(" "+res.getInt("ID") +", "+res.getString("FirstName") +", "+res.getString("LastName") +", "+res.getTimestamp("ModTime")); } res.close();

// Move to the next result isResultSet = cs.getMoreResults(); if (!isResultSet) { System.out.println("The next result is not a ResultSet."); return; }

// Second ReulstSet object System.out.println("Tail of the table:"); res = cs.getResultSet(); while (res.next()) { System.out.println(" "+res.getInt("ID") +", "+res.getString("FirstName") +", "+res.getString("LastName") +", "+res.getTimestamp("ModTime")); } res.close(); // Retrieve OUT parameters System.out.println("Total number of records: "+cs.getInt(1));

// Close resource cs.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output of the program perfectly matches my expectation:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallMultipleResultSet.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallMultipleResultSet

Head of the table: 80029, 2265, 9e559, 2006-12-31 23:59:59.0 80030, 378, 48189, 2007-08-31 21:35:14.0 80031, 1a46, 108f3, 2007-08-31 21:35:14.0 80032, 44f, c8511, 2007-08-31 21:35:14.0 80033, 1e9a, 51ca6, 2007-08-31 21:35:14.0 80034, 665, 6eb12, 2007-08-31 21:35:14.0 80035, 1b97, 30e6d, 2007-08-31 21:35:14.0 80036, 3d8, 270d6, 2007-08-31 21:35:14.0 80037, 26a3, b2290, 2007-08-31 21:35:14.0 80038, 1d65, d9d6d, 2007-08-31 21:35:14.0

Tail of the table: 90029, Herong, Yang, 2006-12-31 23:59:59.0 90028, 545, 728d4, 2007-08-31 21:35:17.0 90027, 169b, 1f61d, 2007-08-31 21:35:17.0 90026, 2559, 737b4, 2007-08-31 21:35:17.0 90025, 1db2, 48b31, 2007-08-31 21:35:17.0 90024, 149, cdf71, 2007-08-31 21:35:17.0 90023, 12a5, 8a909, 2007-08-31 21:35:17.0 90022, 1b67, c6f96, 2007-08-31 21:35:17.0 90021, 12a9, 20ac8, 2007-08-31 21:35:17.0

90020, 18a4, 1ffb4, 2007-08-31 21:35:17.0

Total number of records: 10001

Executing Stored Procedures without PermissionTo test what will happen if a Java application is trying to execute a stored procedure without enough permission, first I logged in to MySQL server with as "root", and created the following stored procedure:

C:\>\local\mysql\bin\mysql -u root

mysql> USE HerongDB;Database changed

mysql> DELIMITER '/';

mysql> -- Creating the stored proceduremysql> CREATE PROCEDURE Info(OUT User VARCHAR(80), -> OUT Catalog VARCHAR(80)) -> BEGIN -> SET User = USER(); -> SET Catalog = DATABASE(); -> END/Query OK, 0 rows affected (0.00 sec)

mysql> -- Testing the stored proceduremysql> CALL Info(@User, @Database)/Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @User, @Database/+----------------+-----------+| @User | @Database |+----------------+-----------+| root@localhost | herongdb |+----------------+-----------+1 row in set (0.00 sec)

Then I wrote the following program to run this stored procedure as user "Herong":

/** * MySqlCallPermissionError.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallPermissionError { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret");

con = ds.getConnection();

// Create CallableStatement CallableStatement cs = con.prepareCall("CALL Info(?,?)");

// Registering output parameters cs.registerOutParameter(1,java.sql.Types.VARCHAR); cs.registerOutParameter(2,java.sql.Types.VARCHAR);

// Execute the call statement cs.executeUpdate();

// Retrieve output parameters System.out.println("User: "+cs.getString(1)); System.out.println("Database: "+cs.getString(2));

// Close resource cs.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Since "Herong" does not have permission to run stored procedures created by "root" by default, I got the following error message:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallPermissionError.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallPermissionError

Exception: User does not have access to metadata required to determine stored procedure parameter types. If rights can not be granted, configure connection with "noAccessToProcedureBodies=true"to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.... at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910) at com.mysql.jdbc.DatabaseMetaData.getCallStmtParameterTypes(...) at com.mysql.jdbc.DatabaseMetaData.getProcedureColumns(...) at com.mysql.jdbc.CallableStatement.determineParameterTypes(...) at com.mysql.jdbc.CallableStatement.<init>(...) at com.mysql.jdbc.Connection.parseCallableStatement(...) at com.mysql.jdbc.Connection.prepareCall(Connection.java:4610) at com.mysql.jdbc.Connection.prepareCall(Connection.java:4584) at MySqlCallPermissionError.main(MySqlCallPermissionError.java:20)

getProcedures() - Listing Stored ProceduresIf you want to get a list of stored procedures through the JDBC interface, you can use the

getProcedures() method on the DatabaseMetaData object as shown in the program program below:

/** * MySqlCallGetProcedures.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlCallGetProcedures { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection(); DatabaseMetaData meta = con.getMetaData();

// Listing all stored procedures ResultSet res = meta.getProcedures(null, null, "%"); System.out.println("Stored procedures:"); while (res.next()) { System.out.println( " "+res.getString("PROCEDURE_CAT") + ", "+res.getString("PROCEDURE_SCHEM") + ", "+res.getString("PROCEDURE_NAME")); } res.close(); // Listing all tables res = meta.getTables(null, null, "%", null); System.out.println("Stored tables:"); while (res.next()) { System.out.println( " "+res.getString("TABLE_CAT") + ", "+res.getString("TABLE_SCHEM") + ", "+res.getString("TABLE_NAME")); } res.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

But the output was a surprise to me:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar

MySqlCallGetProcedures.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlCallGetProcedures

Stored procedures:Stored tables: HerongDB, null, profile

getProcedures() did not return any stored procedure names. This could be a bug in the MySQL JDBC driver (MySQL Connector/J 5.0.7).

MySQL CLOB (Character Large Object) - TEXT

This chapter provides tutorial notes on CLOB (Character Large Object) data types, TEXT, with MySQL JDBC Driver. Topics include creating tables to store CLOB values in MySQL server; inserting CLOB values with direct SQL INSERT statements, or PreparedStatement with setString(), setCharacterStream() or setClob() methods; retrieving CLOB values from ResultSet with getString(), getCharacterStream() or getClob() method.

Overview of CLOB (Character Large Object)

Create Tables with CLOB Columns

Inserting CLOB Values with SQL INSERT Statements

Inserting CLOB Values with setString() Method

Inserting CLOB Values with setCharacterStream() Method

Retrieving CLOB Values with getString() Method

Retrieving CLOB Values with getCharacterStream() Method

Retrieving CLOB Values with getClob() Method

Inserting CLOB Values with setClob() Method

Overview of CLOB (Character Large Object)

CLOB (Character Large Object) is a large collection of character data stored in a database table. Different database servers handles CLOB differently. But there are some common characteristics like:

CLOB stores character data, which requires a specific encoding schema. For regular ASCII characters, the database default encoding schema is fine. So you you don't need to worry.

CLOB data is usually large. Many database servers offer very high maximum sizes like 4 GB.

Operations on CLOB data is usually restricted. For example, LIKE may not be used on CLOB data.

CLOB data is usually not directly stored inside the table. It is stored in different storage areas and its reference address is stored inside the table.

JDBC offers an interface, java.sql.Clob, to allow JDBC driver to provide generic functionalities to handle CLOB data in the database.

CLOB data can also be handled as character strings with the java.lang.String class.

CLOB data can also be handled as character streams with the java.io.Reader class.

Create Tables with CLOB Columns

MySQL server support CLOB with 4 data types:

TINYTEXT - A CLOB column with a maximum length of 255 (2**8 - 1) characters.

TEXT - A CLOB column with a maximum length of 65,535 (2**16 - 1) characters.

MEDIUMTEXT - A CLOB column with a maximum length of 16,777,215 (2**24 - 1) characters.

LONGTEXT - A CLOB column with a maximum length of 4,294,967,295 or 4GB (2**32 - 1) characters.

In order to test CLOB columns in MySQL server, I used the MySQL commnand line interface to create a test table with one CLOB column:

C:\>\local\mysql\bin\mysql -u Herong -pTopSecret

mysql> use HerongDB;Database changed

mysql> CREATE TABLE Article (ID INTEGER PRIMARY KEY AUTO_INCREMENT, -> Subject VARCHAR(256) NOT NULL, -> Body LONGTEXT);Query OK, 0 rows affected (0.38 sec)

Inserting CLOB Values with SQL INSERT StatementsThe simplest way to insert a character string into a CLOB column is to use a SQL INSERT statement and include the character string a SQL string literal in the statement as shown in this sample program:

/** * MySqlClobInsert.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlClobInsert { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test on INSERT statement"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Article WHERE Subject = '"

+subject+"'");

// Inserting CLOB value with a regular insert statement sta = con.createStatement(); int count = sta.executeUpdate( "INSERT INTO Article" +" (Subject, Body)" +" VALUES ('"+subject+"', 'A BLOB (Binary Large OBject) is" +" a large chunk of data which is stored in a database.')");

// Retrieving CLOB value with getString() ResultSet res = sta.executeQuery( "SELECT * FROM Article WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = "+res.getString("Body")); res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the character string value was correctly inserted into the CLOB column:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobInsert.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobInsert

The inserted record: Subject = Test on INSERT statement Body = A BLOB (Binary Large OBject) is a large chunk of data which is stored in a database.

Using SQL string literals to insert CLOB values into database is not recommended, because quote characters (') in the CLOB values must be replaced with escape sequences ('').

Using PreparedStatement with setXXX() method is a much safer choice.

Inserting CLOB Values with setString() MethodAnother way to insert a character string into a CLOB column is to create a PreparedStatement object and use the setString() method. See the sample program below:

/** * SqlServerClobSetString.java

* Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerClobSetString { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of the setString() method"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Article WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a PreparedStatement PreparedStatement ps = con.prepareStatement( "INSERT INTO Article (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); ps.setString(2, "He is wonderful and strange and who knows" +" how old he is, he thought. Never have I had such" +" a strong fish nor one who acted so strangely..." +" He cannot know that it is only one man against him," +" nor that it is an old man. But what a great fish" +" he is and what will he bring in the market" +" if the flesh is good."); int count = ps.executeUpdate(); ps.close();

// Retrieving CLOB value with getString() ResultSet res = sta.executeQuery( "SELECT * FROM Article WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = "+res.getString("Body")); res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the character string value was correctly inserted into the CLOB column:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar SqlServerClobSetString.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar SqlServerClobSetString

The inserted record: Subject = Test of the setString() method Body = He is wonderful and strange and who knows how old he is,he thought. Never have I had such a strong fish nor one who actedso strangely... He cannot know that it is only one man against him,nor that it is an old man. But what a great fish he is and what will he bring in the market if the flesh is good.

Inserting CLOB Values with setCharacterStream() MethodIf you want to insert the entire content of a text file into a CLOB column, you should create a Reader object from this file, and use the PreparedStatement.setCharacterStream() method to pass the text file content to the CLOB column through the Reader object. There are 3 versions of the setCharacterStream() method, two of them were added as part of JDBC 4.0 (Java 1.6). Your JDBC driver may not support them:

setCharacterStream(int parameterIndex, Reader reader) - The data will be read from the Reader stream as needed until end-of-file is reached. This was added in JDBC 4.0 (Java 1.6).

setCharacterStream(int parameterIndex, Reader reader, int length) - The data will be read from the Reader stream as needed for "length" characters.

setCharacterStream(int parameterIndex, Reader reader, long length) - The data will be read from the Reader stream as needed for "length" characters. This was added in JDBC 4.0 (Java 1.6).

To test those setCharacterStream() methods, wrote the following program:

/** * MySqlClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlClobSetCharacterStream { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing

String subject = "Test of setCharacterStream() methods"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Article WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a PreparedStatement PreparedStatement ps = con.prepareStatement( "INSERT INTO Article (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); Reader bodyIn = new FileReader("MySqlClobSetCharacterStream.java");

// Test 1 - This will not work with JDBC 3.0 drivers// ps.setCharacterStream(2, bodyIn);

// Test 2 - This will not work with JDBC 3.0 drivers// File fileIn = new File("MySqlClobSetCharacterStream.java");// ps.setCharacterStream(2, bodyIn, fileIn.length());

// Test 3 - This works with JDBC 3.0 drivers File fileIn = new File("MySqlClobSetCharacterStream.java"); ps.setCharacterStream(2, bodyIn, (int) fileIn.length());

int count = ps.executeUpdate(); bodyIn.close(); ps.close();

// Retrieving CLOB value with getString() sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article" +" WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +res.getString("Body").substring(0,256)); res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

As I mentioned above setCharacterStream(int parameterIndex, Reader reader) will not work with JDBC 3.0 drivers. Here is what I got with the "Test 1" section open in my program. Remember that mysql-connector-java-5.0.7-bin is a JDBC 3.0 driver.

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobSetCharacterStream

Exception in thread "main" java.lang.AbstractMethodError: com.mysql

.jdbc.PreparedStatement.setCharacterStream(ILjava/io/Reader;)V at MySqlClobSetCharacterStream.main(MySqlClobSetCharac...java:34)

"Test 2" also failed for the same reason - fileIn.length() returns "long" and that setCharacterStream(int, Reader, long) is JDBC 4.0 method:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobSetCharacterStream

Exception in thread "main" java.lang.AbstractMethodError: com.mysql .jdbc.PreparedStatement.setCharacterStream(ILjava/io/Reader;J)V at MySqlClobSetCharacterStream.main(MySqlClobSetCharac...java:38)

Of course, "Test 3" worked nicely.

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobSetCharacterStream

The inserted record: Subject = Test of setCharacterStream() methods Body = /** * MySqlClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlClobSetCharacterStream { public static void main(String [] args) { Connection con = null

Retrieving CLOB Values with getString() MethodThe simplest way to retrieve the character string value from a CLOB column is to use the getString() method on the ResultSet object. Here is short example program:

/** * MySqlClobGetString.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlClobGetString { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving CLOB value with getString()

Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); String body = res.getString("Body"); if (body.length() > 100) body = body.substring(0,100); System.out.println(" Body = "+body+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output of the program confirms that CLOB values can be retrieved with getString() method on the ResultSet object:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobGetString

Record ID: 3 Subject = Test on INSERT statement Body = A BLOB (Binary Large OBject) is a large chunk of data which is stored in a database....

Record ID: 4 Subject = Test of the setString() method Body = He is wonderful and strange and who knows how old he is, he thought. Never have I had such a strong...

Record ID: 8 Subject = Test of setCharacterStream() methods Body = /** * MySqlClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserve...

Retrieving CLOB Values with getCharacterStream() MethodCLOB values can also be retrieved with the getCharacterStream() method on the ResultSet object, which will return a Reader object. Then you can read the CLOB values from the Reader object with the read() method. The sample program below shows you how to create Reader objects with the getCharacterStream() method. A utility method is included to read all characters from a Ready object and save them in a file.

/** * MySqlClobGetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved.

*/import java.io.*;import java.sql.*;public class MySqlClobGetCharacterStream { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving CLOB value with getCharacterStream() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); Reader bodyOut = res.getCharacterStream("Body"); String fileOut = "ClobOut_"+res.getInt("ID")+".txt"; saveReader(fileOut,bodyOut); bodyOut.close(); System.out.println(" Body = (Saved in "+fileOut+")"); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static void saveReader(String name, Reader body) { int c; try { Writer f = new FileWriter(name); while ((c=body.read())>-1) { f.write(c); } f.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output looked correct:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobGetCharacterStream

Record ID: 3 Subject = Test on INSERT statement Body = (Saved in ClobOut_3.txt)

Record ID: 4 Subject = Test of the setString() method Body = (Saved in ClobOut_4.txt)

Record ID: 8 Subject = Test of setCharacterStream() methods Body = (Saved in ClobOut_8.txt)

I checked file ClobOut_8.txt. The CLOB value, the MySqlClobSetCharacterStream.java, program was corrected saved.

Retrieving CLOB Values with getClob() MethodIf you like to work with java.sql.Clob objects, you can retrieve CLOB values with the getClob() method on ResultSet objects. The Clob object offers some interesting methods:

length() - Returns the number of characters in the Clob object. The return value has a type of "long". You may need to convert it to "int" to be used in other motheds.

getSubString(long pos, int length) - Returns a substring of characters from the Clob object with a specified starting position and length. Note the start position is "long" value, but the length is "int" value.

getCharacterStream() - Returns a Reader object from the Clob object so that you can read the content as a stream.

free() - Releases the resources that the Clob object holds. This was added in JDBC 4.0 (Java 1.6).

Here is my test program on getClob() method:

/** * MySqlClobGetClob.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlClobGetClob { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret");

con = ds.getConnection();

// Retrieving CLOB value with getClob() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); Clob bodyOut = res.getClob("Body"); int length = (int) bodyOut.length(); System.out.println(" Body Size = "+length); String body = bodyOut.getSubString(1, length); if (body.length() > 100) body = body.substring(0,100); System.out.println(" Body = "+body+"...");// bodyOut.free(); // new in JDBC 4.0 } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output confirms that the getClob() method and Clob objects are not hard to use:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobGetClob

Record ID: 3 Subject = Test on INSERT statement Body Size = 84 Body = A BLOB (Binary Large OBject) is a large chunk of data which is stored in a database....

Record ID: 4 Subject = Test of the setString() method Body Size = 304 Body = He is wonderful and strange and who knows how old he is, he thought. Never have I had such a strong...

Record ID: 8 Subject = Test of setCharacterStream() methods Body Size = 2204 Body = /** * MySqlClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserve...

Inserting CLOB Values with setClob() MethodIf you want to insert a CLOB column with a character string that comes from a

java.sql.Clob object, you can directly set the value with PreparedStatement.setClob() method.

To test this, I wrote the following program to copy some records with CLOB values as new records back into the same table. During the copy process, the CLOB values are also modified with some Clob object methods - The first 32 characters are converted to upper case characters.

/** * MySqlClobSetClob.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlClobSetClob { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting records for re-testing Statement sta = con.createStatement(); sta.executeUpdate( "DELETE FROM Article WHERE Subject LIKE 'Copy of %'");

// Creating a PreparedStatement for inserting new records PreparedStatement ps = con.prepareStatement( "INSERT INTO Article (Subject, Body) VALUES (?,?)");

// Looping though the first 3 records ResultSet res = sta.executeQuery( "SELECT * FROM Article ORDER BY ID"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Copying record ID: "+res.getInt("ID")); String subject = res.getString("Subject"); Clob body = res.getClob("Body");

// Modifying the Clob object String chuck = body.getSubString(1,32); chuck = chuck.toUpperCase(); body.setString(1,chuck);

// Inserting a new record with setClob() ps.setString(1, "Copy of "+subject); ps.setClob(2,body); ps.executeUpdate();

} ps.close(); res.close(); // Checking the new records res = sta.executeQuery( "SELECT * FROM Article WHERE Subject LIKE 'Copy of %'"); while (res.next()) { System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); String body = res.getString("Body"); if (body.length() > 100) body = body.substring(0,100); System.out.println(" Body = "+body+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The program performed exactly as I expected:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlClobSetClob

Copying record ID: 3Copying record ID: 4Copying record ID: 8

Record ID: 9 Subject = Copy of Test on INSERT statement Body = A BLOB (BINARY LARGE OBJECT) IS a large chunk of data which is stored in a database....

Record ID: 10 Subject = Copy of Test of the setString() method Body = HE IS WONDERFUL AND STRANGE AND who knows how old he is,he thought. Never have I had such a strong ...

Record ID: 11 Subject = Copy of Test of setCharacterStream() methods Body = /** * MYSQLCLOBSETCHARACTERSTREam.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserve...

MySQL BLOB (Binary Large Object) - BLOB

This chapter provides tutorial notes on BLOB (Binary Large Object) data types, BLOB, with MySQL JDBC Driver. Topics include creating tables to store BLOB values in MySQL server; inserting BLOB values with direct SQL INSERT statements, or

PreparedStatement with setBytes(), setBinaryStream() or setBlob() methods; retrieving BLOB values from ResultSet with getBytes(), getBinaryStream() or getBlob() method.

Overview of BLOB (Binary Large Object)

Create Tables with CLOB Columns

Inserting BLOB Values with SQL INSERT Statements

Inserting BLOB Values with setBytes() Method

Inserting BLOB Values with setBinaryStream() Method

Closing InputStream Too Early on setBinaryStream()

Retrieving BLOB Values with getBytes() Method

Retrieving BLOB Values with getBinaryStream() Method

Retrieving BLOB Values with getBlob() Method

Inserting BLOB Values with setBlob() Method

Overview of BLOB (Binary Large Object)BLOB (Binary Large Object) is a large collection of binary data stored in a database table. Different database servers handles BLOB differently. But there are some common characteristics like:

BLOB stores binary data, which requires no encoding schema. BLOB data is stored in units of bytes.

BLOB data is usually large. Many database servers offer very high maximum sizes like 4 GB.

BLOB data is usually not directly stored inside the table. It is stored in different storage areas and its reference address is stored inside the table.

JDBC offers an interface, java.sql.Blob, to allow JDBC driver to provide generic functionalities to handle BLOB data in the database.

BLOB data can also be handled as byte arrays: byte[].

BLOB data can also be handled as binary streams with the java.io.InputStream class.

The data type and definition was introduced to describe data not originally defined in traditional computer database systemsCreate Tables with CLOB ColumnsMySQL server support BLOB with 4 data types:

TINYBLOB - A BLOB column with a maximum length of 255 (2**8 - 1) bytes. BLOB - A BLOB column with a maximum length of 65,535 (2**16 - 1) bytes. MEDIUMBLOB - A BLOB column with a maximum length of 16,777,215

(2**24 - 1 ) bytes. LONGBLOB - A BLOB column with a maximum length of 4,294,967,295 or

4GB (2**32 - 1) bytes.

In order to test BLOB columns in MySQL server, I used the MySQL commnand line interface to create a test table with one BLOB column:

C:\>\local\mysql\bin\mysql -u Herong -pTopSecret

mysql> use HerongDB;Database changed

mysql> CREATE TABLE Image (ID INTEGER PRIMARY KEY AUTO_INCREMENT, -> Subject VARCHAR(256) NOT NULL, -> Body LONGBLOB);Query OK, 0 rows affected (0.01 sec)

Inserting BLOB Values with SQL INSERT Statementshe simplest way to insert a binary string into a BLOB column is to use a SQL INSERT statement and include the binary string a SQL binary literal in the statement as shown in this sample program. Note that SQL binary literal format is x'<hex_numbers>'.

/** * MySqlBlobInsert.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;import java.nio.charset.*;public class MySqlBlobInsert { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test on INSERT statement"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a regular insert statement sta = con.createStatement(); int count = sta.executeUpdate(

"INSERT INTO Image" +" (Subject, Body)" +" VALUES ('"+subject+"'" +", x'C9CBBBCCCEB9C8CABCCCCEB9C9CBBB')");

// Retrieving BLOB value with getBytes() ResultSet res = sta.executeQuery( "SELECT * FROM Image WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"))); res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the character string value was correctly inserted into the BLOB column:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobInsert.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobInsert

The inserted record: Subject = Test on INSERT statement Body = ╔╦╗╠╬╣╚╩╝╠╬╣╔╦╗

Using SQL binary literals to insert BLOB values into database is simple. But it requires you to convert your binary data into the SQL binary literal format: x'<hex_numbers>', which could be a problem if you have a very long binary data to enter.

Using PreparedStatement with setXXX() method is a much better choice.

Inserting BLOB Values with setBytes() MethodAnother way to insert a binary string into a BLOB column is to create a PreparedStatement object and use the setBytes() method. See the sample program below:

/** * MySqlBlobSetBytes.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class MySqlBlobSetBytes { public static void main(String [] args) {

Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of the setBytes() method"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with PreparedStatement.setBytes() PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); byte[] bodyIn = {(byte)0xC9, (byte)0xCB, (byte)0xBB, (byte)0xCC, (byte)0xCE, (byte)0xB9, (byte)0xC8, (byte)0xCA, (byte)0xBC, (byte)0xCC, (byte)0xCE, (byte)0xB9, (byte)0xC9, (byte)0xCB, (byte)0xBB}; ps.setBytes(2, bodyIn); int count = ps.executeUpdate(); ps.close();

// Retrieving BLOB value with getBytes() ResultSet res = sta.executeQuery( "SELECT * FROM Image WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"))); res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the binary string value, stored in a byte array, was correctly inserted into the CLOB column:

C:\>javac -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBytes.java

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBytes

The inserted record: Subject = Test on INSERT statement Body = ╔╦╗╠╬╣╚╩╝╠╬╣╔╦╗

Inserting BLOB Values with setBinaryStream() MethodIf you want to insert the entire content of a binary file into a BLOB column, you should create an InputStream object from this file, and use the PreparedStatement.setBinaryStream() method to pass the binary file content to the BLOB column through the InputStream object. There are 3 versions of the setBinaryStream() method, two of them were added as part of JDBC 4.0 (Java 1.6). Your JDBC driver may not support them:

setBinaryStream(int parameterIndex, InputStream x) - The data will be read from the InputStream as needed until end-of-file is reached. This was added in JDBC 4.0 (Java 1.6).

setBinaryStream(int parameterIndex, InputStream x, int length) - The data will be read from the InputStream as needed for "length" bytes.

setBinaryStream(int parameterIndex, InputStream x, long length) - The data will be read from the InputStream as needed for "length" bytes. This was added in JDBC 4.0 (Java 1.6).

To test those setBinaryStream() methods, wrote the following program:

/** * MySqlBlobSetBinaryStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlBlobSetBinaryStream { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of setBinaryStream() methods"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a PreparedStatement PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)");

ps.setString(1, subject); InputStream bodyIn = new FileInputStream("MySqlBlobSetBinaryStream.class");

// Test 1 - This will not work with JDBC 3.0 drivers// ps.setBinaryStream(2, bodyIn);

// Test 2 - This will not work with JDBC 3.0 drivers// File fileIn = new File("MySqlBlobSetBinaryStream.class");// ps.setBinaryStream(2, bodyIn, fileIn.length());

// Test 3 - This works with JDBC 3.0 drivers File fileIn = new File("MySqlBlobSetBinaryStream.class"); ps.setBinaryStream(2, bodyIn, (int) fileIn.length());

int count = ps.executeUpdate(); bodyIn.close(); ps.close();

// Retrieving BLOB value with getBytes() sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image" +" WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"),0,32)); res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

As I mentioned above setBinaryStream(int parameterIndex, InputStream x) will not work with JDBC 3.0 drivers. Here is what I got with the "Test 1" section open in my program. Remember that mysql-connector-java-5.0.7-bin is a JDBC 3.0 driver.

C:>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBinaryStream

Exception in thread "main" java.lang.AbstractMethodError: com.mysql .jdbc.PreparedStatement.setBinaryStream(ILjava/io/InputStream;)V at MySqlBlobSetBinaryStream.main(MySqlBlobSetBinaryStream.java:34)

"Test 2" also failed for the same reason - fileIn.length() returns "long" and that setCharacterStream(int, InputStream, long) is JDBC 4.0 method:

C:>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBinaryStream

Exception in thread "main" java.lang.AbstractMethodError: com.mysql .jdbc.PreparedStatement.setBinaryStream(ILjava/io/InputStream;J)V at MySqlBlobSetBinaryStream.main(MySqlBlobSetBinaryStream.java:38)

Of course, "Test 3" worked nicely.

C:>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBinaryStream

The inserted record: Subject = Test of setBinaryStream() methods Body = -||+ 2 | ; H I ? J ? K

Closing InputStream Too Early on setBinaryStream()The program in the previous tutorial worked nicely. But if you make a mistake by placing the bodyIn.close() statement before ps.executeUpdate(), you will get an IOException when ps.executeUpdate() is called. The reason is simple, reading of binary data from the InputStream is done at the time of executeUpdate() call, not before. Here is a test program:

/** * MySqlBlobSetBinaryStreamError.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlBlobSetBinaryStreamError { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of setBinaryStream() method error"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with PreparedStatement.setBinaryStream() PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); InputStream bodyIn =

new FileInputStream("MySqlBlobSetBinaryStream.class"); File fileIn = new File("MySqlBlobSetBinaryStream.class"); ps.setBinaryStream(2, bodyIn, (int) fileIn.length());

// Error - Closing the InputStream too early. bodyIn.close();

int count = ps.executeUpdate(); ps.close();

// Retrieving BLOB value with getBytes() sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image" +" WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"),0,32)); res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The IOException is shown below. It doesn't tell you that the InputStream is closed.

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBinaryStreamError

Exception: Error reading from InputStream java.io.IOExceptionjava.sql.SQLException: Error reading from InputStream java.io.IOException at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910) at com.mysql.jdbc.PreparedStatement.readblock(...) at com.mysql.jdbc.PreparedStatement.streamToBytes(...) at com.mysql.jdbc.PreparedStatement.fillSendPacket(...) at com.mysql.jdbc.PreparedStatement.executeUpdate(...) at MySqlBlobSetBinaryStreamError.main(MySqlBlobSetBinar...java:38)

Retrieving BLOB Values with getBytes() MethodThe simplest way to retrieve the character string value from a BLOB column is to use the getBytes() method on the ResultSet object. Here is short example program:

/** * MySqlBlobGetBytes.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;

public class MySqlBlobGetBytes { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving BLOB value with getBytes() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); byte[] body = res.getBytes("Body"); String bodyHex = bytesToHex(body, 32); System.out.println(" Body in HEX = "+bodyHex+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static String bytesToHex(byte[] bytes, int max) { StringBuffer buffer = new StringBuffer(); for (int i=0; i<bytes.length && i<max; i++) { buffer.append(Integer.toHexString(bytes[i] & 0xFF)); } return buffer.toString().toUpperCase(); }}

bytesToHex() method is used to convert a byte array to a Hex string. The output of the program confirms that CLOB values can be retrieved with getBytes() method on the ResultSet object:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobGetBytes

Record ID: 4 Subject = Test on INSERT statement Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 10 Subject = Test of the setBytes() method

Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 15 Subject = Test of setBinaryStream() methods Body in HEX = CAFEBABE000320B5A03B0487049A02048804AA0204BA...

Retrieving BLOB Values with getBinaryStream() MethodBLOB values can also be retrieved with the getBinaryStream() method on the ResultSet object, which will return an OutputStream object. Then you can read the BLOB values from the OutputStream object with the read() method. The sample program below shows you how to create OutputStream objects with the getBinaryStream() method. A utility method is included to read all bytes from an OutputStream object and save them in a file.

/** * MySqlBlobGetBinaryStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlBlobGetBinaryStream { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving CLOB value with getBinaryStream() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); InputStream bodyOut = res.getBinaryStream("Body"); String fileOut = "BlobOut_"+res.getInt("ID")+".bin"; saveOutputStream(fileOut,bodyOut); bodyOut.close(); System.out.println(" Body = (Saved in "+fileOut+")"); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static void saveOutputStream(String name, InputStream body) {

int c; try { OutputStream f = new FileOutputStream(name); while ((c=body.read())>-1) { f.write(c); } f.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output looked correct:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobGetBinaryStream

Record ID: 4 Subject = Test on INSERT statement Body = (Saved in BlobOut_4.bin)Record ID: 10 Subject = Test of the setBytes() method Body = (Saved in BlobOut_10.bin)Record ID: 15 Subject = Test of setBinaryStream() methods Body = (Saved in BlobOut_15.bin)

I also tested BlobOut_15.bin file to make sure that I am getting my MySqlBlobSetBinaryStream.class file back correctly:

C:\>del MySqlBlobSetBinaryStream.class

C:\>copy BlobOut_15.bin MySqlBlobSetBinaryStream.class 1 file(s) copied.

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBinaryStream

The inserted record: Subject = Test of setBinaryStream() methods Body = -||+ 2 |...

Retrieving BLOB Values with getBlob() MethodIf you like to work with java.sql.Blob objects, you can retrieve BLOB values with the getBlob() method on ResultSet objects. The Blob object offers some interesting methods:

length() - Returns the number of bytes in the Blob object. The return value has a type of "long". You may need to convert it to "int" to be used in other motheds.

getBytes(long pos, int length) - Returns a substring of characters from the Blob object with a specified starting position and length. Note the start position is "long" value, but the length is "int" value.

getBinaryStream() - Returns a InputStream object from the Blob object so that you can read the content as a stream.

free() - Releases the resources that the Blob object holds. This was added in JDBC 4.0 (Java 1.6).

Here is my test program on getBlob() method:

/** * MySqlBlobGetBlob.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlBlobGetBlob { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving BLOB value with getBlob() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); Blob bodyOut = res.getBlob("Body"); int length = (int) bodyOut.length(); System.out.println(" Body Size = "+length); byte[] body = bodyOut.getBytes(1, length); String bodyHex = bytesToHex(body, 32); System.out.println(" Body in HEX = "+bodyHex+"...");// bodyOut.free(); // new in JDBC 4.0 } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }

public static String bytesToHex(byte[] bytes, int max) { StringBuffer buffer = new StringBuffer(); for (int i=0; i<bytes.length && i<max; i++) { buffer.append(Integer.toHexString(bytes[i] & 0xFF)); } return buffer.toString().toUpperCase(); }}

The output confirms that the getBlob() method and Blob objects are not hard to use:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobGetBlob

Record ID: 4 Subject = Test on INSERT statement Body Size = 15 Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 10 Subject = Test of the setBytes() method Body Size = 15 Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 16 Subject = Test of setBinaryStream() methods Body Size = 2640 Body in HEX = CAFEBABE000320B5A03B0487049A02048804AA0204BA...

Inserting BLOB Values with setBlob() MethodIf you want to insert a BLOB column with a character string that comes from a java.sql.Blob object, you can directly set the value with PreparedStatement.setBlob() method.

To test this, I wrote the following program to copy some records with BLOB values as new records back into the same table. During the copy process, the BLOB values are also modified with some Blob object methods - The first 6 bytes are replaced with 0 values.

/** * MySqlBlobSetBlob.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class MySqlBlobSetBlob { public static void main(String [] args) { Connection con = null; try { com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); ds.setServerName("localhost"); ds.setPortNumber(3306); ds.setDatabaseName("HerongDB"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting records for re-testing Statement sta = con.createStatement(); sta.executeUpdate( "DELETE FROM Image WHERE Subject LIKE 'Copy of %'");

// Creating a PreparedStatement for inserting new records PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)");

// Looping though the first 3 records ResultSet res = sta.executeQuery( "SELECT * FROM Image ORDER BY ID"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Copying record ID: "+res.getInt("ID")); String subject = res.getString("Subject"); Blob body = res.getBlob("Body");

// Modifying the Blob object byte[] chuck = {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00}; body.setBytes(1,chuck);

// Inserting a new record with setBlob() ps.setString(1, "Copy of "+subject); ps.setBlob(2,body); ps.executeUpdate(); } ps.close(); res.close(); // Checking the new records res = sta.executeQuery( "SELECT * FROM Image WHERE Subject LIKE 'Copy of %'"); while (res.next()) { System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); byte[] body = res.getBytes("Body"); String bodyHex = bytesToHex(body, 32); System.out.println(" Body in HEX = "+bodyHex+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static String bytesToHex(byte[] bytes, int max) { StringBuffer buffer = new StringBuffer(); for (int i=0; i<bytes.length && i<max; i++) { buffer.append(Integer.toHexString(bytes[i] & 0xFF)); }

return buffer.toString().toUpperCase(); }}

The program performed exactly as I expected:

C:\>java -cp .;\local\lib\mysql-connector-java-5.0.7-bin.jar MySqlBlobSetBlob

Copying record ID: 4Copying record ID: 10Copying record ID: 16Record ID: 17 Subject = Copy of Test on INSERT statement Body in HEX = 000000C8CABCCCCEB9C9CBBB...Record ID: 18 Subject = Copy of Test of the setBytes() method Body in HEX = 000000C8CABCCCCEB9C9CBBB...Record ID: 19 Subject = Copy of Test of setBinaryStream() methods Body in HEX = 0000000320B5A03B0487049A02048804AA0204BA...

********************************************************Microsoft sql Server 2005 expressMicrosoft SQL Server 2005 Express EditionThis chapter provides tutorial notes on how to install Microsoft SQL Server 2005 Express Edition on your own Windows system. Topics include downloading, installing and running SQL Server 2005 Express Edition; using SQLCMD command line interface; installing AdventureWorksLT sample database.

Downloading Microsoft SQL Server 2005 Express Edition

Installing Microsoft SQL Server 2005 Express Edition

SQLCMD SQL Server Command Line Tool

Installing AdventureWorksLT Sample Database

Downloading Microsoft SQL Server 2005 Express EditionIf you want to learn JDBC connecting to databases managed by Microsoft SQL Server, you could download and install a copy of MS (Microsoft) SQL Server 2005 Express Edition on your local system.

Microsoft SQL Server 2005 Express Edition is the free version of the Microsoft SQL Server 2005. This tutorial shows you how to download Microsoft SQL Server 2005 Express Edition:

1. Go to the Microsoft SQL Server 2005 Express Edition download page.

2. Go to the Files in This Download section, and click Download button next to the "SQLEXPR.EXE - 53.5 MB" file. The File Download box shows up.

3. Save the download file to C:\temp directory. When the download is done, you should get the following file:

Name: SQLEXPR.EXEDescription: Microsoft SQL 2005 Server Express EditionLocation: C:\tempSize: 56,105,688 bytesVersion: 9.0.1399.6

Installing Microsoft SQL Server 2005 Express EditionAssuming that your system meets all the requirements listed on the MS SQL Server 2005 requirement page, Now follow this tutorial to install SQL Server 2005 Express Edition on your local machine.

1. Double click c:\temp\SQLEXPR.EXE. The setup window shows up.

2. Click Next to let the setup program to unpack all files from the downloaded file.

3. When unpack is down, the setup program will check all required programs on your system.

4. Then the setup program will start the installation process.

5. On the Authentication Mode window, click the radio button for Mixed Mode (Windows Authentication and SQL Server Authentication). And enter "HerongYang" in the "Specify the sa logon password below:" field.

6. Continue to finish the installation process.

When installation is done, run Windows Task Manager. You should see a process called sqlservr.exe running in the process list:

sqlservr.exe 00 1,316 K

SQLCMD SQL Server Command Line ToolBefore you start using Java programs to interact with SQL Server databases, you should learn some SQL Server client tools to run SQL statements on the SQL Server. SQLCMD, Query Analyzer, and Management Studio are good client tools to use. But SQLCMD comes with SQL Server 2005 Express Edition installation, and is ready to use.

This tutorial shows you how to use SQLCMD, a command line client tool, to run SQL statements on your local SQL Server databases.

When "sqlcmd" is started and connected to a SQL Server, it will you to enter statements or commands. You can enter one or more statements in one or more lines to form a Transact-SQL statement batch.

To end a batch of statements and send it to the SQL Server for execution, you need to enter the GO command. The following "sqlcmd" tutorial session sends two batches to the SQL Server:

C:\>sqlcmd -S localhost -U sa -P HerongYang1> SELECT DB_NAME();2> GO

------------------------master

(1 rows affected)

1>QUITC:\

As you can see, I logged in to the server as "sa" with no problem. The "GO" command sends one SQL statement to the server. The returning output telling me that I was connected to the "master" database.

Installing AdventureWorksLT Sample DatabaseTo practice JDBC interface with SQL Server, you need some tables and data on the database server. Microsoft has a nice sample database called AdventureWorksLT available free for you to use. This tutorial shows you how to download and install AdventureWorksLT on your local SQL Server.

1. Go to the SQL Server 2005 Samples and Sample Databases download page.

2. Go to the x86 section in the Instructions section, Click "AdventureWorksLT.msi -- 2,251 KB" to download the sample file. Save the download file to c:\temp.

3. Double click on the downloaded file: c:\temp\AdventureWorksLT.msi. The installation setup window shows up. Follow the instructions to finish the installation.

When the installation is done, two physical database files are installed in the data directory: C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data:

AdventureWorksLT_Data.mdf 5,120 KBAdventureWorksLT_Log.ldf 2,048 KB

To finish the installation, you need to attach the database physical files to your SQL server by running the following SQL statement with SQLCMD interface:

EXEC sp_attach_db @dbname=N'AdventureWorksLT', @filename1=N'C:\Program Files\Microsoft SQL Server'

+'\MSSQL.1\MSSQL\Data\AdventureWorks_Data.mdf', @filename2=N'C:\Program Files\Microsoft SQL Server' +'\MSSQL.1\MSSQL\Data\AdventureWorks_Log.ldf'GO

The sample database AdventureWorksLT is ready for you to play now.

Microsoft JDBC Driver for SQL Server - sqljdbc.jar

This chapter provides tutorial notes on sqljdbc.jar, JDBC Driver 1.0 produced by Microsoft for SQL Server 2005. Topics include downloading and installing Microsoft JDBC Driver 1.0; loading sqljdbc.jar; creating a connect to SQL Server with port number and database name; closing a connection.

Installing Microsoft JDBC Driver for SQL Server

Loading Driver Class with Class.forName()

DriverManager.getConnection() and Connection URL

Specifying Port Number in Connection URL

Closing the Database Connection - con.close()

Specifying Database Name in Connection URL

Incorrect Database Name in Connection URL

Creating Connections with DataSource Class

Installing Microsoft JDBC Driver for SQL ServerIf you want to access SQL Server database from Java applications, you need to install a JDBC driver that supports SQL Server. There are many software vendors offer such JDBC drivers.

Microsoft offers a free driver, JDBC Driver 1.0, which is JDBC 3.0 compliant. JDBC Driver 1.0 is a Type 4 JDBC driver using native-protocols.

The tutorial below shows you how to download and install the JDBC Driver 1.0 from Microsoft.

1. Go to Microsoft JDBC page.

2. Click the Download button. The license terms page shows up.

3. Click the link "I Accept and I want to download the Microsoft Windows version" near the end of the page. The download window shows up.

4. Save the downloaded file, sqljdbc_1.0.809.102_enu.exe, to C:\temp directory.

5. Double click on sqljdbc_1.0.809.102_enu.exe. It will be unzipped to a local directory called: "Microsoft SQL Server 2005 JDBC Driver". The JDBC driver .jar file, sqljdbc.jar, is located under the "sqljdbc_1.0\enu" subdirectory.

6. Copy sqljdbc.jar to C:\local\lib\sqljdbc.jar. You are ready to use JDBC Driver 1.0 now.

Loading Driver Class with Class.forName()The first step of using JDBC to connect to SQL Server is to load the JDBC driver class. To do this you need to use the Class.forName() method to load the driver class, com.microsoft.sqlserver.jdbc.SQLServerDriver, which represents the Microsoft JDBC Driver.

Here is my sample program to load Microsoft JDBC Driver 1.0 class:

/** * ConnectionTest1.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ConnectionTest1 { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); System.out.println("JDBC driver loaded ok."); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } }}

Compile and run this program:

C:\>\progra~1\java\jdk1.6.0_02\bin\javac ConnectionTest1.java

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp . ConnectionTest1

ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver

I was getting this exception, because I forgot to add the JDBC driver jar file to the classpath in the "-cp" option. Here is the correct way to run this Java program with the JDBC driver jar file:

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar

ConnectionTest1

JDBC driver loaded ok.

DriverManager.getConnection() and Connection URLOnce the JDBC driver class is loaded, you are ready to connect to a SQL Server by using the DriverManager.getConnection(connection_url) method. The connection URL, connection_url, is a string with the following syntax:

jdbc:sqlserver://server_name;user=login;password=****

The tutorial program below shows you a good example of using getConnection() and connection URL:

/** * ConnectionTest2.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ConnectionTest2 { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost;" + "user=sa;password=HerongYang");

} catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you may get a surprising error like this:

C:\>\progra~1\java\jdk1.6.0_02\bin\javac ConnectionTest2.java

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar ConnectionTest2

SQLException: The TCP/IP connection to the host has failed. java.net.ConnectException: Connection refused: connect

The error is probably caused by the port number where the SQL Server is listening for the client connection. If port number is different than the default number, you must specify the port number in the connection url as shown in the next tutorial.

Specifying Port Number in Connection URLIf the SQL Server is configured with a non-default port number, you need to specify the port number in the connection url in the following syntax:

jdbc:sqlserver://server_name:port;user=login;password=****

For example, if you installed the SQL Server 2005 Express Edition, it will be configured with 1269 as the port number.

The tutorial program below shows you how to include port number 1296 in the connection url:

/** * ConnectionTest3.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ConnectionTest3 { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang");

// Connection is ready to use DatabaseMetaData meta = con.getMetaData(); System.out.println("Driver name: " + meta.getDriverName()); System.out.println("Driver version: " + meta.getDriverVersion()); System.out.println("Server name: " + meta.getDatabaseProductName()); System.out.println("Server version: " + meta.getDatabaseProductVersion()); System.out.println("Connection URL: " + meta.getURL()); System.out.println("Login name: " + meta.getUserName());

} catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage());

} catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

Compile and run it, you should get:

C:\>\progra~1\java\jdk1.6.0_02\bin\javac ConnectionTest3.java

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar ConnectionTest3

Driver name: Microsoft SQL Server 2005 JDBC DriverDriver version: 1.0.809.102Server name: Microsoft SQL ServerServer version: 9.00.1399Connection URL: jdbc:sqljdbc://Login name: sa

Closing the Database Connection - con.close()When you obtained a connection object with DriverManager.getConnection() successfully, you can create statement objects and execute them with this connection object. But when you are done with this connection, you should close it to free resources associated this connection on the SQL server. To close a connection, you should call the close() method on this connection object.

The tutorial program below shows you how to call close() on a connection object. isClosed() method is called to verify the status of the connection object.

/** * CloseConnection.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class CloseConnection { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang");

// Connection is ready to use DatabaseMetaData meta = con.getMetaData(); System.out.println("Server name: "

+ meta.getDatabaseProductName()); System.out.println("Server version: " + meta.getDatabaseProductVersion());

// Closing the connection con.close(); if (con.isClosed()) System.out.println("Connection closed.");

} catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get:

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar CloseConnection

Server name: Microsoft SQL ServerServer version: 9.00.1399Connection closed.

Specifying Database Name in Connection URLWhen you create a connection object to a SQL Server without specifying any database name, the SQL Server will connect you to the default database that is linked to the login name. For example, "master" is the default database for login name "sa". If you create a connection with "sa" login name, the connection will be linked to "master" database.

If you want to work with a different database, you must specify the database name in the connection url in the following syntax:

jdbc:sqlserver://server_name:port;database=name;...

The tutorial program below shows you how to connect to the local SQL Server and select the sample database "AdventureWorksLT" as the working database.

/** * SelectDatabase.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SelectDatabase { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Checking the database name Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT DB_NAME()"); res.next(); String name = res.getString(1); System.out.println("Connected to database: "+name);

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

The program output will confirm you that the database is selected correctly.

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar SelectDatabase

Connected to database: AdventureWorksLT

Incorrect Database Name in Connection URLIf you selected a wrong database, or the login name has no access to that database, getConnection() method will throw a SQLExection object for you to catch. In this case, the getMessage() method of the SQLExection object will give you the detailed error message.

The tutorial program below shows you an example of the SQLException resulting from a wrong database name in the connection URL:

/** * WrongDatabase.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class WrongDatabase { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLI");

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

You will get the following message, if you run it:

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar WrongDatabase

SQLException: Cannot open database "AdventureWorksLI" requested by the login. The login failed.

The error message is not so accurate. The login was correct. The select database operation was failed, because database "AdventureWorksLI" does not exist on the SQL Server.

Creating Connections with DataSource ClassIt is recommended now that connection objects are created by the DataSource implementation class, com.microsoft.sqlserver.jdbc.SQLServerDataSource. Here is a sample program that creates a connection object using the DataSource class without using JNDI services:

/** * SqlServerDataSource.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerDataSource { public static void main(String [] args) { Connection con = null; try {

// Setting up the DataSource object com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269);

ds.setDatabaseName("AdventureWorksLT"); ds.setUser("sa"); ds.setPassword("HerongYang"); // Getting a connection object con = ds.getConnection(); // Getting database info DatabaseMetaData meta = con.getMetaData(); System.out.println("Server name: " + meta.getDatabaseProductName()); System.out.println("Server version: " + meta.getDatabaseProductVersion());

// Closing the connection con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

The output confirms that I got a good connection. Remember to include sqljdbc.jar in the classpath for compilation and execution:

C:\>javac -cp .;\local\lib\sqljdbc.jar SqlServerDataSource.java

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerDataSourceServer name: Microsoft SQL ServerServer version: 9.00.1399

Installing Microsoft JDBC Driver for SQL ServerIf you want to access SQL Server database from Java applications, you need to install a JDBC driver that supports SQL Server. There are many software vendors offer such JDBC drivers.

Microsoft offers a free driver, JDBC Driver 1.0, which is JDBC 3.0 compliant. JDBC Driver 1.0 is a Type 4 JDBC driver using native-protocols.

The tutorial below shows you how to download and install the JDBC Driver 1.0 from Microsoft.

1. Go to Microsoft JDBC page.

2. Click the Download button. The license terms page shows up.

3. Click the link "I Accept and I want to download the Microsoft Windows version" near the end of the page. The download window shows up.

4. Save the downloaded file, sqljdbc_1.0.809.102_enu.exe, to C:\temp directory.

5. Double click on sqljdbc_1.0.809.102_enu.exe. It will be unzipped to a local directory called: "Microsoft SQL Server 2005 JDBC Driver". The JDBC driver .jar file, sqljdbc.jar, is located under the "sqljdbc_1.0\enu" subdirectory.

6. Copy sqljdbc.jar to C:\local\lib\sqljdbc.jar. You are ready to use JDBC Driver 1.0 now.

Loading Driver Class with Class.forName()The first step of using JDBC to connect to SQL Server is to load the JDBC driver class. To do this you need to use the Class.forName() method to load the driver class, com.microsoft.sqlserver.jdbc.SQLServerDriver, which represents the Microsoft JDBC Driver.

Here is my sample program to load Microsoft JDBC Driver 1.0 class:

/** * ConnectionTest1.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ConnectionTest1 { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); System.out.println("JDBC driver loaded ok."); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } }}

Compile and run this program:

C:\>\progra~1\java\jdk1.6.0_02\bin\javac ConnectionTest1.java

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp . ConnectionTest1

ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver

I was getting this exception, because I forgot to add the JDBC driver jar file to the classpath in the "-cp" option. Here is the correct way to run this Java program with the JDBC driver jar file:

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar ConnectionTest1

JDBC driver loaded ok.

DriverManager.getConnection() and Connection URLOnce the JDBC driver class is loaded, you are ready to connect to a SQL Server by using the DriverManager.getConnection(connection_url) method. The connection URL, connection_url, is a string with the following syntax:

jdbc:sqlserver://server_name;user=login;password=****

The tutorial program below shows you a good example of using getConnection() and connection URL:

/** * ConnectionTest2.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ConnectionTest2 { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost;" + "user=sa;password=HerongYang");

} catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you may get a surprising error like this:

C:\>\progra~1\java\jdk1.6.0_02\bin\javac ConnectionTest2.java

C:\>\progra~1\java\jdk1.6.0_02\bin\java -cp .;\local\lib\sqljdbc.jar ConnectionTest2

SQLException: The TCP/IP connection to the host has failed. java.net.ConnectException: Connection refused: connect

The error is probably caused by the port number where the SQL Server is listening for the client connection. If port number is different than the default number, you must specify the port number in the connection url as shown in the next tutorial.

Microsoft JDBC Driver - Query Statements and Result SetsThis chapter provides tutorial notes on running query statements and working with result sets with Microsoft JDBC Driver. Topics include creating statement objects; executing SQL query statements; retrieving ResultSet objects; looping through result set; obtaining field values from the result sets.

Commonly Used JDBC Class Methods

Calling createStatement() and executeQuery

Receiving ResultSet Objects from executeQuery

Closing ResultSet Objects - res.close()

Looping through ResultSet with res.next()

Retrieving Field Values using res.get*() Methods

Using ResultSetMetaData Objects to List All Fields

Commonly Used JDBC Class MethodsMost of the time, the JDBC interface is used to run SELECT queries and parsing through the returning result sets. If you are look at Java documentation, you will find these JDBC classes methods that you need to use most of the time:

DriverManager.getConnection() - Establish and return a connection object. Connection.close() - Closes the connection to the database server. Connection.getMetaData() - Returns meta data from the database server to

provide information like table structures, stored procedures and connection settings.

Connection.createStatement() - Returns a statement object to execute SQL statements.

Statement.executeUpdate() - Execute a SQL statement (non-SELECT statement) that returns no result set.

Statement.executeQuery() - Execute a SQL SELECT statement and returns a result set object.

Statement.close() - Closes the resources used by this statement object. ResultSet.first() - Moves the cursor to the first row in this ResultSet object. ResultSet.last() - Moves the cursor to the last row in this ResultSet object. ResultSet.next() - Moves the cursor forward one row from its current position. ResultSet.previous() - Moves the cursor to the previous row in this ResultSet

object. ResultSet.getBoolean() - Returns the value as boolean of a given column. ResultSet.getByte() - Returns the value as byte of a given column. ResultSet.getDate() - Returns the value as Date of a given column. ResultSet.getDouble() - Returns the value as double of a given column.

ResultSet.getFloat() - Returns the value as float of a given column. ResultSet.getInt() - Returns the value as int of a given column. ResultSet.getLong() - Returns the value as long of a given column. ResultSet.getString() - Returns the value as String of a given column. ResultSet.wasNull() - Returns true if the column fetched by the last get*() method

has a null value. ResultSet.close() - Closes the resources used by this ResultSet object.

Calling createStatement() and executeQueryAfter a connection object is created correctly, you need to create a statement object from the connection object in order to execute a SQL statement on the SQL Server, see the following two method call examples:

Statement sta = con.createStatement();sta.executeQuery("SELECT ...");

Here is a sample program that executes a SELECT statement to get the current date from the SQL Server:

/** * ExecuteQuery.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ExecuteQuery { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Execute a query statement Statement sta = con.createStatement(); sta.executeQuery("SELECT GETDATE()"); sta.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }

}

The program runs correctly. But you will get no output, since the returning data from the query execution was not retrieved and printed to the screen. See the next tutorial to know how to do this.

Receiving ResultSet Objects from executeQueryWhen you execute a SQL SELECT statement with the executeQuery() method, you need receive the returning ResultSet object with a variable. This ResultSet object variable represents the data that are generated by the SELECT query statement.

There are two types of methods available on the ResultSet object:

Moving the internal pointer to set a specific row as the current row of the result set. For example, next() sets the next row as the current row.

Retrieving the value from a specific field of the current row. For example, getInt(1) retrieves the value from the first field as an integer. getint("Age") retrieves the value from a field named as "Age".

The tutorial below shows you how to receive the ResultSet object and the value from the first column of the first row:

/** * GetServerDate.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class GetServerDate { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Checking the database name Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT GETDATE()"); res.next(); Date today = res.getDate(1); System.out.println("Server date: "+today);

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: "

+e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get something like this:

Server date: 2007-07-01

Closing ResultSet Objects - res.close()For each ResultSet object you received from a SELECT statement execution, there is large amount of resource allocated to this object. When you no longer need any data from a ResultSet object, you should close it with the res.close() method.

This tutorial Java program shows you how to do this:

/** * CloseResultSet.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class CloseResultSet { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Run a SELECT statement Statement sta = con.createStatement(); ResultSet res = sta.executeQuery( "SELECT * FROM SalesLT.Customer"); res.next(); String firstName = res.getString("FirstName"); String lastName = res.getString("LastName"); System.out.println("First customer: "+firstName+" "+lastName);

// Closing the ResultSet object res.close(); if (res.isClosed()) System.out.println("ResultSet closed.");

con.close();

} catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get:

First customer: Orlando Gee

Exception in thread "main" java.lang.AbstractMethodError: com.microsoft.sqlserver.jdbc.SQLServerResultSet.isClosed()Z at CloseResultSet.main(CloseResultSet.java:32)

As you can see, the res.close() method worked ok. But the res.isClosed() method gave an error, because res.isClosed was introduced in Java SE 6.0, and is not by Microsoft JDBC Driver 1.0.

Looping through ResultSet with res.next()If the returning ResultSet Object of a query statement contains multiple rows, you can use res.next() method to loop through each row in the output.

The tutorial Java program below shows you how to loop through the ResultSet to list customer names in Customer table:

/** * LoopResultSet.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class LoopResultSet { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Looping through each row Statement sta = con.createStatement(); ResultSet res = sta.executeQuery( "SELECT TOP 10 * FROM SalesLT.Customer");

System.out.println("Customers"); while (res.next()) { String firstName = res.getString("FirstName"); String lastName = res.getString("LastName"); System.out.println(" "+firstName+" "+lastName); }

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get something like:

Customers Orlando Gee Keith Harris Donna Carreras Janet Gates Lucy Harrington Rosmarie Carroll Dominic Gash Kathleen Garza Katherine Harding Johnny Caprio

Retrieving Field Values using res.get*() MethodsOnce the result set is captured in an object, you can think of it as a "table" with rows and columns (fields). As shown in the previous tutorial, you can use res.next() to loop through each row. Then use res.get*() methods to retrieve field values of the current row by giving the field name or field position as shown below:

type value = res.getType(i); // retrieve by position // position value starts from 1

type value = res.getType(name); // retrieve by name

The tutorial Java program below shows you how to list tables in the current database. Multiple attributes of each table are retrieved and displayed:

/** * GetFieldValues.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class GetFieldValues { public static void main(String [] args) {

Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Getting field values Statement sta = con.createStatement(); ResultSet res = sta.executeQuery( "SELECT * FROM sys.objects" + " WHERE type_desc='USER_TABLE'"); System.out.println("User Tables:"); while (res.next()) { String name = res.getString("name"); int id = res.getInt(2); String type = res.getString(7); System.out.println(" "+name+", "+id+", "+type); }

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this script, you will get something like:

BuildVersion, 5575058, USER_TABLE Address, 37575172, USER_TABLE Customer, 85575343, USER_TABLE CustomerAddress, 149575571, USER_TABLE Product, 197575742, USER_TABLE ProductCategory, 309576141, USER_TABLE ProductDescription, 357576312, USER_TABLE ProductModel, 405576483, USER_TABLE ProductModelProductDescription, 453576654, USER_TABLE SalesOrderDetail, 501576825, USER_TABLE SalesOrderHeader, 613577224, USER_TABLE ErrorLog, 2073058421, USER_TABLE

Using ResultSetMetaData Objects to List All FieldsThe ResultSet object returned by a SELECT statement also contains column (field) names, lengths and types. You can use res.getMetaData() to retrieve them into a

ResultSetMetaData object, which offers the following methods to allow to get different information:

getColumnCount() - Returns the number of columns in this ResultSet object. getColumnName(int column) - Get the designated column's name. getColumnTypeName(int column) - Retrieves the designated column's database-

specific type name. getPrecision(int column) - Get the designated column's specified column size.

The tutorial program below shows a good example of how to use ResultSetMetaData:

/** * ListResultFields.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ListResultFields { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Getting result meta data Statement sta = con.createStatement(); ResultSet res = sta.executeQuery( "SELECT * FROM sys.objects" + " WHERE type_desc='USER_TABLE'"); ResultSetMetaData rmd = res.getMetaData(); System.out.println("Result set columns:"); for (int i=1; i<=rmd.getColumnCount(); i++) { String name = rmd.getColumnName(i); String type = rmd.getColumnTypeName(i); int size = rmd.getPrecision(i); System.out.println(" "+name+", "+type+", "+size); }

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); }

}}

Run this Java program, you will see a list columns (fields) of the system view, sys.objects:

Result set columns: name, nvarchar, 128 object_id, int, 10 principal_id, int, 10 schema_id, int, 10 parent_object_id, int, 10 type, char, 2 type_desc, nvarchar, 60 create_date, datetime, 23 modify_date, datetime, 23 is_ms_shipped, bit, 1 is_published, bit, 1 is_schema_published, bit, 1

Microsoft JDBC Driver - DatabaseMetaData ObjectThis chapter provides tutorial notes on using DatabaseMetaData object to obtain information on database objects. Topics include getting SQL server and JDBC driver information; listing all databases on the SQL Server; listing all tables, stored procedures and schemas in the current database; listing all columns in a table or a view.

Commonly Used DatabaseMetaData Methods

Getting Database Server and Driver Info

Listing All Databases - getCatalogs()

Listing All Schemas - getSchemas()

Listing All Tables - getTables()

Listing All Culumns - getColumns()

Listing All Stored Procedures - getProcedures()

Commonly Used DatabaseMetaData MethodsJDBC database meta data is a collection of information reported by the JDBC driver about the connected database. This collection of information can be obtained by con.getMetaData as a DatabaseMetaData object. Detailed information can be retrieved with various methods offered on the DatabaseMetaData object. Some of them are:

getDatabaseProductName() - Retrieves the name of this database product. getDatabaseProductVersion() - Retrieves the version number of this database

product.

getDriverName() - Retrieves the name of this JDBC driver. getDriverVersion() - Retrieves the version number of this JDBC driver as a

String. getJDBCMinorVersion() - Retrieves the minor JDBC version number for this

driver. getJDBCMajorVersion() - Retrieves the major JDBC version number for this

driver. getUserName() - Retrieves the user name as known to this database. getCatalogs() - Retrieves the catalog names available in this database. getSchemas(String catalog, String schemaPattern) - Retrieves the schema names

available in this database. getTables(String catalog, String schemaPattern, String tableNamePattern, String[]

types) - Retrieves a description of the tables available in the given catalog. getPrimaryKeys(String catalog, String schema, String table) - Retrieves a

description of the given table's primary key columns. getColumns(String catalog, String schemaPattern, String tableNamePattern,

String columnNamePattern) - Retrieves a description of table columns available in the specified catalog.

getProcedures(String catalog, String schemaPattern, String procedureNamePattern) - Retrieves a description of the stored procedures available in the given catalog.

getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) - Retrieves a description of the given catalog's stored procedure parameter and result columns.

getFunctions(String catalog, String schemaPattern, String functionNamePattern) - Retrieves a description of the system and user functions available in the given catalog.

getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) - Retrieves a description of the given catalog's system or user function parameters and return type.

Getting Database Server and Driver InfoIf you want to know the database server name and version, or the JDBC driver name and version, you can get them through the DatabaseMetaData object as shown in the following sample program:

/** * DatabaseInfo.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class DatabaseInfo { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang");

// Database and driver info DatabaseMetaData meta = con.getMetaData(); System.out.println("Server name: " + meta.getDatabaseProductName()); System.out.println("Server version: " + meta.getDatabaseProductVersion()); System.out.println("Driver name: " + meta.getDriverName()); System.out.println("Driver version: " + meta.getDriverVersion()); System.out.println("JDBC major version: " + meta.getJDBCMajorVersion()); System.out.println("JDBC minor version: " + meta.getJDBCMinorVersion());

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program you will get:

Server name: Microsoft SQL ServerServer version: 9.00.1399Driver name: Microsoft SQL Server 2005 JDBC DriverDriver version: 1.0.809.102JDBC major version: 3JDBC minor version: 0

JDBC version information tells us that Microsoft JDBC Driver 1.0 supports only JDBC 3.0 API.

Listing All Databases - getCatalogs()If you want to list all databases on the SQL Server, you can use the DatabaseMetaData method: getCatalogs(). It returns all databases in a ResultSet object with one field, TABLE_CAT, containing the database name.

Note that a JDBC catalog is mapped to SQL Server database.

The tutorial Java program below shows you how to list all databases on the SQL Server:

/** * ListDatabases.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ListDatabases { public static void main(String [] args) { Connection con = null; try {

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang");

DatabaseMetaData meta = con.getMetaData(); ResultSet res = meta.getCatalogs(); System.out.println("List of databases: "); while (res.next()) { System.out.println(" " +res.getString("TABLE_CAT")); } res.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

Here is the output of the program:

List of databases: AdventureWorksLT master model msdb tempdb

Listing All Schemas - getSchemas()If you want list all schemas in the current database on the SQL server, you can use the DatabaseMetaData method: getSchemas(). It returns all schemas in the current database. The output is captured in a ResultSet object with two field, TABLE_SCHEM and TABLE_CATALOG.

The following sample program displays all schemas in the AdventureWorksLT database:

/**

* ListSchemas.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ListSchemas { public static void main(String [] args) { Connection con = null; try {

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

DatabaseMetaData meta = con.getMetaData(); ResultSet res = meta.getSchemas(); System.out.println("List of schemas: "); while (res.next()) { System.out.println( " "+res.getString("TABLE_SCHEM") + ", "+res.getString("TABLE_CATALOG")); } res.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get:

List of schemas: db_accessadmin, null db_backupoperator, null db_datareader, null db_datawriter, null db_ddladmin, null db_denydatareader, null db_denydatawriter, null db_owner, null db_securityadmin, null dbo, dbo guest, guest public, null

But why we are getting "null" as the catalog names, which suppose to be the database name: AdventureWorksLT. Something wrong with Microsoft JDBC Driver 1.0.

To see if the list is correct or not, I checked the system view sys.schemas directly:

use AdventureWorksLT;select * from sys.schemas;GOname ---------------------db_accessadmin db_backupoperator db_datareader db_datawriter db_ddladmin db_denydatareader db_denydatawriter db_owner db_securityadmin dbo guest INFORMATION_SCHEMA SalesLT sys

Surprisingly, the list from sys.schemas gives two more schema names: SalesLT and INFORMATION_SCHEMA. This makes me to think that the JDBC schema concept is not mapped to the SQL Server schema concept.

As a final note, Microsoft JDBC Driver 1.0 does not support getSchemas(String catalog, String schemaPattern), which was introduced in Java SE 6. If you try it, you will get this error:

Exception in thread "main" java.lang.AbstractMethodError: com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData.getSchemas(Ljava/lang/String;Ljava/lang/String;)Ljava/sql/ResultSet; at ListSchemas.main(ListSchemas.java:16)

Listing All Tables - getTables()If you want to list all tables in the current database on the SQL server, you can use the DatabaseMetaData method: getTables(). It returns all tables that meets the input criteria specified. Input parameters are:

catalog - a catalog name; must match the catalog name as it is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow the search.

schemaPattern - a schema name pattern; must match the schema name as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to narrow the search

tableNamePattern - a table name pattern; must match the table name as it is stored in the database

types - a list of table types, which must be from the list of table types returned from getTableTypes(); null returns all types

The output is captured in a ResultSet object with the following fields:

TABLE_CAT String => table catalog (may be null) TABLE_SCHEM String => table schema (may be null) TABLE_NAME String => table name TABLE_TYPE String => table type. Typical types are "TABLE", "VIEW",

"SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM".

REMARKS String => explanatory comment on the table TYPE_CAT String => the types catalog (may be null) TYPE_SCHEM String => the types schema (may be null) TYPE_NAME String => type name (may be null) SELF_REFERENCING_COL_NAME String => name of the designated

"identifier" column of a typed table (may be null) REF_GENERATION String => specifies how values in

SELF_REFERENCING_COL_NAME are created. Values are "SYSTEM", "USER", "DERIVED". (may be null)

The following sample program displays all tables in the AdventureWorksLT database:

/** * ListTables.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ListTables { public static void main(String [] args) { Connection con = null; try {

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

DatabaseMetaData meta = con.getMetaData(); ResultSet res = meta.getTables(null, null, null, new String[] {"TABLE"}); System.out.println("List of tables: "); while (res.next()) { System.out.println( " "+res.getString("TABLE_CAT") + ", "+res.getString("TABLE_SCHEM") + ", "+res.getString("TABLE_NAME") + ", "+res.getString("TABLE_TYPE") + ", "+res.getString("REMARKS")); } res.close();

con.close();

} catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get:

List of tables: AdventureWorksLT, dbo, BuildVersion, TABLE, null AdventureWorksLT, dbo, ErrorLog, TABLE, null AdventureWorksLT, SalesLT, Address, TABLE, null AdventureWorksLT, SalesLT, Customer, TABLE, null AdventureWorksLT, SalesLT, CustomerAddress, TABLE, null AdventureWorksLT, SalesLT, Product, TABLE, null AdventureWorksLT, SalesLT, ProductCategory, TABLE, null AdventureWorksLT, SalesLT, ProductDescription, TABLE, null AdventureWorksLT, SalesLT, ProductModel, TABLE, null AdventureWorksLT, SalesLT, ProductModelProductDescription, TABL... AdventureWorksLT, SalesLT, SalesOrderDetail, TABLE, null AdventureWorksLT, SalesLT, SalesOrderHeader, TABLE, null

Listing All Culumns - getColumns()If you want list all columns in a table, you can use the DatabaseMetaData method: getColumns(). Input parameters are:

catalog - a catalog name; must match the catalog name as it is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow the search

schemaPattern - a schema name pattern; must match the schema name as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to narrow the search

tableNamePattern - a table name pattern; must match the table name as it is stored in the database

columnNamePattern - a column name pattern; must match the column name as it is stored in the database

The output is captured in a ResultSet object with the following fields:

TABLE_CAT String => table catalog (may be null) TABLE_SCHEM String => table schema (may be null) TABLE_NAME String => table name COLUMN_NAME String => column name DATA_TYPE int => SQL type from java.sql.Types TYPE_NAME String => Data source dependent type name, for a UDT the type

name is fully qualified

COLUMN_SIZE int => column size. DECIMAL_DIGITS int => the number of fractional digits. Null is returned for

data types where DECIMAL_DIGITS is not applicable. NULLABLE int => is NULL allowed.

The following sample program displays all columns in the Customer table:

/** * ListColumns.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ListColumns { public static void main(String [] args) { Connection con = null; try {

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

DatabaseMetaData meta = con.getMetaData(); ResultSet res = meta.getColumns(null, null, "Customer", null); System.out.println("List of columns: "); while (res.next()) { System.out.println( " "+res.getString("TABLE_SCHEM") + ", "+res.getString("TABLE_NAME") + ", "+res.getString("COLUMN_NAME") + ", "+res.getString("TYPE_NAME") + ", "+res.getInt("COLUMN_SIZE") + ", "+res.getString("NULLABLE")); } res.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get:

List of columns: SalesLT, Customer, CustomerID, int identity, 10, 0 SalesLT, Customer, NameStyle, NameStyle, 1, 0

SalesLT, Customer, Title, nvarchar, 8, 1 SalesLT, Customer, FirstName, Name, 50, 0 SalesLT, Customer, MiddleName, Name, 50, 1 SalesLT, Customer, LastName, Name, 50, 0 SalesLT, Customer, Suffix, nvarchar, 10, 1 SalesLT, Customer, CompanyName, nvarchar, 128, 1 SalesLT, Customer, SalesPerson, nvarchar, 256, 1 SalesLT, Customer, EmailAddress, nvarchar, 50, 1 SalesLT, Customer, Phone, Phone, 25, 1 SalesLT, Customer, PasswordHash, varchar, 128, 0 SalesLT, Customer, PasswordSalt, varchar, 10, 0 SalesLT, Customer, rowguid, uniqueidentifier, 36, 0 SalesLT, Customer, ModifiedDate, datetime, 23, 0

Listing All Stored Procedures - getProcedures()If you want to list all stored procedures in the current database, you can use the DatabaseMetaData method: getProcedures(). Input parameters are:

catalog - a catalog name; must match the catalog name as it is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow the search

schemaPattern - a schema name pattern; must match the schema name as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to narrow the search

procedureNamePattern - a procedure name pattern; must match the procedure name as it is stored in the database

The output is captured in a ResultSet object with the following fields:

PROCEDURE_CAT String => table catalog (may be null) PROCEDURE_SCHEM String => table schema (may be null) PROCEDURE_NAME String => procedure name

The following sample program displays all columns in the fyi_links table:

/** * ListProcedures.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ListProcedures { public static void main(String [] args) { Connection con = null; try {

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

DatabaseMetaData meta = con.getMetaData(); ResultSet res = meta.getProcedures(null, null, null); System.out.println("List of procedures: "); while (res.next()) { System.out.println( " "+res.getString("PROCEDURE_CAT") + ", "+res.getString("PROCEDURE_SCHEM") + ", "+res.getString("PROCEDURE_NAME")); } res.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, you will get:

List of procedures: AdventureWorksLT, dbo, ufnGetAllCategories;0 AdventureWorksLT, dbo, ufnGetCustomerInformation;0 AdventureWorksLT, dbo, ufnGetSalesOrderStatusText;0 AdventureWorksLT, dbo, uspLogError;1 AdventureWorksLT, dbo, uspPrintError;1 AdventureWorksLT, sys, dm_db_index_operational_stats;0 AdventureWorksLT, sys, dm_db_index_physical_stats;0 AdventureWorksLT, sys, dm_db_missing_index_columns;0 AdventureWorksLT, sys, dm_exec_cached_plan_dependent_objects;0 ...

Microsoft JDBC Driver - DDL StatementsThis chapter provides tutorial notes on executing DDL statements with Microsoft JDBC Driver. Topics include creating schemas; creating, altering and dropping tables.

Executing "Update" Statements - executeUpdate()

"CREATE SCHEMA" Statements

"CREATE TABLE" Statements

"ALTER TABLE" Statements

"DROP TABLE" Statements

Executing "Update" Statements - executeUpdate()JDBC supports two basic types of statement executions:

Query Statement Execution - executeQuery() - Executes a SQL SELECT statement and returns a ResultSet object.

Update Statement Execution - executeUpdate() - Executes a SQL DDL or DML statement and returns 0 or the number of affected rows.

Note executing update statements requires higher security permissions. I am assuming that your login has been granted enough permissions to follow my tutorials.

DDL (Data Definition Language) statements are used to create, alter or drop database objects like tables, views, indexes, or stored procedures. Here is a sample Java program that uses an executeUpdate() method to create a simple table in the current database:

/** * ExecuteUpdate.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class ExecuteUpdate { public static void main(String [] args) { Connection con = null; try {

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Executing update statements Statement sta = con.createStatement(); int count = sta.executeUpdate( "CREATE TABLE HerongTest (Name VARCHAR(20), Age INT)"); System.out.println("Return value from executeUpdate(): "+count); System.out.println("Table created."); sta.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program, a new table will be created in the current database on the SQL Server, and you will get a output message like this:

Return value from executeUpdate(): 0Table created.

But if you run it again, you will get a SQLException because the same table can not be created twice:

SQLException: There is already an object named 'HerongTest' in the database.

"CREATE SCHEMA" StatementsA schema on SQL Server is a container of other database objects like tables, views or stored procedures. Creating schemas in the database can help to divide database objects into groups to make object management easier.

Here is a simple Java program that creates a new schema in the current database:

/** * CreateSchema.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class CreateSchema { public static void main(String [] args) { Connection con = null; try {

// Load Microsoft JDBC Driver 1.0 Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

// Obtaining a connection to SQL Server con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Creating a database schema Statement sta = con.createStatement(); int count = sta.executeUpdate("CREATE SCHEMA Herong"); System.out.println("Schema created."); sta.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program a new schema will be created with the following message.

Schema created.

"CREATE TABLE" Statements"CREATE TABLE" is probably the most commonly used DDL statement. Its syntax is relatively simple. All you need to do is to specify the table name with an optional schema name and a list of column definitions.

The sample Java program below shows you how to create a table called "Price" in "Herong" schema. I defined only three columns in this table: ProductID, Price, and Currency.

/** * CreateTable.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class CreateTable { public static void main(String [] args) { Connection con = null; try {

Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver");

con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Altering a database table with an extra column Statement sta = con.createStatement(); int count = sta.executeUpdate( "CREATE TABLE Herong.Price (ProductID INT, Price REAL," + " Currency CHAR(3))"); System.out.println("Table created."); sta.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

If you run this program a new table will be created with the following message.

Table created.

"ALTER TABLE" StatementsWhen you are adding new features to an existing database application, very often you need to add new columns to some existing tables. This can be done by using the "ALTER

TABLE" statement. Here is a sample Java program that adds a new column called "Discount" to the table "Price":

/** * AlterTable.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class AlterTable { public static void main(String [] args) { Connection con = null; try { Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Adding a new column to an existing table Statement sta = con.createStatement(); int count = sta.executeUpdate( "ALTER TABLE Herong.Price ADD Discount REAL"); System.out.println("A new column added."); sta.close();

DatabaseMetaData meta = con.getMetaData(); ResultSet res = meta.getColumns(null, null, "Price", null); System.out.println("List of columns: "); while (res.next()) { System.out.println( " "+res.getString("TABLE_SCHEM") + ", "+res.getString("TABLE_NAME") + ", "+res.getString("COLUMN_NAME") + ", "+res.getString("TYPE_NAME") + ", "+res.getString("NULLABLE")); } res.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

When you run this program, you should get this output:

A new column added.List of columns: Herong, Price, ProductID, int, 10, 1

Herong, Price, Price, real, 24, 1 Herong, Price, Currency, char, 3, 1 Herong, Price, Discount, real, 24, 1

"DROP TABLE" StatementsSometimes you need to drop a table from the database. Dropping a table means deleting all rows of data in the table and also deleting the table definition. The following Java sample program shows you how to drop the table named "Price":

/** * DropTable.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class DropTable { public static void main(String [] args) { Connection con = null; try { Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT");

// Adding a new column to an existing table Statement sta = con.createStatement(); sta.executeUpdate("DROP TABLE Herong.Price"); System.out.println("Table dropped.");

sta.executeQuery("SELECT * FROM Herong.Price"); sta.close();

con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

The output of this program has a SQLException showing us that the table is gone:

Table dropped.SQLException: Invalid object name 'Herong.Price'.

Microsoft JDBC Driver - DML StatementsThis chapter provides tutorial notes on executing DDL statements with Microsoft JDBC Driver. Topics include inserting, updating and deleting data rows in existing tables.

"SELECT ... INTO" Statements

"INSERT INTO" Statements

"INSERT INTO" Statements with INDENTITY Columns

"UPDATE" Statements

"DELETE FROM" Statements

"SELECT ... INTO" StatementsSQL Server supports a special SELECT statement with an INTO clause, referred as SELECT INTO statement sometimes. It combines SELECT, CREATE TABLE and INSERT operations in a single statement. This statement should be treated as an update statement from the JDBC point of view, since it will not return any result set.

In this section, I want to use this SELECT INTO statement to build a new table with data from the existing sample table Customer. The new table will be called, Profile, in my schema, Herong. Here is the Java program that execute this SELECT INTO statement:

/** * SelectInto.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SelectInto { public static void main(String [] args) { Connection con = null; try { Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT"); Statement sta = con.createStatement();

// creating new table and insert data in a single statement int count = sta.executeUpdate( "SELECT TOP 10 CustomerID UserID, FirstName, LastName," + " ModifiedDate LastAccessDate" + " INTO Herong.Profile FROM SalesLT.Customer"); System.out.println("Number of rows inserted: "+count);

// getting the data back ResultSet res = sta.executeQuery( "SELECT * FROM Herong.Profile"); System.out.println("List of Profiles: "); while (res.next()) { System.out.println( " "+res.getInt("UserID") + ", "+res.getString("FirstName")

+ ", "+res.getString("LastName") + ", "+res.getDate("LastAccessDate")); } res.close();

sta.close(); con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

The output confirms that the SELECT INTO statement worked correctly:

Number of rows inserted: 10List of Profiles: 1, Orlando, Gee, 2004-10-13 2, Keith, Harris, 2004-10-13 3, Donna, Carreras, 2004-10-13 4, Janet, Gates, 2004-10-13 5, Lucy, Harrington, 2004-10-13 6, Rosmarie, Carroll, 2004-10-13 7, Dominic, Gash, 2004-10-13 10, Kathleen, Garza, 2004-10-13 11, Katherine, Harding, 2004-10-13 12, Johnny, Caprio, 2004-10-13

Note that the executeUpdate() method did return the number of rows inserted into the new table correctly.

"INSERT INTO" StatementsINSERT statements are used very often by database applications to insert data rows into tables. The syntax of INSERT statements is very simple. You need to provide a list of column names and a list of values for those columns. There are a couple of simple rules about INSERT statements:

You don't have to provide values to columns that have default values defined. You don't have to provide values to columns that allow null values. You should not provide values to IDENTITY columns, mainly used as primary

key columns.

INSERT statements should be executed with the executeUpdate() method. Here is a simple program that insert a single row into my table Herong.Profile:

/** * InsertRows.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved.

*/import java.sql.*;public class InsertRows { public static void main(String [] args) { Connection con = null; try { Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT"); Statement sta = con.createStatement();

// insert a single row int count = sta.executeUpdate( "INSERT INTO Herong.Profile" + " (FirstName, LastName, LastAccessDate)" + " VALUES ('Herong', 'Yang', '2007-04-01')"); System.out.println("Number of rows inserted: "+count);

// getting the data back ResultSet res = sta.executeQuery( "SELECT * FROM Herong.Profile"); System.out.println("List of Profiles: "); while (res.next()) { System.out.println( " "+res.getInt("UserID") + ", "+res.getString("FirstName") + ", "+res.getString("LastName") + ", "+res.getDate("LastAccessDate")); } res.close();

sta.close(); con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

The output confirms that the insert statement was executed correctly:

Number of rows inserted: 1List of Profiles: 13, Herong, Yang, 2007-04-01 1, Orlando, Gee, 2004-10-13 2, Keith, Harris, 2004-10-13 3, Donna, Carreras, 2004-10-13 4, Janet, Gates, 2004-10-13 5, Lucy, Harrington, 2004-10-13

6, Rosmarie, Carroll, 2004-10-13 7, Dominic, Gash, 2004-10-13 10, Kathleen, Garza, 2004-10-13 11, Katherine, Harding, 2004-10-13 12, Johnny, Caprio, 2004-10-13

"INSERT INTO" Statements with INDENTITY ColumnsAn INDENTITY column is a special column in a table that defined to have it value automatically added with a sequence number generator. An INDENTITY column is normal used for the primary key column in a table. For example, the UserID column in the Profile table created in a previous tutorial is an INDENTITY column.

If you try to add values to an INDENTITY column in INSERT statements, you will get an error as shown in the sample program below:

/** * InsertIdentity.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class InsertIdentity { public static void main(String [] args) { Connection con = null; try { Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT"); Statement sta = con.createStatement();

// insert a single row int count = sta.executeUpdate( "INSERT INTO Herong.Profile" + " (UserID, FirstName, LastName, LastAccessDate)" + " VALUES (1001, 'Herong', 'Yang', '2007-04-01')"); System.out.println("Number of rows inserted: "+count);

sta.close(); con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

Here is the error message from this program:

SQLException: Cannot insert explicit value for identity column

in table 'Profile' when IDENTITY_INSERT is set to OFF.

"UPDATE" StatementsUPDATE statements are also commonly used by database applications when using users making changes to field values on the UI. An UPDATE statements should have a SET clause to provide a list of columns with new values and a WHERE clause to identify the rows to be updated.

UPDATE statements should be executed with the executeUpdate() method. The Java tutorial program below shows you how to update

/** * UpdateRows.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class UpdateRows { public static void main(String [] args) { Connection con = null; try { Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT"); Statement sta = con.createStatement();

// updating multiple rows int count = sta.executeUpdate( "UPDATE Herong.Profile SET LastAccessDate = '2007-04-01'" + " WHERE UserID > 6"); System.out.println("Number of rows updated: "+count);

// getting the data back ResultSet res = sta.executeQuery( "SELECT * FROM Herong.Profile"); System.out.println("List of Profiles: "); while (res.next()) { System.out.println( " "+res.getInt("UserID") + ", "+res.getString("FirstName") + ", "+res.getString("LastName") + ", "+res.getDate("LastAccessDate")); } res.close();

sta.close(); con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: "

+e.getMessage()); } }}

The output confirms that 5 rows were updated with a new date in the LastAccessDate column:

Number of rows updated: 5List of Profiles: 13, Herong, Yang, 2007-04-01 1, Orlando, Gee, 2004-10-13 2, Keith, Harris, 2004-10-13 3, Donna, Carreras, 2004-10-13 4, Janet, Gates, 2004-10-13 5, Lucy, Harrington, 2004-10-13 6, Rosmarie, Carroll, 2004-10-13 7, Dominic, Gash, 2007-04-01 10, Kathleen, Garza, 2007-04-01 11, Katherine, Harding, 2007-04-01 12, Johnny, Caprio, 2007-04-01

"DELETE FROM" StatementsDELETE statements are used less frequently in database applications. They are used only when you want to remove data rows from a table permanently. DELETE statements should include a WHERE clause to identify the data rows to be deleted.

UPDATE statements should be executed with the executeUpdate() method. The Java tutorial program below shows you how to update

/** * DeleteRows.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class DeleteRows { public static void main(String [] args) { Connection con = null; try { Class.forName( "com.microsoft.sqlserver.jdbc.SQLServerDriver"); con = DriverManager.getConnection( "jdbc:sqlserver://localhost:1269;" + "user=sa;password=HerongYang;" + "database=AdventureWorksLT"); Statement sta = con.createStatement();

// deleting multiple rows int count = sta.executeUpdate( "DELETE FROM Herong.Profile WHERE UserID in (1, 3, 5, 7)"); System.out.println("Number of rows deleted: "+count);

// getting the data back ResultSet res = sta.executeQuery(

"SELECT * FROM Herong.Profile"); System.out.println("List of Profiles: "); while (res.next()) { System.out.println( " "+res.getInt("UserID") + ", "+res.getString("FirstName") + ", "+res.getString("LastName") + ", "+res.getDate("LastAccessDate")); } res.close();

sta.close(); con.close(); } catch (java.lang.ClassNotFoundException e) { System.err.println("ClassNotFoundException: " +e.getMessage()); } catch (SQLException e) { System.err.println("SQLException: " +e.getMessage()); } }}

The output confirms that 4 rows were deleted:

Number of rows deleted: 4List of Profiles: 13, Herong, Yang, 2007-04-01 2, Keith, Harris, 2004-10-13 4, Janet, Gates, 2004-10-13 6, Rosmarie, Carroll, 2004-10-13 10, Kathleen, Garza, 2007-04-01 11, Katherine, Harding, 2007-04-01 12, Johnny, Caprio, 2007-04-01

SQL Server – PreparedStatementThis chapter provides tutorial notes on JDBC PreparedStatement with SQL Server Driver. Topics include creating PreparedStatement objects; setting PreparedStatement parameters; running PreparedStatement in batch mode; comparing PreparedStatement performance.

Create a New User in SQL Server

Creating a Table with an IDENTITY Column

Inserting Rows to the Test Table

PreparedStatement Overview

PreparedStatement with Parameters

PreparedStatement in Batch Mode

Performance of Inserting Rows with a PreparedStatement

Performance of Inserting Rows with a Regular Statement

Performance of Inserting Rows with a ResultSet

Create a New User in SQL ServerIn order to do some tests on PreparedStatement, I created a new login user, "Herong", and granted full permission to "Herong" to use database, AdventureWorksLT:

C:\>sqlcmd -S localhost -U sa -P HerongYang

1> -- Set AdventureWorksLT as the current database2> USE AdventureWorksLT;3> GOChanged database context to 'AdventureWorksLT'.

1> -- Create a new server login name: Herong2> CREATE LOGIN Herong WITH PASSWORD = 'TopSecret'3> GO

1> -- Create a new database user linked to the login name2> CREATE USER Herong FOR LOGIN Herong;3> GO

1> -- Grant database ALTER permision to the user2> GRANT ALTER To Herong;3> GO

1> -- Grant database CONTROL permision to the user2> GRANT CONTROL To Herong;3> GO

Here is what I did to test this new login name and user: Herong

C:\>sqlcmd -S localhost -U Herong -P TopSecret

1> -- Set AdventureWorksLT as the current database2> USE AdventureWorksLT;3> GOChanged database context to 'AdventureWorksLT'.

1> -- Create a new table2> CREATE TABLE Test (ID INT);3> GO

1> -- Drop the table2> DROP TABLE Test;3> GO

Tests looked good to me. New login user, Herong, has enough permission to use database AdventureWorksLT.

Creating a Table with an IDENTITY ColumnIDENTITY column is a nice feature provided by SQL Server that provides auto-incremented sequence values for the specified column in a table. Usually, a primary key column is defined as an IDENTITY column.

In order to try IDENTITY columns and provide a table for PreparedStatement tests, I wrote the following program to create a table called "Profile" with the primary key column defined as an IDENTITY column:

/** * SqlServerIdentityColumn.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;import javax.sql.*;public class SqlServerIdentityColumn { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Creating a database table Statement sta = con.createStatement(); int count = sta.executeUpdate( "CREATE TABLE Profile (" + " ID INTEGER PRIMARY KEY IDENTITY," + " FirstName VARCHAR(20) NOT NULL," + " LastName VARCHAR(20)," + " Point REAL DEFAULT 0.0," + " BirthDate DATETIME DEFAULT '1988-12-31'," + " ModTime DATETIME DEFAULT '2006-12-31 23:59:59.999')"); System.out.println("Table created."); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

The test program executed correctly:

C:\>javac -cp .;\local\lib\sqljdbc.jar SqlServerIdentityColumn.java

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerIdentityColumnTable created.

Inserting Rows to the Test TableINSERT statements are used very often by database applications to insert data rows into tables. The syntax of INSERT statements is very simple. You need to provide a list of column names and a list of values for those columns. There are a couple of simple rules about INSERT statements:

You don't have to provide values to columns that have default values defined. You don't have to provide values to columns that allow null values. You should not provide values to IDENTITY columns, mainly used as primary

key columns.

INSERT statements should be executed with the executeUpdate() method. Here is a simple program that inserts some rows into my table Profile:

/** * SqlServerMultipleInserts.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.util.*;import java.sql.*;public class SqlServerMultipleInserts { public static void main(String [] args) { Connection con = null; try {

// Setting up the DataSource object com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret");

// Getting a connection object and statement object con = ds.getConnection(); Statement sta = con.createStatement(); int count = 0;

// insert a single row using default values count += sta.executeUpdate( "INSERT INTO Profile" + " (FirstName)" + " VALUES ('Herong')");

// insert a single row using provided values count += sta.executeUpdate( "INSERT INTO Profile" + " (FirstName, LastName, Point, BirthDate)" + " VALUES ('Janet', 'Gates', 999.99, '1984-10-13')");

// insert rows with loop with random values Random r = new Random(); for (int i=0; i<10; i++) {

Float points = 1000*r.nextFloat(); String firstName = Integer.toHexString(r.nextInt(9999)); String lastName = Integer.toHexString(r.nextInt(999999)); count += sta.executeUpdate( "INSERT INTO Profile" + " (FirstName, LastName, Point)" + " VALUES ('"+firstName+"', '"+lastName+"', "+points+")"); }

// How many rows were inserted System.out.println("Number of rows inserted: "+count);

// Checking inserted rows ResultSet res = sta.executeQuery( "SELECT * FROM Profile"); System.out.println("List of Profiles: "); while (res.next()) { System.out.println( " "+res.getInt("ID") + ", "+res.getString("FirstName") + ", "+res.getString("LastName") + ", "+res.getDouble("Point") + ", "+res.getDate("BirthDate") + ", "+res.getTimestamp("ModTime")); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Notice that Random class was used to generate some random strings and numbers. The output confirms that insert statements was executed correctly:

C:\>javac -cp .;\local\lib\sqljdbc.jar SqlServerMultipleInserts.java

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerMultipleInsertsNumber of rows inserted: 12List of Profiles: 1, Herong, null, 0.0, 1988-12-31, 2007-01-01 00:00:00.0 2, Janet, Gates, 999.9890234375, 1984-10-13, 2007-01-01 00:00:00.0 3, 1262, 70469, 696.89356875, 1988-12-31, 2007-01-01 00:00:00.0 4, 135c, 3b291, 226.866060839844, 1988-12-31, 2007-01-01 00:00:00.0 5, 1cc, 3f8a9, 517.886236875, 1988-12-31, 2007-01-01 00:00:00.0 6, 248a, ade28, 634.84337890625, 1988-12-31, 2007-01-01 00:00:00.0 7, 26, 39a85, 200.7675785, 1988-12-31, 2007-01-01 00:00:00.0 8, 23fc, 63135, 332.93850800781, 1988-12-31, 2007-01-01 00:00:00.0 9, 18c5, b7e7e, 742.11163359375, 1988-12-31, 2007-01-01 00:00:00.0 10, 265, 39859, 179.678320898438, 1988-12-31, 2007-01-01 00:00:00.0 11, f8e, bcce7, 726.69514296875, 1988-12-31, 2007-01-01 00:00:00.0 12, 1785, 4be9, 749.823746875, 1988-12-31, 2007-01-01 00:00:00.0

PreparedStatement OverviewIf you have a SQL statement that needs to be executed multiple times, it is more efficient to use a JDBC PreparedStatement object to run it. JDBC PreparedStatement class supports the following main features:

SQL statements PreparedStatement objects are pre-compiled on the database server side.

IN parameters are supported in SQL statements in PreparedStatement objects. Batch execution mode is supported to run the run SQL statement multiple times in

a single transaction.

A PreparedStatement object should be created from a Connection object with the prepareStatement() method and executed like a regular Statement object as shown in the following program:

/** * SqlServerPreparedSelect.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerPreparedSelect { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// PreparedStatement for SELECT statement PreparedStatement sta = con.prepareStatement(

"SELECT * FROM Profile WHERE ID = 2");

// Execute the PreparedStatement as a query ResultSet res = sta.executeQuery();

// Get values out of the ResultSet res.next(); String firstName = res.getString("FirstName"); String lastName = res.getString("LastName"); System.out.println("User ID 2: "+firstName+' '+lastName);

// Close ResultSet and PreparedStatement res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage());

e.printStackTrace(); } }}

The output looks correct:

C:\>javac -cp .;\local\lib\sqljdbc.jar SqlServerPreparedSelect.java

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerPreparedSelectUser ID 2: Janet Gates

PreparedStatement with ParametersTo make a PreparedStatement object more flexible, you add parameters to the embedded SQL statement with question marks (?). Real values should be added before executing the PreparedStatement object.

Adding values to PreparedStatement parameters should be done by calling setXXX() methods in this format:

ps.setXXX(1, value);ps.setXXX(2, value);...ps.setXXX(n, value); // Sets value to the n-th parameter.

JDBC supports many setXXX() methods, one for each Java data type, so that you can set parameter values directly with the desired Java data types without any conversion. Here is a list of setXXX() methods:

setArray() - Sets the value of the specified parameter as Java Array type. setAsciiStream() - Sets the value of the specified parameter as a stream of ASCII

characters. setBigDecimal() - Sets the value of the specified parameter as

java.sql.BigDecimal type. setBinaryStream() - Sets the value of the specified parameter as a stream of bytes. setByte() - Sets the value of the specified parameter as Java byte type. setBlob() - Sets the value of the specified parameter as Java Blob type. setBoolean() - Sets the value of the specified parameter as Java boolean type. setBytes() - Sets the value of the specified parameter as Java byte[] type. setCharacterStream() - Sets the value of the specified parameter as java.io.Reader

type. setClob() - Sets the value of the specified parameter as Java Clob type. setDate() - Sets the value of the specified parameter as java.sql.Date type. setDouble() - Sets the value of the specified parameter as Java double type. setFloat() - Sets the value of the specified parameter as Java float type. setInt() - Sets the value of the specified parameter as Java int type. setLong() - Sets the value of the specified parameter as Java long type. setNull() - Sets the value of the specified parameter as a null value.

setObject() - Sets the value of the specified parameter as Java Object type. setRef() - Sets the value of the specified parameter as Java Ref type. setShort() - Sets the value of the specified parameter as Java short type. setString() - Sets the value of the specified parameter as Java String type. setTime() - Sets the value of the specified parameter as java.sql.Time type. setTimestamp() - Sets the value of the specified parameter as java.sql.Timestamp

type.

Here is a sample program that created a PreparedStatement object with one parameter:

/** * SqlServerPreparedStatementParameter.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerPreparedStatementParameter { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// PreparedStatement for SELECT statement with one parameter PreparedStatement sta = con.prepareStatement(

"SELECT * FROM Profile WHERE ID = ?");

// Provide a value to the parameter int id = 9; sta.setInt(1,id);

// Execute the PreparedStatement as a query ResultSet res = sta.executeQuery();

// Get values out of the ResultSet res.next(); String firstName = res.getString("FirstName"); String lastName = res.getString("LastName"); System.out.println("User ID "+id+": "+firstName+' '+lastName);

// Close ResultSet and PreparedStatement res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); }

}}

Output of the program confirms that the PreparedStatement object worked correctly:

C:\>javac -cp .;\local\lib\sqljdbc.jar SqlServerPreparedStatementParameter.java

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerPreparedStatementParameterUser ID 9: 18c5 b7e7e

PreparedStatement in Batch ModeIf you want to execute a PreparedStatement object multiple times in a single transaction, you can use the batch feature of the PreparedStatement object. Each time the addBatch() method is called, a copy of the embedded SQL statement will be created, but not executed until the execution method call. Here is sample sequence of method calls to execute a PreparedStatement object in batch mode:

ps.setXXX(...); // Set parameters for the first copy...ps.addBatch(); // Create the first copy of the SQL statementps.setXXX(...); // Set parameters for the second copy ...ps.addBatch(); // Create the second copy of the SQL statementps.setXXX(...); // Set parameters for the third copy...ps.addBatch(); // Create the third copy of the SQL statement

ps.executeBatch(); // Execute all copies together as a batch

Here is a sample program that creates a PrepareStatement object and executes it in batch mode to run an INSERT statement 4 times:

/** * SqlServerPreparedStatementBatch.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerPreparedStatementBatch { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// PreparedStatement

PreparedStatement ps = con.prepareStatement("INSERT INTO Profile (FirstName, LastName) VALUES (?, ?)");

// Provide values to parameters for copy 1 ps.setString(1,"John"); ps.setString(2,"First");

// Create copy 1 ps.addBatch();

// Provide values to parameters for copy 2 ps.setString(1,"Bill"); ps.setString(2,"Second");

// Create copy 2 ps.addBatch();

// Provide values to parameters for copy 3 ps.setString(1,"Mark"); ps.setString(2,"Third");

// Create copy 3 ps.addBatch();

// Provide values to parameters for copy 4 ps.setString(1,"Jack"); ps.setString(2,"Last");

// Create copy 4 ps.addBatch();

// Execute all 4 copies int[] counts = ps.executeBatch(); int count = 0; for (int i=0; i<counts.length; i++) { count += counts[i]; } System.out.println("Total effected rows: "+count);

// Close the PreparedStatement object ps.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output indicates the batch statement executed correctly:

C:\>javac -cp .;\local\lib\sqljdbc.jar SqlServerPreparedStatementBatch.java

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerPreparedStatementBatch

Total effected rows: 4

Performance of Inserting Rows with a PreparedStatementRunning SQL statements using PreparedStatement objects is supposed to be faster than using regular Statement objects. To test this, I wrote the following Java program to measure the performance of inserting rows using a PreparedStatement object into an empty table:

/** * SqlServerPerformancePreparedStatement.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerPerformancePreparedStatement { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Delete all rows from the table Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Profile");

// Start the test int count = 10000; long t1 = System.currentTimeMillis();

// PreparedStatement to insert rows PreparedStatement ps = con.prepareStatement(

"INSERT INTO Profile (FirstName, LastName) VALUES (?, ?)"); java.util.Random r = new java.util.Random(); for (int i = 0; i < count; i++) { ps.setString(1,Integer.toHexString(r.nextInt(9999))); ps.setString(2,Integer.toHexString(r.nextInt(999999))); ps.executeUpdate(); } ps.close();

// End the test long t2 = System.currentTimeMillis(); System.out.println("PreparedStatement insert "+count +" rows with "+(t2 -t1) +" milliseconds");

con.close(); } catch (Exception e) {

System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Here is the result on a Windows XP system with a 997MHz processor. You should compare this with the result the next tutorial.

PreparedStatement insert 10000 rows with 5625 milliseconds

Performance of Inserting Rows with a Regular StatementTo compare the performance result from the previous result with regular Statement objects. I wrote the following Java program to measure the performance of inserting rows using a regular Statement object into an empty table:

/** * SqlServerPerformanceRegularStatement.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerPerformanceRegularStatement { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Delete all rows from the table Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Profile");

// Start the test int count = 10000; long t1 = System.currentTimeMillis();

// Regular Statement to insert rows Statement rs = con.createStatement(); java.util.Random r = new java.util.Random(); for (int i = 0; i < count; i++) { rs.executeUpdate("INSERT INTO Profile (FirstName, LastName)" +" VALUES ('"+Integer.toHexString(r.nextInt(9999)) +"', '"+Integer.toHexString(r.nextInt(999999))+"')"); } rs.close();

// End the test long t2 = System.currentTimeMillis();

System.out.println("Regular Statement insert "+count +" rows with "+(t2 -t1) +" milliseconds");

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Here is the result on a Windows XP system with a 997MHz processor. You should compare this with the result from other performance tutorials.

Regular Statement insert 10000 rows with 6750 milliseconds

Comparing with PreparedStatement, regular Statement performs much slower than PreparedStatement, see the table below:

Statement # of inserts Time in ms. ComparisonPreparedStatement 10000 5625 100%Regular Statement 10000 6750 120%

Performance of Inserting Rows with a ResultSetSince ResultSet objects can also be used to insert rows, I wrote the following Java sample program to measure the performance of inserting multiple rows using a ResultSet object:

/** * SqlServerPerformanceResultSet.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerPerformanceResultSet { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Delete all rows from the table Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Profile");

// Start the test int count = 10000; long t1 = System.currentTimeMillis();

// ResultSet to insert rows

Statement rs = con.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);

ResultSet res = rs.executeQuery("SELECT * FROM Profile"); res.moveToInsertRow(); java.util.Random r = new java.util.Random(); for (int i = 0; i < count; i++) { res.updateString("FirstName", Integer.toHexString(r.nextInt(9999))); res.updateString("LastName", Integer.toHexString(r.nextInt(999999))); res.insertRow(); } rs.close();

// End the test long t2 = System.currentTimeMillis(); System.out.println("ResultSet insert "+count +" rows with "+(t2 -t1) +" milliseconds");

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Here is the result on a Windows XP system with a 997MHz processor. You should compare this with the result from other performance tutorials.

ResultSet insert 10000 rows with 6203 milliseconds

ResultSet is a little bit faster than regular statement, see the table below:

Statement # of inserts Time in ms. ComparisonPreparedStatement 10000 5625 100%Regular Statement 10000 6750 120%ResultSet 10000 6203 110%

SQL Server CLOB (Character Large Object) – TEXTThis chapter provides tutorial notes on CLOB (Character Large Object) data types, TEXT, with SQL Server JDBC Driver. Topics include creating tables to store CLOB values in SQL Server server; inserting CLOB values with direct SQL INSERT statements, or PreparedStatement with setString(), setCharacterStream() or setClob() methods; retrieving CLOB values from ResultSet with getString(), getCharacterStream() or getClob() method.

Overview of CLOB (Character Large Object)

Create Tables with CLOB Columns

Inserting CLOB Values with SQL INSERT Statements

Inserting CLOB Values with setString() Method

Inserting CLOB Values with setCharacterStream() Method

Closing InputStream Too Early on setCharacterStream()

Retrieving CLOB Values with getString() Method

Retrieving CLOB Values with getCharacterStream() Method

Retrieving CLOB Values with getClob() Method

Inserting CLOB Values with setClob() Method

Overview of CLOB (Character Large Object)CLOB (Character Large Object) is a large collection of character data stored in a database table. Different database servers handles CLOB differently. But there are some common characteristics like:

CLOB stores character data, which requires a specific encoding schema. For regular ASCII characters, the database default encoding schema is fine. So you you don't need to worry.

CLOB data is usually large. Many database servers offer very high maximum sizes like 4 GB.

Operations on CLOB data is usually restricted. For example, LIKE may not be used on CLOB data.

CLOB data is usually not directly stored inside the table. It is stored in different storage areas and its reference address is stored inside the table.

JDBC offers an interface, java.sql.Clob, to allow JDBC driver to provide generic functionalities to handle CLOB data in the database.

CLOB data can also be handled as character strings with the java.lang.String class.

CLOB data can also be handled as character streams with the java.io.Reader class.

Create Tables with CLOB ColumnsSQL Server support CLOB with the regular VARCHAR data types, but with a special length value called MAX:

VARCHAR(MAX) - A CLOB column with a maximum length of (2**31 - 1) bytes (not characters), about 2GB.

In order to test CLOB columns in SQL Server server, I used the SQL Server commnand line interface to create a test table with one CLOB column:

C:\>sqlcmd -S localhost -U Herong -P TopSecret

1> -- Set AdventureWorksLT as the current database2> USE AdventureWorksLT;3> GO

1> -- Create the test table with a CLOB column2> CREATE TABLE Article (ID INTEGER PRIMARY KEY IDENTITY,3> Subject VARCHAR(256) NOT NULL, 4> Body VARCHAR(MAX));5> GO

Inserting CLOB Values with SQL INSERT StatementsThe simplest way to insert a character string into a CLOB column is to use a SQL INSERT statement and include the character string a SQL string literal in the statement as shown in this sample program:

/** * SqlServerClobInsert.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerClobInsert { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test on INSERT statement"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Article WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a regular insert statement sta = con.createStatement(); int count = sta.executeUpdate( "INSERT INTO Article" +" (Subject, Body)" +" VALUES ('"+subject+"', 'A BLOB (Binary Large OBject) is" +" a large chunk of data which is stored in a database.')");

// Retrieving CLOB value with getString() ResultSet res = sta.executeQuery( "SELECT * FROM Article WHERE Subject = '"+subject+"'");

res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = "+res.getString("Body")); res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the character string value was correctly inserted into the CLOB column:

C:\>javac -cp .;\local\lib\sqljdbc.jar SQL SqlServerClobInsert.java

C:\>java -cp .;\local\lib\sqljdbc.jar SQL SqlServerClobInsert

The inserted record: Subject = Test on INSERT statement Body = A BLOB (Binary Large OBject) is a large chunk of data which is stored in a database.

Using SQL string literals to insert CLOB values into database is not recommended, because quote characters (') in the CLOB values must be replaced with escape sequences ('').

Using PreparedStatement with setXXX() method is a much safer choice.

Inserting CLOB Values with setString() MethodAnother way to insert a character string into a CLOB column is to create a PreparedStatement object and use the setString() method. See the sample program below:

/** * SqlServerClobSetString.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerClobSetString { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of the setString() method"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Article WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a PreparedStatement PreparedStatement ps = con.prepareStatement( "INSERT INTO Article (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); ps.setString(2, "He is wonderful and strange and who knows" +" how old he is, he thought. Never have I had such" +" a strong fish nor one who acted so strangely..." +" He cannot know that it is only one man against him," +" nor that it is an old man. But what a great fish" +" he is and what will he bring in the market" +" if the flesh is good."); int count = ps.executeUpdate(); ps.close();

// Retrieving CLOB value with getString() ResultSet res = sta.executeQuery( "SELECT * FROM Article WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = "+res.getString("Body")); res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the character string value was correctly inserted into the CLOB column:

C:\>javac -cp .;\local\lib\sqljdbc.jar SQL SqlServerClobSetString.java

C:\>java -cp .;\local\lib\sqljdbc.jar SQL SqlServerClobSetString

The inserted record: Subject = Test of the setString() method Body = He is wonderful and strange and who knows how old he is,he thought. Never have I had such a strong fish nor one who actedso strangely... He cannot know that it is only one man against him,nor that it is an old man. But what a great fish he is and what will he bring in the market if the flesh is good.

Inserting CLOB Values with setCharacterStream() MethodIf you want to insert the entire content of a text file into a CLOB column, you should

create a Reader object from this file, and use the PreparedStatement.setCharacterStream() method to pass the text file content to the CLOB column through the Reader object. There are 3 versions of the setCharacterStream() method, two of them were added as part of JDBC 4.0 (Java 1.6). Your JDBC driver may not support them:

setCharacterStream(int parameterIndex, Reader reader) - The data will be read from the Reader stream as needed until end-of-file is reached. This was added in JDBC 4.0 (Java 1.6).

setCharacterStream(int parameterIndex, Reader reader, int length) - The data will be read from the Reader stream as needed for "length" characters.

setCharacterStream(int parameterIndex, Reader reader, long length) - The data will be read from the Reader stream as needed for "length" characters. This was added in JDBC 4.0 (Java 1.6).

To test those setCharacterStream() methods, wrote the following program:

/** * SqlServerClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerClobSetCharacterStream { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of setCharacterStream() methods"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Article WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a PreparedStatement PreparedStatement ps = con.prepareStatement( "INSERT INTO Article (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); Reader bodyIn = new FileReader("SqlServerClobSetCharacterStream.java");

// Test 1 - This will not work with JDBC 3.0 drivers// ps.setCharacterStream(2, bodyIn);

// Test 2 - This will not work with JDBC 3.0 drivers// File fileIn = new File("SqlServerClobSetCharacterStream.java");

// ps.setCharacterStream(2, bodyIn, fileIn.length());

// Test 3 - This works with JDBC 3.0 drivers File fileIn = new File("SqlServerClobSetCharacterStream.java"); ps.setCharacterStream(2, bodyIn, (int) fileIn.length());

int count = ps.executeUpdate(); bodyIn.close(); ps.close();

// Retrieving CLOB value with getString() sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article" +" WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +res.getString("Body").substring(0,256)); res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

As I mentioned above setCharacterStream(int parameterIndex, Reader reader) will not work with JDBC 3.0 drivers. Here is what I got with the "Test 1" section open in my program. Remember that SQL Server JDBC driver 1.0 is a JDBC 3.0 driver.

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobSetCharacterStream

Exception in thread "main" java.lang.AbstractMethodError: com .microsoft.sqlserver.jdbc.SQLServerPreparedStatement .setCharacterStream(ILjava/io/Reader;)V at SqlServerClobSetCharacterStream.main(SqlServerClobSet...java:34)

"Test 2" also failed for the same reason - fileIn.length() returns "long" and that setCharacterStream(int, Reader, long) is JDBC 4.0 method:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobSetCharacterStream

Exception in thread "main" java.lang.AbstractMethodError: com .microsoft.sqlserver.jdbc.SQLServerPreparedStatement .setCharacterStream(ILjava/io/Reader;J)V at SqlServerClobSetCharacterStream.main(SqlServerClobSet...java:38)

Of course, "Test 3" worked nicely.

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobSetCharacterStream

The inserted record: Subject = Test of setCharacterStream() methods Body = /** * SqlServerClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerClobSetCharacterStream { public static void main(String [] args) { Connection co

Closing InputStream Too Early on setCharacterStream()The program in the previous tutorial worked nicely. But if you make a mistake by placing the bodyIn.close() statement before ps.executeUpdate(), you will get an IOException when ps.executeUpdate() is called. The reason is simple, reading of data from the Reader is done at the time of executeUpdate() call, not before. Here is a test program:

/** * SqlServerClobSetCharacterStreamError.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerClobSetCharacterStreamError { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of setCharacterStream() methods"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Article WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a PreparedStatement PreparedStatement ps = con.prepareStatement( "INSERT INTO Article (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); Reader bodyIn = new FileReader("SqlServerClobSetCharacterStream.java"); File fileIn = new File("SqlServerClobSetCharacterStream.java"); ps.setCharacterStream(2, bodyIn, (int) fileIn.length());

// Error - Closing the Reader too early. bodyIn.close();

int count = ps.executeUpdate(); ps.close();

// Retrieving CLOB value with getString() sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article" +" WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +res.getString("Body").substring(0,256)); res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The IOException is shown below. It doesn't tell you that the Reader is closed.

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobSetCharacterStreamError

Exception: Unexpected IOException processing character stream Reader.com.microsoft.sqlserver.jdbc.SQLServerException: Unexpected IOException processing character stream Reader. at com.microsoft.sqlserver.jdbc.SQLServerException .makeFromDriverError(...) at com.microsoft.sqlserver.jdbc.IOBuffer .bufferAppendRPCReader(...) at com.microsoft.sqlserver.jdbc.DTV$SendByRPCOp.execute(...) at com.microsoft.sqlserver.jdbc.DTV.executeOp(Unknown Source) at com.microsoft.sqlserver.jdbc.DTV.sendByRPC(Unknown Source) at com.microsoft.sqlserver.jdbc.Parameter.sendByRPC(...) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement .sendParamsByRPC(...) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement .doPrepExec(...) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement .executeUpdate(...) at SqlServerClobSetCharacterStreamError.main (SqlServerClobSetCharacterStreamError.java:38)

Retrieving CLOB Values with getString() MethodThe simplest way to retrieve the character string value from a CLOB column is to use the getString() method on the ResultSet object. Here is short example program:

/** * SqlServerClobGetString.java

* Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerClobGetString { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving CLOB value with getString() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); String body = res.getString("Body"); if (body.length() > 100) body = body.substring(0,100); System.out.println(" Body = "+body+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output of the program confirms that CLOB values can be retrieved with getString() method on the ResultSet object:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobGetString

Record ID: 1 Subject = Test on INSERT statement Body = A BLOB (Binary Large OBject) is a large chunk of data which is stored in a database....

Record ID: 2 Subject = Test of the setString() method Body = He is wonderful and strange and who knows how old he is, he thought. Never have I had such a strong...

Record ID: 7 Subject = Test of setCharacterStream() methods

Body = /** * SqlServerClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights res...

Retrieving CLOB Values with getCharacterStream() MethodCLOB values can also be retrieved with the getCharacterStream() method on the ResultSet object, which will return a Reader object. Then you can read the CLOB values from the Reader object with the read() method. The sample program below shows you how to create Reader objects with the getCharacterStream() method. A utility method is included to read all characters from a Ready object and save them in a file.

/** * SqlServerClobGetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerClobGetCharacterStream { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving CLOB value with getCharacterStream() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); Reader bodyOut = res.getCharacterStream("Body"); String fileOut = "ClobOut_"+res.getInt("ID")+".txt"; saveReader(fileOut,bodyOut); bodyOut.close(); System.out.println(" Body = (Saved in "+fileOut+")"); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static void saveReader(String name, Reader body) { int c;

try { Writer f = new FileWriter(name); while ((c=body.read())>-1) { f.write(c); } f.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output looked correct:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobGetCharacterStream Record ID: 1 Subject = Test on INSERT statement Body = (Saved in ClobOut_1.txt)

Record ID: 2 Subject = Test of the setString() method Body = (Saved in ClobOut_2.txt)

Record ID: 7 Subject = Test of setCharacterStream() methods Body = (Saved in ClobOut_7.txt)

I checked file ClobOut_7.txt. The CLOB value, the SqlServerClobGetCharacterStream.java, program was corrected saved.

Retrieving CLOB Values with getClob() MethodIf you like to work with java.sql.Clob objects, you can retrieve CLOB values with the getClob() method on ResultSet objects. The Clob object offers some interesting methods:

length() - Returns the number of characters in the Clob object. The return value has a type of "long". You may need to convert it to "int" to be used in other motheds.

getSubString(long pos, int length) - Returns a substring of characters from the Clob object with a specified starting position and length. Note the start position is "long" value, but the length is "int" value.

getCharacterStream() - Returns a Reader object from the Clob object so that you can read the content as a stream.

free() - Releases the resources that the Clob object holds. This was added in JDBC 4.0 (Java 1.6).

Here is my test program on getClob() method:

/** * SqlServerClobGetClob.java

* Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerClobGetClob { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving CLOB value with getClob() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Article"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); Clob bodyOut = res.getClob("Body"); int length = (int) bodyOut.length(); System.out.println(" Body Size = "+length); String body = bodyOut.getSubString(1, length); if (body.length() > 100) body = body.substring(0,100); System.out.println(" Body = "+body+"...");// bodyOut.free(); // new in JDBC 4.0 } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The output confirms that the getClob() method and Clob objects are not hard to use:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobGetClob

Record ID: 1 Subject = Test on INSERT statement Body Size = 84 Body = A BLOB (Binary Large OBject) is a large chunk of data which is stored in a database....

Record ID: 2 Subject = Test of the setString() method

Body Size = 304 Body = He is wonderful and strange and who knows how old he is, he thought. Never have I had such a strong...

Record ID: 7 Subject = Test of setCharacterStream() methods Body Size = 2252 Body = /** * SqlServerClobSetCharacterStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights res...

Inserting CLOB Values with setClob() MethodIf you want to insert a CLOB column with a character string that comes from a java.sql.Clob object, you can directly set the value with PreparedStatement.setClob() method.

To test this, I wrote the following program to copy some records with CLOB values as new records back into the same table. During the copy process, the CLOB values are also modified with some Clob object methods - The first 32 characters are converted to upper case characters.

/** * SqlServerClobSetClob.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerClobSetClob { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting records for re-testing Statement sta = con.createStatement(); sta.executeUpdate( "DELETE FROM Article WHERE Subject LIKE 'Copy of %'");

// Creating a PreparedStatement for inserting new records PreparedStatement ps = con.prepareStatement( "INSERT INTO Article (Subject, Body) VALUES (?,?)");

// Looping though the first 3 records ResultSet res = sta.executeQuery( "SELECT * FROM Article ORDER BY ID"); int i = 0; while (res.next() && i<3) {

i++; System.out.println("Copying record ID: "+res.getInt("ID")); String subject = res.getString("Subject"); Clob body = res.getClob("Body");

// Modifying the Clob object String chuck = body.getSubString(1,32); chuck = chuck.toUpperCase(); body.setString(1,chuck);

// Inserting a new record with setClob() ps.setString(1, "Copy of "+subject); ps.setClob(2,body); ps.executeUpdate(); } ps.close(); res.close(); // Checking the new records res = sta.executeQuery( "SELECT * FROM Article WHERE Subject LIKE 'Copy of %'"); while (res.next()) { System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); String body = res.getString("Body"); if (body.length() > 100) body = body.substring(0,100); System.out.println(" Body = "+body+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The program performed exactly as I expected:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerClobSetClob

Copying record ID: 1Copying record ID: 2Copying record ID: 7

Record ID: 8 Subject = Copy of Test on INSERT statement Body = A BLOB (BINARY LARGE OBJECT) IS a large chunk of data which is stored in a database....

Record ID: 9 Subject = Copy of Test of the setString() method Body = HE IS WONDERFUL AND STRANGE AND who knows how old he is,he thought. Never have I had such a strong ...

Record ID: 10 Subject = Copy of Test of setCharacterStream() methods Body = /** * SQLSERVERCLOBSETCHARACTERStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights res...

SQL Server BLOB (Binary Large Object) – BLOBThis chapter provides tutorial notes on BLOB (Binary Large Object) data types, BLOB, with MySQL JDBC Driver. Topics include creating tables to store BLOB values in MySQL server; inserting BLOB values with direct SQL INSERT statements, or PreparedStatement with setBytes(), setBinaryStream() or setBlob() methods; retrieving BLOB values from ResultSet with getBytes(), getBinaryStream() or getBlob() method.

Overview of BLOB (Binary Large Object)

Create Tables with CLOB Columns

Inserting BLOB Values with SQL INSERT Statements

Inserting BLOB Values with setBytes() Method

Inserting BLOB Values with setBinaryStream() Method

Closing InputStream Too Early on setBinaryStream()

Retrieving BLOB Values with getBytes() Method

Retrieving BLOB Values with getBinaryStream() Method

Retrieving BLOB Values with getBlob() Method

Inserting BLOB Values with setBlob() Method

Overview of BLOB (Binary Large Object)BLOB (Binary Large Object) is a large collection of binary data stored in a database table. Different database servers handles BLOB differently. But there are some common characteristics like:

BLOB stores binary data, which requires no encoding schema. BLOB data is stored in units of bytes.

BLOB data is usually large. Many database servers offer very high maximum sizes like 4 GB.

BLOB data is usually not directly stored inside the table. It is stored in different storage areas and its reference address is stored inside the table.

JDBC offers an interface, java.sql.Blob, to allow JDBC driver to provide generic functionalities to handle BLOB data in the database.

BLOB data can also be handled as byte arrays: byte[].

BLOB data can also be handled as binary streams with the java.io.InputStream class.

The data type and definition was introduced to describe data not originally defined in traditional computer database systemsCreate Tables with CLOB ColumnsSQL Server support BLOB with the regular VARBINARY data types, but with a special length value called MAX:

VARBINARY(MAX) - A BLOB column with a maximum length of (2**31 - 1) bytes, about 2GB.

In order to test BLOB columns in SQL Server server, I used the SQL Server commnand line interface to create a test table with one BLOB column:

C:\>sqlcmd -S localhost -U Herong -P TopSecret

1> -- Set AdventureWorksLT as the current database2> USE AdventureWorksLT;3> GO

1> -- Create the test table with a BLOB column2> CREATE TABLE Image (ID INTEGER PRIMARY KEY IDENTITY,3> Subject VARCHAR(256) NOT NULL,4> Body VARBINARY(MAX));5> GO

Inserting BLOB Values with SQL INSERT StatementsThe simplest way to insert a binary string into a BLOB column is to use a SQL INSERT statement and include the binary string a SQL binary literal in the statement as shown in this sample program. Note that SQL binary literal format is 0x<hex_numbers>.

/** * SqlServerBlobInsert.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerBlobInsert { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong");

ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test on INSERT statement"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a regular insert statement sta = con.createStatement(); int count = sta.executeUpdate( "INSERT INTO Image" +" (Subject, Body)" +" VALUES ('"+subject+"'" +", 0xC9CBBBCCCEB9C8CABCCCCEB9C9CBBB)"); //SQL Server format// +", x'C9CBBBCCCEB9C8CABCCCCEB9C9CBBB')"); // MySQL format

// Retrieving BLOB value with getBytes() ResultSet res = sta.executeQuery( "SELECT * FROM Image WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"))); res.close(); sta.close();

con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the character string value was correctly inserted into the BLOB column:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobInsert

The inserted record: Subject = Test on INSERT statement Body = ╔╦╗╠╬╣╚╩╝╠╬╣╔╦╗

Using SQL binary literals to insert BLOB values into database is simple. But it requires you to convert your binary data into the SQL binary literal format: 0x<hex_numbers>, which could be a problem if you have a very long binary data to enter.

Notice that the binary literal format on SQL Server is different than MySQL. This is another reason that you should avoid using binary literals in SQL statements to make your Java program portable.

Using PreparedStatement with setXXX() method is a much better choice.

Inserting BLOB Values with setBytes() MethodAnother way to insert a binary string into a BLOB column is to create a PreparedStatement object and use the setBytes() method. See the sample program below:

/** * SqlServerBlobSetBytes.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.sql.*;public class SqlServerBlobSetBytes { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of the setBytes() method"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with PreparedStatement.setBytes() PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); byte[] bodyIn = {(byte)0xC9, (byte)0xCB, (byte)0xBB, (byte)0xCC, (byte)0xCE, (byte)0xB9, (byte)0xC8, (byte)0xCA, (byte)0xBC, (byte)0xCC, (byte)0xCE, (byte)0xB9, (byte)0xC9, (byte)0xCB, (byte)0xBB}; ps.setBytes(2, bodyIn); int count = ps.executeUpdate(); ps.close();

// Retrieving BLOB value with getBytes() ResultSet res = sta.executeQuery( "SELECT * FROM Image WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"))); res.close(); sta.close();

con.close();

} catch (Exception e) { System.err.println("Exception: "+e.getMessage()); } }}

Compilation and execution of this program is below. The output confirms that the binary string value, stored in a byte array, was correctly inserted into the CLOB column:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobSetBytes

The inserted record: Subject = Test on INSERT statement Body = ╔╦╗╠╬╣╚╩╝╠╬╣╔╦╗

Inserting BLOB Values with setBinaryStream() MethodIf you want to insert the entire content of a binary file into a BLOB column, you should create an InputStream object from this file, and use the PreparedStatement.setBinaryStream() method to pass the binary file content to the BLOB column through the InputStream object. There are 3 versions of the setBinaryStream() method, two of them were added as part of JDBC 4.0 (Java 1.6). Your JDBC driver may not support them:

setBinaryStream(int parameterIndex, InputStream x) - The data will be read from the InputStream as needed until end-of-file is reached. This was added in JDBC 4.0 (Java 1.6).

setBinaryStream(int parameterIndex, InputStream x, int length) - The data will be read from the InputStream as needed for "length" bytes.

setBinaryStream(int parameterIndex, InputStream x, long length) - The data will be read from the InputStream as needed for "length" bytes. This was added in JDBC 4.0 (Java 1.6).

To test those setBinaryStream() methods, wrote the following program:

/** * SqlServerBlobSetBinaryStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerBlobSetBinaryStream { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret");

con = ds.getConnection();

// Deleting the record for re-testing String subject = "Test of setBinaryStream() methods"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with a PreparedStatement PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); InputStream bodyIn = new FileInputStream("SqlServerBlobSetBinaryStream.class");

// Test 1 - This will not work with JDBC 3.0 drivers// ps.setBinaryStream(2, bodyIn);

// Test 2 - This will not work with JDBC 3.0 drivers// File fileIn = new File("SqlServerBlobSetBinaryStream.class");// ps.setBinaryStream(2, bodyIn, fileIn.length());

// Test 3 - This works with JDBC 3.0 drivers File fileIn = new File("SqlServerBlobSetBinaryStream.class"); ps.setBinaryStream(2, bodyIn, (int) fileIn.length());

int count = ps.executeUpdate(); bodyIn.close(); ps.close();

// Retrieving BLOB value with getBytes() sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image" +" WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"),0,32)); res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

As I mentioned above setBinaryStream(int parameterIndex, InputStream x) will not work with JDBC 3.0 drivers. Here is what I got with the "Test 1" section open in my program. Remember that SQL Server JDBC driver 1.0 is a JDBC 3.0 driver.

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobSetBinaryStream

Exception in thread "main" java.lang.AbstractMethodError: com .microsoft.sqlserver.jdbc.SQLServerPreparedStatement .setBinaryStream(ILjava/io/InputStream;)V at SqlServerBlobSetBinaryStream.main(SqlServerBlobSetBi...java:34)

"Test 2" also failed for the same reason - fileIn.length() returns "long" and that setCharacterStream(int, InputStream, long) is JDBC 4.0 method:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobSetBinaryStream

Exception in thread "main" java.lang.AbstractMethodError: com .microsoft.sqlserver.jdbc.SQLServerPreparedStatement .setBinaryStream(ILjava/io/InputStream;J)V at SqlServerBlobSetBinaryStream.main(SqlServerBlobSetBi...java:38)

Of course, "Test 3" worked nicely.

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobSetBinaryStream

The inserted record: Subject = Test of setBinaryStream() methods Body = -||+ 2 | ; H I ? J ? K

Closing InputStream Too Early on setBinaryStream()The program in the previous tutorial worked nicely. But if you make a mistake by placing the bodyIn.close() statement before ps.executeUpdate(), you will get an IOException when ps.executeUpdate() is called. The reason is simple, reading of binary data from the InputStream is done at the time of executeUpdate() call. Here is a test program:

/** * SqlServerBlobSetBinaryStreamError.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerBlobSetBinaryStreamError { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting the record for re-testing

String subject = "Test of setBinaryStream() method error"; Statement sta = con.createStatement(); sta.executeUpdate("DELETE FROM Image WHERE Subject = '" +subject+"'");

// Inserting CLOB value with PreparedStatement.setBinaryStream() PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)"); ps.setString(1, subject); InputStream bodyIn = new FileInputStream("SqlServerBlobSetBinaryStream.class"); File fileIn = new File("SqlServerBlobSetBinaryStream.class"); ps.setBinaryStream(2, bodyIn, (int) fileIn.length());

// Error - Closing the InputStream too early. bodyIn.close();

int count = ps.executeUpdate(); ps.close();

// Retrieving BLOB value with getBytes() sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image" +" WHERE Subject = '"+subject+"'"); res.next(); System.out.println("The inserted record: "); System.out.println(" Subject = "+res.getString("Subject")); System.out.println(" Body = " +new String(res.getBytes("Body"),0,32)); res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

The IOException is:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobSetBinaryStreamError

Exception: java.io.IOException: Read errorcom.microsoft.sqlserver.jdbc.SQLServerException: java.io.IOException: Read error at com.microsoft.sqlserver.jdbc.SQLServerException .makeFromDriverError(...) at com.microsoft.sqlserver.jdbc.IOBuffer .bufferAppendRPCInputStream(...) at com.microsoft.sqlserver.jdbc.DTV$SendByRPCOp.execute(...) at com.microsoft.sqlserver.jdbc.DTV.executeOp(Unknown Source) at com.microsoft.sqlserver.jdbc.DTV.sendByRPC(Unknown Source) at com.microsoft.sqlserver.jdbc.Parameter.sendByRPC(...)

at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement .sendParamsByRPC(...) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement .doPrepExec(...) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement .executeUpdate(...) at SqlServerBlobSetBinaryStreamError.main(SqlServerBlob...java:40)

Retrieving BLOB Values with getBytes() MethodThe simplest way to retrieve the character string value from a BLOB column is to use the getBytes() method on the ResultSet object. Here is short example program:

/** * SqlServerBlobGetBytes.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerBlobGetBytes { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving BLOB value with getBytes() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); byte[] body = res.getBytes("Body"); String bodyHex = bytesToHex(body, 32); System.out.println(" Body in HEX = "+bodyHex+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static String bytesToHex(byte[] bytes, int max) { StringBuffer buffer = new StringBuffer(); for (int i=0; i<bytes.length && i<max; i++) {

buffer.append(Integer.toHexString(bytes[i] & 0xFF)); } return buffer.toString().toUpperCase(); }}

bytesToHex() method is used to convert a byte array to a Hex string. The output of the program confirms that CLOB values can be retrieved with getBytes() method on the ResultSet object:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobGetBytes

Record ID: 1 Subject = Test on INSERT statement Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 2 Subject = Test of the setBytes() method Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 4 Subject = Test of setBinaryStream() methods Body in HEX = CAFEBABE000320B5A03B0487049A02048804AA0204BA...

Retrieving BLOB Values with getBinaryStream() MethodBLOB values can also be retrieved with the getBinaryStream() method on the ResultSet object, which will return an OutputStream object. Then you can read the BLOB values from the OutputStream object with the read() method. The sample program below shows you how to create OutputStream objects with the getBinaryStream() method. A utility method is included to read all bytes from an OutputStream object and save them in a file.

/** * SqlServerBlobGetBinaryStream.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerBlobGetBinaryStream { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving CLOB value with getBinaryStream() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image"); int i = 0; while (res.next() && i<1) { i++;

System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); InputStream bodyOut = res.getBinaryStream("Body"); String fileOut = "BlobOut_"+res.getInt("ID")+".bin"; saveOutputStream(fileOut,bodyOut); bodyOut.close(); System.out.println(" Body = (Saved in "+fileOut+")"); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static void saveOutputStream(String name, InputStream body) { int c; try { OutputStream f = new FileOutputStream(name); while ((c=body.read())>-1) { f.write(c); } f.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } }}

Surprisingly, I got two exceptions on the InputStream object. The message says that the stream was closed by result set access. I don't know what was wrong.

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobGetBinaryStream

Record ID: 1 Subject = Test on INSERT statement

Exception: The stream was closed by result set access.java.io.IOException: The stream was closed by result set access. at com.microsoft.sqlserver.jdbc.PLPInputStream.checkClosed(...) at com.microsoft.sqlserver.jdbc.PLPInputStream.read(...) at SqlServerBlobGetBinaryStream.saveOutputStream( SqlServerBlobGetBinaryStream.java:47) at SqlServerBlobGetBinaryStream.main( SqlServerBlobGetBinaryStream.java:30)

Exception: The stream was closed by result set access.java.io.IOException: The stream was closed by result set access. at com.microsoft.sqlserver.jdbc.PLPInputStream.checkClosed(...) at com.microsoft.sqlserver.jdbc.PLPInputStream.close(...) at SqlServerBlobGetBinaryStream.main( SqlServerBlobGetBinaryStream.java:31)

Retrieving BLOB Values with getBlob() MethodIf you like to work with java.sql.Blob objects, you can retrieve BLOB values with the getBlob() method on ResultSet objects. The Blob object offers some interesting methods:

length() - Returns the number of bytes in the Blob object. The return value has a type of "long". You may need to convert it to "int" to be used in other motheds.

getBytes(long pos, int length) - Returns a substring of characters from the Blob object with a specified starting position and length. Note the start position is "long" value, but the length is "int" value.

getBinaryStream() - Returns a InputStream object from the Blob object so that you can read the content as a stream.

free() - Releases the resources that the Blob object holds. This was added in JDBC 4.0 (Java 1.6).

Here is my test program on getBlob() method:

/** * SqlServerBlobGetBlob.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerBlobGetBlob { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource(); ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Retrieving BLOB value with getBlob() Statement sta = con.createStatement(); ResultSet res = sta.executeQuery("SELECT * FROM Image"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); Blob bodyOut = res.getBlob("Body"); int length = (int) bodyOut.length(); System.out.println(" Body Size = "+length); byte[] body = bodyOut.getBytes(1, length); String bodyHex = bytesToHex(body, 32); System.out.println(" Body in HEX = "+bodyHex+"...");// bodyOut.free(); // new in JDBC 4.0 } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); } } public static String bytesToHex(byte[] bytes, int max) { StringBuffer buffer = new StringBuffer(); for (int i=0; i<bytes.length && i<max; i++) { buffer.append(Integer.toHexString(bytes[i] & 0xFF)); } return buffer.toString().toUpperCase(); }}

The output confirms that the getBlob() method and Blob objects are not hard to use:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobGetBlob

Record ID: 1 Subject = Test on INSERT statement Body Size = 15 Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 2 Subject = Test of the setBytes() method Body Size = 15 Body in HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB...Record ID: 4 Subject = Test of setBinaryStream() methods Body Size = 2663 Body in HEX = CAFEBABE000320B5A03B0487049A02048804AA0204BA...

Inserting BLOB Values with setBlob() MethodIf you want to insert a BLOB column with a character string that comes from a java.sql.Blob object, you can directly set the value with PreparedStatement.setBlob() method.

To test this, I wrote the following program to copy some records with BLOB values as new records back into the same table. During the copy process, the BLOB values are also modified with some Blob object methods - The first 6 bytes are replaced with 0 values.

/** * SqlServerBlobSetBlob.java * Copyright (c) 2007 by Dr. Herong Yang. All rights reserved. */import java.io.*;import java.sql.*;public class SqlServerBlobSetBlob { public static void main(String [] args) { Connection con = null; try { com.microsoft.sqlserver.jdbc.SQLServerDataSource ds = new com.microsoft.sqlserver.jdbc.SQLServerDataSource();

ds.setServerName("localhost"); ds.setPortNumber(1269); ds.setDatabaseName("AdventureWorksLT"); ds.setUser("Herong"); ds.setPassword("TopSecret"); con = ds.getConnection();

// Deleting records for re-testing Statement sta = con.createStatement(); sta.executeUpdate( "DELETE FROM Image WHERE Subject LIKE 'Copy of %'");

// Creating a PreparedStatement for inserting new records PreparedStatement ps = con.prepareStatement( "INSERT INTO Image (Subject, Body) VALUES (?,?)");

// Looping though the first 3 records ResultSet res = sta.executeQuery( "SELECT * FROM Image ORDER BY ID"); int i = 0; while (res.next() && i<3) { i++; System.out.println("Copying record ID: "+res.getInt("ID")); String subject = res.getString("Subject"); Blob body = res.getBlob("Body");

// Modifying the Blob object byte[] chuck = {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00}; body.setBytes(1,chuck);

// Inserting a new record with setBlob() ps.setString(1, "Copy of "+subject); ps.setBlob(2,body); ps.executeUpdate(); } ps.close(); res.close(); // Checking the new records res = sta.executeQuery( "SELECT * FROM Image WHERE Subject LIKE 'Copy of %'"); while (res.next()) { System.out.println("Record ID: "+res.getInt("ID")); System.out.println(" Subject = "+res.getString("Subject")); byte[] body = res.getBytes("Body"); String bodyHex = bytesToHex(body, 32); System.out.println(" Body in HEX = "+bodyHex+"..."); } res.close();

sta.close(); con.close(); } catch (Exception e) { System.err.println("Exception: "+e.getMessage()); e.printStackTrace(); }

} public static String bytesToHex(byte[] bytes, int max) { StringBuffer buffer = new StringBuffer(); for (int i=0; i<bytes.length && i<max; i++) { buffer.append(Integer.toHexString(bytes[i] & 0xFF)); } return buffer.toString().toUpperCase(); }}

The program performed exactly as I expected:

C:\>java -cp .;\local\lib\sqljdbc.jar SqlServerBlobGetBlob

Copying record ID: 1Copying record ID: 2Copying record ID: 4Record ID: 5 Subject = Copy of Test on INSERT statement Body in HEX = 000000C8CABCCCCEB9C9CBBB...Record ID: 6 Subject = Copy of Test of the setBytes() method Body in HEX = 000000C8CABCCCCEB9C9CBBB...Record ID: 7 Subject = Copy of Test of setBinaryStream() methods Body in HEX = 0000000320B5A03B0487049A02048804AA0204BA...JDBC-ODBC Bridge Driver - sun.jdbc.odbc.JdbcOdbcDriver

This chapter provides tutorial notes on the JDBC-ODBC Bridge driver produced by Sun and DataDirect. Topics include loading JDBC-ODBC Bridge driver; creating a DSN (Data Source Name) with a ODBC driver; Connecting to SQL Server with JDBC-ODBC Bridge.

JDBC-ODBC Bridge Driver Features

JDBC-ODBC - Loading sun.jdbc.odbc.JdbcOdbcDriver

JDBC-ODBC - Creating DSN

JDBC-ODBC - Connecting to a DSN

JDBC-ODBC - Problem with Incorrect DSN

JDBC-ODBC Bridge Driver Features