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 achobbs@stepinc.com
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) {}
}
}