 | Level: Advanced Andrew Spyker (aspyker@us.ibm.com), Senior Software Engineer, IBM Christopher Blythe (cjblythe@us.ibm.com), Software Engineer, IBM
14 Oct 2005 Study several techniques and coding best practices that you can use to improve the performance of your Service Data Objects (SDO) and Java™ DataBase Connectivity (JDBC) Data Mediator Service (DMS) application.
Introduction
Part 1 of this article series provided an introduction to Java™ DataBase Connectivity (JDBC) Data Mediator Service (DMS)-based queries as well as a model for a typical SDO and JDBC DMS application. Part 2 examines several techniques and coding best practices that you can use to improve the performance of your SDO and JDBC DMS application. These techniques include:
- Passive connection wrappers
- Statically defined JDBC DMS metadata
- Reduction of query results (using external tables)
- User supplied SQL
- Statically defined data models
By using the five queries (allSystems, systemByHostname, recordsByShortname, usersHoldingSystems, and systemsCheckedOutByUser) for the Hardware Reservation sample application that were defined in the pervious article, we demonstrate how you can apply each of these techniques to your existing JDBC DMS code. Next, we measure the performance gains associated with each modification based on the test methodology outlined in the previous part of this series. Finally, we discuss the architectural impact of each modification.
Using passive connection wrappers
Listing 2 in the previous article demonstrated how to establish a connection to the database using a ConnectionWrapper that is needed by the mediator instance to perform a query against the database. In that code sample, the createConnectionWrapper() method was used to obtain an active connection wrapper. Active indicates that the mediator is responsible for performing a commit or rollback operation against the database connection. This feature is convenient and allows for simple rapid development, but what about scenarios where you need to perform several queries in a logical transaction?
In this type of scenario, you would want to use a passive connection and handle the commit and rollback operations within the application code. Listing 1 below demonstrates how to use passive connection wrappers to manage a transaction.
Listing 1. Performing a query using a passive connection wrapper
// Establish connection and connection wrapper
DataSource datasource = (DataSource) context.lookup(...);
ConnectionWrapperFactory factory = ConnectionWrapperFactory.soleInstance;
Connection conn = datasource.getConnection();
conn.setAutoCommit(false);
// Create a passive connection wrapper instead of active
ConnectionWrapper wrapper = factory.createPassiveConnectionWrapper(conn);
. . .
// Create the mediator and perform the query based on the
// hwsystem metadata defined above.
JDBCMediatorFactory mFactory = JDBCMediatorFactory.soleInstance;
JDBCMediator med = mFactory.createMediator(hwsystem, wrapper);
DataObject system = med.getGraph();
. . .
// For passive connections, you must explicitly commit the connection
wrapper.getConnection().commit();
// Close the connection
wrapper.getConnection().close();
|
For a logical transaction with multiple queries, the passive connection wrapper can improve performance, since a commit is not performed after each query. Furthermore, a passive connection wrapper provides a finer level of control over your database operations. Figure 1 below demonstrates the performance improvement that you can realize using passive connection wrappers and a single database commit with the systemByHostname query demonstrated in Listing 4 from the previous article. With single query transactions, a passive connection wrapper is roughly equivalent in performance to an active connection wrapper. However, with ten systemByHostname queries performed as a single transaction, a 23.6 percent improvement was measured using passive connection wrappers.
Figure 1. Passive connection wrappers versus active connection wrappers
Using pre-built metadata
In a typical Java 2 Platform, Enterprise Edition (J2EE) application, it's rare that a particular database query will only be executed a small number of times. For your Hardware Reservation application, it's safe to say that the previously defined queries are fairly standard for this type of application and would be executed frequently. A first attempt at writing a method to perform the systemByHostname query might look something like the following in Listing 2 below (based on code Listings 1, 3, and 4 from the previous article).
Listing 2. findSystemByHostname method
public DataObject findSystemByHostname(String hostname) {
. . .
Create metadata for systemByHostname query
. . .
Establish connection and connection wrapper
. . .
JDBCMediatorFactory mFactory = JDBCMediatorFactory.soleInstance;
JDBCMediator med = mFactory.createMediator(hwsystem, wrapper);
DataObject args = med.getParameterDataObject();
args.setString("HOSTNAME", hostname);
DataObject system = med.getGraph(args);
. . .
Commit and close the connection
. . .
return system;
}
|
The major drawback to this code is that each time the findSystemByHostname() method is called, you re-build the JDBC DMS metadata for the query from scratch. Since the metadata will never change, a better solution is to define a class to manage the metadata creation for all of your queries and return the metadata statically, as shown in Listing 3 below.
Listing 3. Schema factory for metadata creation
public class HWReserveMetadataFactory {
public static Metadata systemByHostname = null;
static {
systemByHostname = generateSystemByHostnameMetadata();
}
private static Metadata generateSystemByHostnameMetadata ()
. . .
Create metadata for systemByHostname query
. . .
return metadata;
}
}
|
Using this approach, the metadata is only created once by the static initializer when the HWReserveMetadataFactory class is loaded. You can then change the findSystemByHostname() method to access the pre-built metadata using the systemByHostname class variable instead of recreating the metadata on each request. If you disregard the execution time of the query and simply look at the time required to build the metadata versus accessing it statically, the performance improves by multiple orders of magnitude. However, building up the metadata is relatively light weight in comparison to executing the database query. Consequently, this approach yields a marginal improvement in overall run-time performance. From a memory footprint and garbage collection perspective, you have significantly reduced the number of temporary objects that are created to build up the metadata.
Another advantage of creating a separate class to handle metadata creation is the ability to reuse common code. For instance, the recordsByShortname and usersHoldingSystems require the same code to define the UserAccount table and columns. As the Listing 4 demonstrates, this can easily be consolidated into a single method, which can then be reused to create both sets of metadata.
Listing 4. Reuse code within metadata factory
public class HWReserveMetadataFactory {
public static Metadata recordsByShortname = null;
public static Metadata usersHoldingSystems = null;
static {
recordsByShortname = generateRecordsByShortnameMetadata();
usersHoldingSystems = generateUsersHoldingSystemsMetadata();
}
. . .
private static void addUserAccountTable (Metadata meta) {
. . .
Code to define metadata for the UserAccount table
. . .
}
private static Metadata generateRecordsByShortnameMetadata () {
. . .
addUserAccountTable(metadata);
Update remaining metadata for recordsByShortname query
. . .
return metadata;
}
private static Metadata generateUsersHoldingSystemsMetadata () {
. . .
addUserAccountTable(metadata);
Update remaining metadata for userHoldingSystems query
. . .
return metadata;
}
}
|
Specifying tables as "external"
Take a quick look back at the pseudo-metadata code for the recordsByShortname query in the Part 1 of this series. To perform this query, you have to add the UserAccount and CheckOutRecords tables, establish the relationship, and set up the UserAccount shortname filter on the metadata. Since both tables are defined to the metadata, the resulting data graph will consist of data objects from both tables. One data object represents the user account selected by the shortname and the remaining data objects represent all checkout records associated with this user. If you need both sets of information at the same time, this is an extremely powerful feature.
Now consider if your application only needs the checkout records. In this case, the user account information is completely unnecessary. To keep this data from showing up in the resulting data graph, the UserAccount table can be denoted as an "external" table when defining the metadata. Even though the mediator needs the user account metadata to generate the correct query, the user account data returned in the result set is ignored and the data graph is only populated with checkout record data objects. This improves the overall efficiency of the mediators getGraph() call. Listing 5 below illustrates how to designate an external table.
Listing 5. Defining external tables
. . .
// CheckOutRecord metadata
Table recordTable = metadata.addTable("CHECKOUTRECORD");
Column recordID = recordTable.addIntegerColumn("ID");
recordTable.addStringColumn("USAGE1");
recordTable.addTimestampColumn("CHECKIN");
recordTable.addTimestampColumn("CHECKOUT");
recordTable.addIntegerColumn("SYSTEM_ID");
recordTable.addIntegerColumn("USERACCOUNT_ID");
recordID.setNullable(false);
recordTable.setPrimaryKey(recordID);
metadata.setRootTable(recordTable);
// UserAccount metadata
Table userTable = metadata.addTable("USERACCOUNT");
Column userID = userTable.addIntegerColumn("ID");
userTable.addStringColumn("FULLNAME");
userTable.addStringColumn("SHORTNAME");
userTable.addStringColumn("EMAIL");
userTable.addStringColumn("PASSWORD");
userID.setNullable(false);
userTable.setPrimaryKey(userID);
// Designate userTable as External
userTable.setExternal(true);
. . .
|
Figure 2 below demonstrates the performance improvements that can be realized by designating an unecessary table in the resulting data graph as external. In addition to the recordsByShortname query, you also modified the usersHoldingSystems query to set the CheckOutRecord table as external.
Figure 2. Unused tables versus external tables
For the recordsByShortname query, designating the UserAccount table as external translated into a 13.8 percent improvement in performance. This is a sizable improvement when you consider that you are only removing a single user account data object from the resulting data graph that contains on average nine checkout record data objects. The usersHoldingSystems query, on the other hand, benefited a great deal from designating the CheckOutRecord table as external. For this test case, you eliminated half of the data objects in the resulting data graph and performance improved 49.6 percent.
Another small optimization that can be used when defining finders based on data in external tables is to limit the number of columns defined to the metadata for the external table. For instance, in the recordsByShortname query, the only information used from the user account metadata is the primary key (ID) and the discriminator column (SHORTNAME). All other columns can be removed from the metadata for this finder, because they are not needed in the query or returned in the resulting data graph. This slight modification improved the existing measurements by a few tenths of a percent.
Using user-supplied SQL statements
Another key feature of the JDBC DMS that has a significant impact on performance is the ability to use user-supplied SQL statements instead of relying upon the mediator to generate the query for you. The user-supplied query option allows for performance improvements in three following areas:
-
Full control over SQL statements -- In some cases, the JDBC DMS might not generate the most efficient SQL statement to perform a particular query. For instance, the JDBC DMS uses joins to handle queries involving two or more tables, and in some circumstances, this might not be necessary.
-
No query generation -- Since the user is now supplying the SQL query to the JDBC DMS, the logic needed to generate the query based on the defined metadata can be skipped.
-
Slimmer metadata definitions -- With user-supplied SQL, the metadata is only used to generate the data graph from the result set. Therefore, the metadata definition can be slimmed down to remove anything that is not explicitly needed in the resulting data graph. This would include filters, external tables, or any relationships used to support filters based on external tables.
Looking back at your systemsByHostname query example, the changes necessary to perform the query using user-supplied SQL are demonstrated in Listing 6 below. Note that you are now providing the SQL statement to the mediator during the createMediator() call and using the property index value to set up the parameter data object.
Listing 6. User-supplied SQL
// Establish connection and connection wrapper
. . .
String sql = "SELECT * FROM HWSYSTEM WHERE HOSTNAME = ?";
JDBCMediatorFactory mFactory = JDBCMediatorFactory.soleInstance;
JDBCMediator med = mFactory.createMediator(hwsystem, sql, wrapper);
DataObject args = med.getParameterDataObject();
args.setString(0, hostname);
DataObject system = med.getGraph(args);
. . .
// Commit and close the connection
|
In addition to the systemsByHostname query, the usersHoldingSystems and systemsCheckedOutByUser queries were modified to use user-supplied SQL. Figure 3 below demonstrates the resulting performance improvement. For this series of tests, external tables were used, and where applicable, the same metadata definitions were used for both the generated and user-supplied query cases.
Figure 3. Generated queries versus user-supplied SQL queries
For the systemByHostname single table, single-row finder, only a 12.2 percent improvement was achieved. Given the relative simplicity of this finder to the other test cases, this is a reasonable result. The usersHoldingSystems and systemsCheckedOutBuyUser queries improved by 21.3 percent and 31 percent respectively, and demonstrate that the potential for improvement increases with the complexity of the metadata and the generated query.
For example, let's take a quick look at the usersHoldingSystems test case, as shown in Listing 7 below. Using DB2® as the relational database provider, the following query is generated by the JDBC DMS based on the defined metadata.
Listing 7. usersHoldingSystems test case
WITH CTE1 AS (SELECT CHECKOUTRECORD.ID, CHECKOUTRECORD.USAGE1,
CHECKOUTRECORD.CHECKIN, CHECKOUTRECORD.CHECKOUT,
CHECKOUTRECORD.SYSTEM_ID, CHECKOUTRECORD.USERACCOUNT_ID FROM
CHECKOUTRECORD WHERE CHECKIN IS NULL) SELECT USERACCOUNT.ID,
USERACCOUNT.SHORTNAME, USERACCOUNT.FULLNAME, USERACCOUNT.EMAIL,
USERACCOUNT.PASSWORD FROM USERACCOUNT INNER JOIN CTE1 ON
USERACCOUNT.ID = CTE1.USERACCOUNT_ID
|
However, the following user-supplied SQL was used to perform the same query, as shown in Listing 8 below.
Listing 8. User-supplied SQL
SELECT * FROM USERACCOUNT WHERE ID IN (SELECT USERACCOUNT_ID FROM
CHECKOUTRECORD CR WHERE CR.CHECKIN IS NULL)
|
It is fairly easy to see that some major differences exist between the two queries that will impact performance. The most obvious differences between the two statements are the use of fully qualified table/column names and the explicit definition of the fields in the result set. This over qualification of the columns is necessary, because the JDBC DMS cannot ensure that all columns in the database table have been defined in the metadata. However, this significantly increases the size of the query passed over the wire to the database by the JDBC driver.
A slightly more subtle difference between the two statements is the use of database joins for queries involving two or more tables. For the generated query case, the result set contains data associated with both the CheckOutRecord and the UserAccount tables, even though the checkout record was designated as an external table in the metadata. The user-supplied query, on the other hand, uses a sub-select instead of a join and only returns the user account information. This results in half as much information being prepared in the database, returned to the application server, and parsed by the mediator, thus improving the overall efficiency of our database query.
Static data models
One of the more important features of SDO 2.0 is the ability to use both dynamic and static (strongly typed) APIs (application programming interfaces) for accessing data. When accessing data within an SDO using the dynamic API, the code would look something like Listing 9 for the data graph returned from your systemByHostname query.
Listing 9. Accessing data in a dynamic SDO model
. . .
DataObject systems = med.getGraph(args);
DataObject hwsystem = (DataObject) systems.getList("HWSYSTEM").get(0);
String hostname = hwsystem.getString("HOSTNAME");
. . .
|
With a static model, this same task would be performed using the following code in Listing 10 below.
Listing 10. Accessing data in a static SDO model
. . .
DataGraphRoot systems = med.getGraph(args);
HWSYSTEM hwsystem = systems.getHWSYSTEM().get(0);
String hostname = hwsystem.getHOSTNAME();
. . .
|
In an environment where data models are generated on the fly at runtime, SDO dynamic models work well. All of the examples we have presented thus far create the models for the HWSystem, UserAccount, and CheckOutRecord data objects using dynamic models generated at runtime from the JDBC DMS metadata.
However, if the shape of the data object is known during development, a static model can be generated and used to simplify data access within the application, as demonstrated in the code in Listing 8. Before discussing the performance aspects of static SDO models, we would like to briefly discuss the steps required to generate the static model using the systemByHostname query as an example. This discussion uses Rational® Application Developer 6.0 to generate the static model, but you could just as easily use the base Eclipse Platform with the EMF (Eclipse Modeling Framework) / SDO Version 2.0.1 development plug-ins loaded. For additional details, please consult the EMF documentation and examples located on the Eclipse site.
Generating static SDO models
Saving the EMF meta model (Ecore) -- The JDBC DMS metadata API provides a saveToEcore() function that allows you to save the underlying EMF meta model that backs an SDO to a file in the current working directory. You can do this using the following code, as shown in Listing 11 below.
Listing 11. Generating Ecore model file
. . .
metadata.saveToEcore("hwsystem.ecore","hwsystem",
"com.ibm.websphere.samples.performance.hwreserve.sdo.models.hwsystem");
. . .
|
This method generates a file named hwsystem.ecore that contains a description of the data object named hwsystem. The third argument specifies the package name that will be used for the generated static classes.
Modifying the Ecore file for uniqueness -- By default, the ecore file that is generated by the saveToEcore() function names the root object of the data graph structure DataGraphRoot. If you are using multiple static models in your application, this can become extremely confusing since the only way to differentiate between the generated classes is by using the fully qualified package name. To avoid this issue, the eClassifiers tag for DataGraphRoot should be modified to a unique identifier. In Listing 12 below, the tag was modified to HWSystemRoot.
Listing 12. Modifying the root eClassifier name for uniqueness
. . .
<eClassifiers xsi:type="ecore:EClass" name="HWSystemRoot">
. . .
|
Another item that should be modified in order to ensure uniqueness in the EMF package registry is the namespace URI (nsURI). This is another field that defaults to a common name (DataGraphRoot.ecore) for all Ecore files generated with the saveToEcore() method. See Listing 13 below.
Listing 13. Modifying nsURI for uniqueness
. . .
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
name="com.ibm.websphere.samples.performance.hwreserve.sdo.models.hwsystem"
nsURI="HWSystemRoot.ecore" nsPrefix="hwsystem">
. . .
|
Importing the Ecore file into Rational Application Developer -- To create a new EMF model in your project, open the New dialog window by selecting File > New > Other. Within the dialog window, check Show All Wizards and select EMF Models under the Eclipse Modeling Framework folder. Click Next. See Figure 4.
Figure 4. Create EMF core and generator models
If a Confirm Enablement dialog appears, click Ok. Select the parent folder within your project and specify the file name for the imported generator model. In this case, we have chosen hwsystem.genmodel. Click Next. See Figure 5.
Figure 5. Create a new generator model resource
At this point, you need to specify the initial source for your model. You will be loading the model from an EMF core model, but you could also load from an annotated Java file, an XML Schema, or a Rational Rose® class model. Click Next. See Figure 6.
Figure 6. Specify how to load the initial model contents
Specify the location of the hwsystem.ecore file that was created in the first step and click Next. See Figure 7.
Figure 7. Specify an Ecore file
Select the hwsystem package and click Finish. See Figure 8.
Figure 8. Package selection
This will import the hwsystem.ecore file into your project and generate a hwsystem.genmodel file.
Set the SDO defaults -- Open the hwsystem.genmodel file and right-click on the System model. In the menu, select Set SDO Defaults. Save the changes to the model. This step alters various fields within the genmodel file, allowing the model to be represented as an SDO object instead of a base EMF object. See Figure 9.
Figure 9. SDO defaults
Generate the model code -- Once again, right-click on the HWSystem model and select Generate Model Code from the menu. See Figure 10.
Figure 10. Generate model code
This will generate the following classes in your project for the static model. See Figure 11.
Figure 11. Static model classes
Once the static model for the data has been created, a few small changes are needed in your application code to use the static model with the JDBC DMS. The first change involves passing the static model as an argument to the JDBC DMS Factory when a new mediator instance is created. The second change involves casting the result of the getGraph() call to the HWSystemRoot class that was just generated. The following code fragment demonstrates these changes based on the systemByHostname example in Listing 14.
Listing 14. Using static SDO packages
// Establish connection and connection wrapper
. . .
JDBCMediatorFactory mFactory = JDBCMediatorFactory.soleInstance;
JDBCMediator med = mFactory.createMediator(hwsystem,
HwsystemPackage.eINSTANCE.getHWSystemRoot(),wrapper);
DataObject args = med.getParameterDataObject();
args.setString("HOSTNAME", hostname);
HWSystemRoot system = (HWSystemRoot) med.getGraph(args);
. . .
// Commit and close the connection
|
From a performance perspective, it seems perfectly logical that the static APIs would have a significant impact on the performance of getters and setters for SDO properties. Since the static APIs tie a getter or setter method directly to the underlying SDO data, the mapping overhead associated with the dynamic API is avoided. Nevertheless, static models also improve the performance of the mediators getGraph() call. Figure 12 demonstrates the performance improvements associated with moving from dynamic to static SDO models for the getGraph() call.
Figure 12. Dynamic models versus static models
In all four of the test cases, use of static SDO models for the application queries resulted in performance improvements ranging from 13 percent to 28 percent. This improvement can be attributed to the fact that the mediator is now being passed the underlying EMF Ecore model directly and no longer has to derive the model from the metadata definition.
As previously mentioned, static SDO models have an even larger impact when accessing SDO data using the generated getter and setter methods. Figure 13 demonstrates the improvement in getter and setter performance that can be realized from static SDO models. For the getter test case, the performance was measured using the systemByHostname query since a single SDO object could be accessed multiple times and the same code path would be followed. However, the code path for a setter changes if a single SDO object is modified several times. Therefore, the allSystems query was used and each of the 100 objects returned by the query was modified before re-performing the allSystems query. This was the only way to guarantee that the same code path was followed for each setter on each iteration of the test loop.
Figure 13. Dynamic model versus static model for getters and setters
Figure 13 clearly shows the benefit of static models for SDO getter accesses, improving the performance by multiple orders of magnitude. By using static SDO models, an SDO property getter essentially becomes a POJO (plain old Java object) access. Due to the overhead associated with change tracking, SDO setters do not benefit as much as getters from static models. However, for this test case, an increase of 18.2 percent was measured over 100,000 iterations.
Note that this example shows the static SDO concept in relation to SDO 1.0. The support of static SDO is documented in the SDO 2.0 specification.
Other Considerations
Even though this article has specifically focused on read-oriented operations, some of the optimizations discussed will improve the performance of database insert and update operations as well. Usage of passive connection wrappers, pre-built metadata, and statically defined data models will improve JDBC DMS performance of write operations for the same reasons as in read-only operations.
Another item worth mentioning is the addition of referential integrity support (for example, ordering of update operations to satisfy referential integrity defined in the database schema) to the JDBC DMS in WebSphere® Application Server Version 6.0.2. In prior releases, the JDBC DMS did not support referential integrity constraints in the database. However, in this release, the feature is enabled by default, will negatively impact the performance of insert, and delete operations associated with the applyChanges() method. This impact is due to the additional logic needed to support the ordering of updates to maintain database referential integrity constraints. If your database schema does not establish referential integrity constraints, use the method provided in Listing 15 below to disable the ordering of operations by setting sortUpdates to false.
Listing 15. Setting sortUpdates to false
public void applyChanges(boolean sortUpdates,
DataObject datagraph) throws MediatorException;
|
Conclusion
In this series of articles, we explored usage of SDO and the JDBC DMS provided by WebSphere Application Server V6.0 in a typical user application. If you are thinking about using SDO and the JDBC DMS, be aware that SDO is best suited for predominantly read-oriented applications or read-write applications with low data concurrency that can support a disconnected data model. If your application requires a significant number of updates with high levels of data concurrency across multiple users, then SDO and the JDBC DMS might not be the right choice for you.
Using the data model from a simple Hardware Reservation system, we discussed the JDBC DMS and supporting metadata API and how they are used to perform complex queries against a relational database. Starting with a basic find all query, we discussed the following steps required to use the JDBC DMS to generate SDO objects based on relational information stored in the database. The standard process for this is to:
- Define the shape of the database using the mediator metadata API.
- Obtain a connection to the persistent data source using a connection wrapper.
- Define a JDBC DMS instance using the metadata and connection wrapper.
- Execute the query using the mediator's
getGraph() call.
- Close the connection to the data source.
We demonstrated how to perform more advanced queries against the database by creating filters and relationships on the JDBC DMS metadata.
After establishing the core concepts around the JDBC DMS and the support metadata API, we then discussed the following techniques for improving the efficiency SDO/JDBC DMS queries:
- Passive connection wrappers -- Passive connection wrappers allow you to manage the database commit and rollback operations associated with your database queries. For logical transactions involving multiple queries, passive connection wrappers can be used to perform a single commit or rollback for the entire transaction.
- Statically defined JDBC DMS metadata -- Instead of building the mediator metadata each time it is needed to perform a query, the metadata should be defined once and statically cached for faster access.
- Reduction of query results (using external tables) -- Particularly for finders involving related data from multiple tables, tables can be designated as external in the metadata to indicate that a table is needed to perform the query, but is not necessary in the resulting SDO data graph. An additional optimization is to remove columns in the external table definition that are not needed to generate the query.
- User-supplied SQL -- The JDBC DMS typically builds its SQL queries based on the metadata definition. In many cases, the mediator generates complex queries that can be simplified by removing over qualification of columns and/or using sub-selects instead of joins to support relationships between tables. The JDBC DMS allows you to override the generated SQL and supply your own SQL query.
- Statically defined data models -- Based on the metadata definition of your application data, a static (strongly typed) SDO model can be generated using Eclipse and / or Rational Application Developer to represent your SDO data models. We not only discussed the procedure used to generate static models using Rational Application Developer, but also demonstrated the associated performance improvements.
Even though these techniques might seem dramatically different in nature, they all share a few common themes. To obtain the best performance from SDO and the JDBC DMS, keep your metadata models as small as possible, ensure that the most efficient SQL queries are being used, and use static SDO models whenever possible.
Resources Learn
Get products and technologies
-
Get your hands on application development tools and middleware products from DB2, Lotus®, Rational, Tivoli®, and WebSphere. You can download evaluation versions of the products at no charge, or select the Linux® or Windows® version of developerWorks' Software Evaluation Kit.
Discuss
About the authors  | 
|  | Andrew Spyker is a team lead of the WebSphere Application Server Performance and Benchmarking organization within the WebSphere development organization. His current focus area is on programming models including J2EE and SDO, Web services including WS-Security and other evolving Web services standards, and how these areas fit into a Service-Oriented Architecture. Given this SOA focus, he also works to understand key areas of performance as it relates to all WebSphere products including, but not limited to, WebSphere Business Integration Server and WebSphere Portal Server. His performance work includes enabling more performant and scalable application server features and helping customers architect their current and future applications for best performance. Andrew has two and a half years of performance experience and over seven years of Java programming experience. He received his BS in Computer Engineer from Pennsylvania State University. |
 | 
|  | Christopher Blythe joined the WebSphere Application Server Performance and Benchmarking organization in Research Triangle Park, North Carolina after graduating from North Carolina State University with a Master's degree in computer engineering in 2001. In addition to his work on Service Data Objects, Christopher has also been highly involved with the SPECjAppServer and Trade performance benchmarks. |
Rate this page
|  |