HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

With the release of the Java 1.1 API, Java developers have even more tools to work with. Two important abilities that were added to the Java 1.1 API include the Java Database Connectivity (JDBC) objects and objects to handle Serialization. JDBC provides developers with the means to connect to any type of database and perform queries and updates against the data located in the database. The JDBC API finally gives developers the ability to create usable business solutions using Java. Another new ability added to the Java language is the ability to use Serialization. Serialization is the process by which the application can send program objects through a stream, which can be a file stream or a network stream. Sending objects through a stream will allow developers to create solutions that were not available until now.

In this article, I will demonstrate how to use Java Serialization and the JDBC to save objects to a database and then access these objects from the same database. This process will allow an application to save a current session to a database and then reload the session from the database at a later date. Unlike current programming languages, Java allows me to save the object itself and not just the data contained within the object. Most programming languages allow the application to save the data contained within an object, but the data has to be reloaded into the object to be usable. Using Java's Serialization, I can save the entire object, including the look and feel of the object, to a file.

For this article, I am using Sybase SQL Anywhere as my database. An evaluation copy of this database is available from www.sybase.com. I am also using the JDBC-ODBC Bridge, available from JavaSoft at www.javasoft.com, to provide a JDBC driver to access the database. There are many different databases and drivers that could be used to provide the same solution; however, I chose these two products since they both provide an evaluation copy that can be used.

I will be using only one table to store the objects I will be saving in this article. The table, tObject, will contain two columns, ObjName and Object. ObjName will be a character field that will store the name of the object. The Object column will be a LongBinary column that will contain the binary data of the object. Depending on the database being used, the LongBinary data type could also be known as a Blob, LongVarBinary, or sometimes just Binary.

I will create two applications in this article. The first application will create a basic window with a Close button. The window will be serialized and saved to the tObject table. I will use JDBC to access the database and insert a new record into the tObject table. I will store a reference to the window and a stream of data that contains the serialized window object. The second application will simply connect to the database, retrieve the window object and open it.

The first application will extend the Frame object to create a new window. I will name this new object AppFrame. In the constructor for the AppFrame class, I will initially call the ancestor method to create the Frame object. Once the frame has been created, I will place a button with the label Close on the window using the FlowLayout.

this.setLayout(new FlowLayout(FlowLayout.CENTER));
this.add(new Button("Close"));

Once the button has been added to the frame, I will make a connection to the database using the Driver object provided with the JDBC-ODBC Bridge. To register the Driver object with the DriverManager object, I will simply declare the Driver object by using the Class.forName() method. This method finds and loads the specified class. The following line of code will attempt to find and load in the JDBC-ODBC Bridge object that will provide a link to an ODBC data source.

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Once the Driver object is declared and loaded, it is registered with the DriverManager object. I can now use the DriverManager object to create a connection to a database with a given Uniform Resource Locator (URL). The URL of the data source will simply be jdbc:odbc:, where is the declared name of the ODBC data source. New data sources can be added in Windows 96 and NT by using the ODBC utility located in Control Panel. I am using the data source name JDBC, which is an ODBC connection to my SQL Anywhere database. The following line of code will return a Connection object that is created to the JDBC database I am using. Notice that I also provide a username and password, needed to connect to the database.

Connection c = DriverManager.getConnection
("jdbc:odbc:JDBC", "dba", "sql");

Once a connection has been established with the database, the application can begin to serialize itself into a local file. This local file with then be read into the application and stored as a binary stream in the database. To output the serialized object, I will use the new Java Object, ObjectOutputStream. The ObjectOutputStream takes an OutputStream object and allows the developer to write native data types and objects to the output stream.

FileOutputStream ostream = new FileOutputStream("temp.dat");
ObjectOutputStream out = new ObjectOutputStream(ostream);
out.writeObject(this);

Notice that I used the writeObject() method to write the contents of the current object to the serialized stream. The ObjectOutputStream also provides other methods for writing integers, booleans and most other native data types in Java.

Once the object has been written to a local file, I will save the contents of the file in the tObject table, located on the database. I will use the PreparedStatement object to create a SQL Insert statement that I can later insert specific values into. The SQL Insert statement will insert a record into the tObject table with the ObjName equal to Frame1 and the contents of the local file saved above. The following lines will create a PreparedStatement object with the SQL Insert statement needed to allow the object information to be specified using the setBinaryStream() method of the PreparedStatement object.

String sql = "insert into tObject values (Frame1', ?)";
PreparedStatement prepare = c.prepareStatement(sql);

Now that I have the SQL statement ready to be executed, I will need to get the binary data stream that I want to use in place of the ? in the Insert statement above. Since the setBinaryStream() method takes an InputStream object and the length of the stream in bytes, I will need to open the local file that contains the serialized data stream and pass the InputStream object and the length of the input stream. I can do this by using the FileInputStream object and its available() method.

FileInputStream istream = new FileInputStream("temp.dat");
int count = istream.available();

I can now set the parameter in the Insert statement to the contents of the local file using the setBinaryStream() method. Since there is only one parameter in the SQL Insert statement, I instruct the method to set the value of the first parameter with the contents of the input stream.

prepare.setBinaryStream(1, istream, count);

The Insert statement is now ready to be sent to the database and executed. Calling the executeUpdate() method of the PreparedStatement object will send the completed SQL statement to the database for processing.

prepare.executeUpdate();

This finishes the first application needed to save the serialized object to the database. Listing 1 contains the complete code for the AppFrame class.

