|
YOUR FEEDBACK
Did you read today's front page stories & breaking news?
SYS-CON.TV |
TOP THREE LINKS YOU MUST CLICK ON General Java Browsing the JDBC API
Browsing the JDBC API
Apr. 1, 1998 12:00 AM
It is not easy to query the contents of a database without proprietary front end tools or a database-aware IDE. A database-aware toolkit should be able to connect to and work with a variety of databases (local and remote, application and corporate) without a shift in how we view the contents of different databases. Java and JDBC allow the builder to abstract the viewing of the data from the implementation of the database and database queries that yield the data for viewing. A number of IDEs allow the builder to query the contents of a database as part of the database component integration , but using an IDE is an expensive option for someone who would like a simple tool for viewing and maintaining data in different databases. This article presents a simple database browser that allows access to any database that supplies either an ODBC or JDBC driver, and acts as a simple but useful interface to the JDBC API. The browser can list the various types of table as well as the tables and table columns, and maintain data in those tables via SQL, which is entered free-form. Most of this article addresses the design and build of the browser. The aim is to produce a complete tool written in 100% Java 1.1 (AWT and JDBC) with no third party add-ons, and which is scalable enough to allow a new presentation layer to be bolted on. It is also reasonable to expect browser components to behave as middleware components on a multi-tier platform so that the browser can be imported as a Java Beanª into any Bean-complaint IDE. This will allow the 'Data Browser Bean' to interact with GUI and non-GUI components in a more ambitious distribution of functionality on a broader corporate platform. Although the browser does not implement the Bean interface, this is a small task for those interested in pursuing this extension.
Functionality The user clicks on a button and the relevant tab detail is displayed.
Query Capture and Display Figure 1 shows a query entered into the query window; when the 'Go' button is clicked, the query is executed and the results displayed in the Panel above. The user can limit the number of rows returned from the ResultSet by entering a value in the 'Row Count' text box. Note that the projection name (i.e., the name defined in the query), not the table column name, is displayed. If no projection name were nominated, the table column name would be displayed. Queries that are not select statements (e.g., Update, Insert) will return an update count, and a confirmatory message is appended to the panel display. Figure 2 shows the update message when the update query is executed.
Table and Column Listing
Connection Detail
JDBC Drivers and the JDBC/ODBC Bridge JDBC/Net drivers enable a connection from the client, thru a JDBC driver, to a comms server which then connects to a database server on a remote host. This is required when the JDBC driver does not inherently support the protocol required between the client and the server directly. The local comms server will handle the protocol of database requests and, in some cases, will make transparent the different versions of products on multiple platforms. Generally, a name service is provided on all cooperating hosts to direct the clients to host ports where the service is listening. For example, you could use JDBC to connect to an Ingres/NET comms server, which then establishes a connection to a remote comms server, which in turn connects to the remote database server, and a virtual circuit is then established. In the process, the local and remote name servers are queried to find the host/port addresses of the cooperating services. JDBC drivers can also handle the communication protocol directly. In this case, the JDBC driver connects directly to the database server, local or remote. For those data sources that do not have a JDBC driver, the JDBC/ODBC bridge allows an ODBC data source to be interrogated by the JDBC API. The bridge software converts the JDBC calls into ODBC calls. The Database class in the Database Browser handles the database connection. The User Interface passes an instruction to the database via the protocol handler. The connectDB method uses the instruction operands as arguments to the JDBC API. To use the ODBC data sources, you need to create a new System DSN from the Windows Control Panel. The name you assign to the database is the name you enter into the Database URL field in the Connection Tab. For example, the screenshots use the NorthWestTrade name, which maps down to the nwind.mdb Access database provided by Microsoft. The driver is loaded using the class.forname() method call. The getConnection() method call establishes a link to the database through the loaded driver. Once a link is created, a Statement object is created which is the handle for executing queries as in Listing 1.
Components (Interfaces and Classes)
Design and Build Builders unfamiliar with anonymous classes should find the examples here straightforward.
Separation of Presentation Layer from Database Layer
There are three ways to approach the handling of query results from the database:
In the actual implementation: Use of a log device that is visible to the database class yields a number of benefits. Exceptions can be handled and reported within the database service itself. If this were not the case, then either the protocol handler or the user interface would have to catch the exceptions thrown by the database. This reduces the granularity of error handling, leading to a lack of flexibility. Some of the SQL Exceptions thrown are not malevolent; a JDBC API method may not be supported by the ODBC driver (e.g., getSchemas()). In this case, we want to continue in the database server, not return to the client. We also want to report this fact to standard error.
Use of Interfaces to Support Component Integration
Implementation of Observer/Observable from a Model/View Design Pattern
Protocol Handling with the use of a Protocol Client and Server The data browser uses a 'protocol client' class to package up a protocol instruction into a 'protocol data' class which then forwards this instruction to the 'protocol server' class to interpret, the interpretation being a call to a DataBase method to execute a JDBC method call. What seems like overengineering a simple procedure yields dividends when a host implements a different service. The client will then require access to a different protocol client to connect to this service, the simple cost being the implementation of a new protocol interface to parameterize new service instructions instead of hardcoding the service calls manually. The ProtocolClient class contains a method to package up operand values and send a protocol instruction to the protocol server. The example in Listing 3 takes the query entered by the user, and the row limit, as operands, and sends an execute query instruction to the ProtocolServer object. The ProtocolServer class in Listing 4 contains a method that unpacks these operands and executes the instruction opcode. The object passed between the protocol client and server is an instance of ProtocolData class. Listing 5 shows the ProtocolData class handling all the operand/operator capture. The ProtocolData object is a Singleton object; i.e., one instance of the class is created, regardless of how many times the constructor is called:
private static ProtocolData pd; The ProtocolClient and ProtocolServer classes are never instantiated; the relevant variables and methods are declared as static so they are referenced at class level. There is no need to instantiate them. A distributed, multi-tier treatment of the protocol handling would use RMI, in which the call to the serviceDBRequest() methods is an RMI call. Without RMI, the ProtocolData object must be serializable, with the protocol client and server connected over sockets.
Tight Coupling of Component to Event using Anonymous Classes Listing 7 shows how to display the Query Window when the Query button is pressed.
Strict Use of Core Java 1.1 AWT Classes In a prototyping context, the ideal would be to use a Bean-compliant IDE and Bean-compliant widgets, of which there are many. However, the design constraints dictate the core functional requirement that a user interface be placed on top of the JDBC API. Some would argue that the user interface is the most disposable part of an application; this is true if the parts are built quickly and cheaply. Evaluation of GUI components is outside the scope of this exercise, so a decision was made to use the core AWT components, using a CardLayout to implement a form of tabbed panelling. Good use is made of the CardLayout component, which implements tab-like functionality. Here, the user clicks a button (Query, Tables, Connect) and a different card, or 'Tab', is displayed. The contents of the tab persist between displays so that, for example, the list of tables is still displayed when entering and returning from the Query tab. When the user clicks one of the central buttons, the relevant tab is displayed as follows (using an anonymous class):
connectButton.addActionListener( Panel panel4 is a panel that has been added to the tabOptions CardLayout component and has a tag that is equivalent to the text of the button which fires the action event. The Connect tab is shown when the application starts up by setting the default display property as follows:
tabOptions.first(panel4);
Extensions and Conclusion Clearly, not all of the API has been implemented; interested readers can include extra method calls using popup menus against different menu options; e.g. metadata, statements, connection details, etc. It is a moderately simple task to hook another user interface to the protocol and database classes. One enhancement would be to attach a grid component to the user interface. There are several excellent grid components available for 1.0 and 1.1 Java; Swing grids are available for 1.2. The next enhancement would be to generate screens dynamically based on the columns of tables that we want to update. For example, we may select a database and a table, then nominate columns for a form that would be used to qualify queries for insert, update and delete queries. Remember that the browser is not tied to one database design so the forms must be created dynamically. Network distribution of components can be achieved with little modification to the code through an RMI implementation, with the User Interface and Protocol Client on one host and the Protocol Server and DataBase on another host. All four components could be on four different hosts. One drawback in multi-tier hosting of service objects is the serialization and network transport of marshalled objects. Java 1.1 introduces object serialization; it is very easy to implement this feature without using RMI. Essentially, the ProtocolData instance is serialized and passed over a TCP/IP socket connection to a listening protocol server. Since both client and server are Java, you can use RMI, which implements serialization underneath the RMI protocol. For an implementation where many clients require access to the database, the main requirement is that the Database class makes all methods 'synchronized' so that there is no contention when updating or inserting data. Where many clients require the protocol handler to perform a service, the ProtocolData object must not be a Singleton object, but be instantiated for every client that requires a protocol instruction to be formatted. The ProtocolClient and ProtocolServer classes would need some adjustment to enable the client/database handles to retain their identity for each client. For example, the ProtocolClient could be instantiated for every user interface connection, the variable data being a reference to the main UI object (the methods remain static); alternatively, a static array could contain a list of references to controlling user-interface objects. In summary, when we relocate cooperating objects from the same virtual machine, we need to implement RMI or sockets while retaining the component functionality. Most functional requirements you explore can be implemented by extending the core classes in this way. LATEST JAVA STORIES & POSTS
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK SPONSORED BY INFRAGISTICS
BREAKING JAVA NEWS |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||