"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" Statements When 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" Statements Sometimes 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 Statements This 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" Statements SQL 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 wo rked correctly:
Number of rows inserted: 10 List 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" Statements INSERT statements are used very often by database app lications 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: 1 List 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 Columns An 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" Statements UPDATE 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: 5 List 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" Statements DELETE statements are used less frequently in database ap plications. 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: 4 List 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 – PreparedStatement This 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 Server In 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 database 2> USE AdventureWorksLT; 3> GO Changed database context to 'AdventureWorksLT'. 1> -- Create a new server login name: Herong 2> CREATE LOGIN Herong WITH PASSWORD = 'TopSecret' 3> GO 1> -- Create a new database user linked to the login name 2> CREATE USER Herong FOR LOGIN Herong; 3> GO 1> -- Grant database ALTER permision to the user 2> GRANT ALTER To Herong; 3> GO 1> -- Grant database CONTROL permision to the user 2> 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 database 2> USE AdventureWorksLT; 3> GO Changed database context to 'AdventureWorksLT'. 1> -- Create a new table 2> CREATE TABLE Test (ID INT); 3> GO 1> -- Drop the table 2> 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 Column IDENTITY column is a nice feature provided by SQL Server that provides autoincremented 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 SqlServerIdentityColumn Table created.
Inserting Rows to the Test Table INSERT statements are used very often by database app lications 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 SqlServerMultipleInserts Number of rows inserted: 12 List 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 Overview If 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 SqlServerPreparedSelect User ID 2: Janet Gates PreparedStatement with Parameters To 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 da ta 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 SqlServerPreparedStatementParameter User ID 9: 18c5 b7e7e PreparedStatement in Batch Mode If 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(...); ... ps.addBatch(); ps.setXXX(...); ... ps.addBatch(); ps.setXXX(...); ... ps.addBatch();
// Set parameters for the first copy // Create the first copy of the SQL statement // Set parameters for the second copy // Create the second copy of the SQL statement // Set parameters for the third copy // 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
}
}
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 PreparedStatement Running 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 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:
/** * 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 PreparedStatement Regular Statement
# of inserts 10000 10000
Time in ms. 5625 6750
Comparison 100% 120%
Performance of Inserting Rows with a ResultSet Since 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 PreparedStatement Regular Statement ResultSet
# of inserts 10000 10000 10000
Time in ms. 5625 6750 6203
Comparison 100% 120% 110%
SQL Server CLOB (Character Large Object) – TEXT This 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 d river 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 c lass. CLOB data can also be handled as character streams with the java.io.Reader class. Create Tables with CLOB Columns SQL 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 database 2> USE AdventureWorksLT; 3> GO 1> -- Create the test table with a CLOB column 2> 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 Statements The 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() Method Another 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 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. Inserting CLOB Values with setCharacterStream() Method If 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 b y 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() Method The 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() Method CLOB 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() Method If 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() Method If 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: 1 Copying record ID: 2 Copying 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) – 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 d river 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 systems Create Tables with CLOB Columns SQL 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 database 2> USE AdventureWorksLT; 3> GO 1> -- Create the test table with a BLOB column 2> 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 Statements The 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.
/** * 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, 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() Method Another 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() Method If 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 u se 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 b y 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 error com.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() Method The 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
}
}
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: Subject Body in Record ID: Subject Body in Record ID: Subject Body in
1 = Test on INSERT statement HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB... 2 = Test of the setBytes() method HEX = C9CBBBCCCEB9C8CABCCCCEB9C9CBBB... 4 = Test of setBinaryStream() methods HEX = CAFEBABE000320B5A03B0487049A02048804AA0204BA...
Retrieving BLOB Values with getBinaryStream() Method BLOB values can also be b e 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" "+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 k now 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() Method If you like to work with java.sql.Blob objects, ob jects, 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" "+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
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() Method If 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(); }