The second application will be responsible for connecting to the database and getting the Frame object that was saved using the AppFrame application. The retrieved frame will then be displayed to the user exactly as it appeared in the AppFrame application. Using this method for saving objects, it is possible to create applications that can save all of the components within an application to the database and then use the database as a persistent object server. The database and serialization method also could be used to create an extensible and usable version control system. Not only could the source and compiled files be stored in the versioning system, but the actual objects the application uses could be versioned and stored as well.

The second application I will create will be the GetFrame application. It will not contain any code in the constructor, but the main() method for the application will be responsible for connecting to the database and retrieving the object. Once the Frame object has been retrieved from the database, it will be displayed to the user in the same fashion as a normal Frame object.

The connection to the database will be established in exactly the same fashion as it was established in the AppFrame application. Once the connection to the database has been established, I will execute a SQL Select statement to return the Object column from the database for the record that has ObjName equal to Frame1. The SQL Select statement will be as follows:

String sql = "select Object from
tObject where ObjName = Frame1'";

I will then use a Statement object and a ResultSet object to execute the SQL Select statement and store the result set returned by the Select statement. The executeQuery() method for the Statement object will take as a parameter a SQL statement and return the results returned from the database into a ResultSet object. The following two lines of code will create the Statement object, execute the Select statement and store the results returned from the database into the ResultSet object.

Statement s = c.createStatement();
ResultSet r = s.executeQuery(sql);

Once the result set has been returned, I will need to call the next() method of the ResultSet object to move to the first record in the result set. I can then use the getBytes() method to get the bytes returned for the Object column. The following line of code will get the data stored in the Object column and store it in a local array of bytes.

byte b[] = r.getBytes(1);

I passed parameter 1 to the getBytes() method to indicate that I wanted the data from the first column. I also could have passed the string Object to indicate that I wanted the contents of the Object column from the result set. Once I have the array of bytes for the object, I will need to write those bytes to a local file using a FileOutputStream and then read those bytes back into the application using a FileInputStream and the ObjectInputStream. To write the bytes to a local file, I will create a FileOutputStream object, specifying the name of the file to create, and then use the write() method of the FileOutputStream object to write the bytes. The following three lines of code will create a file to save the byte stream in, write the array of bytes to the output stream and close the output stream and file.

FileOutputStream ostream = new FileOutputStream("temp,dat");
ostream.write(b);
ostream.close();

Once the data has been saved to the local file, I can use the FileInputStream to create an input stream that I can then use to create an ObjectInputStream. The ObjectInputStream will allow me to read in serialized data objects. The following code will create the input stream and the ObjectInputStream object using the supplied input stream.

FileInputStream istream = new FileInputStream("temp.dat");
ObjectInputStream in = new ObjectInputStream(istream);

Now that I have an object stream, I can read in the Frame object that was saved in the AppFrame application. I can use the readObject() method of the ObjectInputStream object to read in any data types that are not native. I will need to typecast the returned value to be a Frame object before I can assign it to a local Frame variable. Once the object has been read in and stored in a local Frame variable, I will call the show() method top display the frame to the user. It will look exactly like the frame displayed in the AppFrame application.

Frame f = (Frame)in.readObject();
f.show();

Listing 2 contains the full code listing for the GetFrame application.

Using a combination of Java Serialization and JDBC, I was able to use a database to store Java objects. Using JDBC and Serialization, Java applications can be developed that provide functionality as well as flexibility. Applications can store themselves in a database and then be recreated later, or developers can store completed objects within a database to be accessed by users directly. Using JDBC and Serialization was not complex, but it allows for some complex solutions to be created.

About the Author
Ashton Hobbs is a consultant, Webmaster and Java programmer with STEP Consulting, Inc. He is a Certified PowerBuilder Associate and has created a number of Web-based applications using HTML, Javascript, Web.PB and Java with JDBC. Ashton can be reached at [email protected]

	

Listing 1.

import java.io.*;
import java.sql.*;
import java.awt.*;

public class AppFrame extends Frame {

	public AppFrame () {
	super();

	this.setLayout(new FlowLayout(FlowLayout.CENTER));
	this.add(new Button("Close"));

		try {
	Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    Connection c = DriverManager.getConnection("jdbc:odbc:JDBC", "dba", "sql");
	FileOutputStream ostream = new FileOutputStream("temp.dat");
	ObjectOutputStream out = new ObjectOutputStream(ostream);
	out.writeObject(this);
	ostream.close();
	String sql = "insert into tObject values ('Frame1', ?)";
	PreparedStatement prepare = c.prepareStatement(sql);
	FileInputStream istream = new FileInputStream("temp.dat");
	int count = istream.available();
	prepare.setBinaryStream(1, istream, count);
	prepare.executeUpdate();
	istream.close();
	prepare.close();
	c.close();
}
catch (Exception e) {}
}

public static void main (String args[]) {
	AppFrame app = new AppFrame();
	app.setSize(200,200);
	app.show();
}
}

 Listing 2		
	
import java.io.*;
import java.sql.*;
import java.awt.*;

public class GetFrame {

	public GetFrame () {}

	public static void main (String args[]) {
		try {
	Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
	Connection c = DriverManager.getConnection("jdbc:odbc:JDBC", "dba", "sql");
	String sql = "select Object from tObject where ObjName = 'Frame1' ";
	Statement s = c.createStatement();
	ResultSet r = s.executeQuery(sql);
	r.next();
	byte b[] = r.getBytes(1);
	FileOutputStream ostream = new FileOutputStream("temp,dat");
	ostream.write(b);
	ostream.close();
	FileInputStream istream = new FileInputStream("temp.dat");
	ObjectInputStream in = new ObjectInputStream(istream);
	Frame f = (Frame)in.readObject();
	f.setSize(200,200);
	f.show();
}
catch (Exception e) {}
}
}


 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.