Performance Best Practices


In this section, we turn our attention to application design and configuration best practices that directly affect the performance of your WebLogic Server system. We ll start by reviewing a few design patterns that can improve performance, then present a series of Web Container and EJB Container best practices, and finish up with some best practices related to database access.

Designing for Performance

Good application performance starts with good application design. Overly complex or poorly designed applications will perform poorly regardless of the system-level tuning and best practices you employ to improve performance. Entire books have been dedicated to good application design, and we have only part of one chapter in this book, so we must limit our discussion to a few key principles and best practices. There are many books on J2EE design patterns and anti-patterns ; two of our favorites are Bitter Java by Bruce A. Tate (Manning Publications, 2002) and EJB Design Patterns: Advanced Patterns, Processes, and Idioms by Floyd Marinescu (Wiley, 2002).

Design patterns are an important topic in the J2EE development community. Proper use of design patterns can provide significant benefits in the maintainability of application code through standardizing the approach to common design problems. Certain design patterns are also beneficial to performance because they reduce the overhead associated with transactions and inter-component messaging. Let s look at three of the more important patterns for J2EE applications. For a more extensive overview of well-known J2EE design patterns, take a look at Core J2EE Patterns: Best Practices and Design Strategies by Deepak Alur et al. (Prentice Hall, 2001).

Session Fa §ade

The session fa §ade pattern is a high-level wrapper for server-side components and was discussed at some length in Chapter 7. J2EE applications commonly implement this pattern by creating a stateless session bean that wraps business logic and the entity beans, database interactions, and Java objects required to perform the business logic. The bigrez.com application makes use of this pattern for key business services such as reservation creation and the rate and availability search processes.

Session fa §ades improve application performance by exposing only high-level business operations and reducing the individual client invocations to components such as entity beans. This is especially important when accessing EJB components by remote interfaces, as the network and marshalling overhead for each remote call is substantial. Even with local interfaces, there is some overhead because the EJB container provides security, life-cycle management, and transaction control for each invocation. Chapter 7 presents an argument that local interfaces and client-controlled transactions reduce this overhead and make direct interaction with entity bean components more palatable, but the principle remains: reduce the number of invocations that require network, data marshalling, life-cycle, or transactional services to improve application performance. Session fa §ades are one good way to follow this principle.

Best Practice  

Reduce the number of remote calls or other high-overhead invocations in your system to improve performance. Stateless session beans implementing the session fa §ade pattern are one good way to reduce the number of these types of invocations.

Value Object

Another common and important mechanism available to reduce the need for remote invocations is the value object pattern. Creating a separate data object that contains all of the fields within an entity bean allows a single client invocation to retrieve all attribute data from an entity bean. These value objects also allow you to create and populate with information in the EJB client, which may be a stand-alone Java client, a servlet, or even another EJB. This works because the value objects are simply serializable Java objects rather than EJB components. Passing the entire object from an EJB client to a session bean or entity bean eliminates to invoke multiple setter methods from the client.

You can also use HashMap objects and other collections to pass information to the entity bean. The advantage of using these types of collection objects is that they are not tightly coupled to the entity bean interface. Entity bean attribute changes are easier to handle, and older clients can still work with the entity bean. This flexibility, though, comes at the cost of losing some compile-time checking of the client s use of the entity bean interface and therefore requires more run-time checking in the entity bean itself. Many times, it is simply better to use a value object that provides some parameter validation checks so that the entity bean does not have to handle validation.

The bigrez.com application uses value objects for key business data, such as reservation information. We did this for two reasons. First, this provides a single data structure to use when communicating with the reservation session bean. Second, because value objects are Serializable , their use allows us to store data in the HttpSession .

Command Pattern

The command pattern uses a single command object to encapsulate multiple steps or requests that are passed to the server for processing with a single invocation. Use this pattern by itself or in conjunction with the session fa §ade pattern to further reduce individual component remote invocations. Systems that contain many business interfaces implemented as fa §ades often use this pattern to create additional higher-level interfaces without creating additional session beans.

The command pattern resembles the business delegate pattern in that both patterns create a higher-level interface for multiple business services. The command pattern processes the logic within the command class on the server, while the business delegate pattern uses the client tier . In other words, while business delegates can simplify the interfaces to back-end systems by providing a higher-level interface, they generally execute on the client side of the application and create multiple invocations to back-end services. The command pattern may therefore be a better choice if performance is a key requirement for the design.

Best Practice  

Create objects that implement the command pattern to further reduce the number of remote calls or other high-overhead invocations in your system. When using remote clients, favor command objects over business delegates for better performance.

Understanding Web Container Best Practices

Many high-volume J2EE applications service hundreds, if not thousands, of page requests per second. Small improvements in performance within the Web Container can therefore lead to big improvements in overall system throughput and scalability. This section touches on a few important best practices related to servlets and JavaServer Pages (JSPs), the core components in most Web applications.

Session Management

As discussed in Chapter 1, many applications supplement the stateless HTTP protocol by storing session-specific data in the HttpSession . This session data will then be available during subsequent requests from the same browser session. In a reliable application, this session data must be available even if the server originally used to service the HTTP request fails or is shut down. WebLogic Server provides two options for session persistence:

In-memory replication.    Session data is kept in memory on the primary server assigned to this session and replicated to a backup server in the cluster for failover.

JDBC-based persistence.    Session data is stored in a special database table and read in to memory by the server processing an HTTP request.

In-memory replication is much faster that JDBC-based persistence and should be used when it meets your requirements. Recognize, however, that there is a cost associated with session persistence regardless of the persistence option chosen . When a Web application updates the HttpSession object, WebLogic Server must save these changes at the end of the HTTP request. The quantity of data the server needs to save depends on the structure of the data in the HttpSession . WebLogic Server uses the HttpSession.setAttribute() method to detect when changes to the session occur. At the end of each request, WebLogic Server saves each new or changed attribute value. This means that WebLogic Server will save the entire object graph bound into the attribute that changed. If your Web application updates only one or a few fields in a large object structure, you should consider breaking this large object structure up into smaller objects that are bound into different attributes in the session to reduce the amount of data the server has to save at the end of any particular request.

Best Practice  

Avoid placing all session data in a single large object in the HttpSession . Use in-memory replication for session persistence unless you need JDBC-based persistence to meet your requirements.

HttpSession objects also use system resources, so it is important to clean them up when you finish using them. WebLogic Server releases HttpSession objects in two ways:

Session time-outs.    WebLogic Server will remove an HttpSession object after a period of inactivity, known as a session time-out . Session time-outs can be set using either the session-descriptor element s TimeoutSecs attribute in the weblogic.xml deployment descriptor or the session-timeout element in the web.xml descriptor. See the WebLogic Server documentation at http:// edocs.bea.com/wls/docs81/webapp/sessions.html for more information.

Explicit invalidation .    You can programmatically remove a session by calling the invalidate() method on the HttpSession object.

Too often, Web application programmers abuse the session object. You should carefully consider what you store in session objects. Make sure that you understand all of the other available options and the performance trade-offs associated with using sessions. The following list represents a set of best practices related to sessions.

  • Avoid using the session to pass data between Web application components such as servlet controllers and JSP pages during the processing of a single HTTP request. You should use the HttpServletRequest object for passing data needed when forwarding or including other Web application components.

  • Consider other alternatives when state can easily be maintained or derived. You can often store the state in local variables , the database, or client-side cookies.

  • Keep your session objects as small as possible by always removing objects from the session that you no longer need with the session s removeAttribute() method.

  • Understand your business requirements for session time-outs. Always make sure that you tune the session time-out interval to the smallest value consistent with your business requirements. This will prevent users from tying up valuable resources when they fail to exit the system when finished. WebLogic Server uses a default value of 3,600 seconds, or 1 hour . A value of 10 or 15 minutes is usually sufficient for most Web applications.

JavaServer Pages

Chapter 1 presented a number of the best practices related to JavaServer Pages. Here, we highlight two areas of particular importance to performance.

Using Sessions Efficiently

JavaServer Pages (JSPs) create HttpSession objects by default, as required by the JSP specification. If your pages are not using sessions, turning the default behavior off can enhance performance. Include the following JSP page directive to avoid creating unnecessary HttpSession objects:

 <%@ page session=false%> 
Using Dynamic Content Caching

Chapter 1 presented the wl:cache custom tag and the CacheFilter servlet filter, two powerful techniques available for dynamic content caching in WebLogic Server. When determining what content to cache you should consider the following questions.

  • How often is the content requested ?

  • How often does the content change?

  • How expensive is it to create or calculate the content with each request?

Content items that change infrequently, such as headers or footers on a portal page, are excellent candidates. Additionally, repetitive database queries that are returning data that changes infrequently also offer the opportunity for performance gains from caching.

Be careful not to go overboard with caching, however, as it consumes memory on the server and there is currently no mechanism to limit the size of the response cache unless you are using keys . The key parameter in the wl:cache tag makes it very easy to cache response data for different key values. Be sure to set the size attribute to control the size of the cache when the number of possible key values is large enough to create a very large cache of responses.

Best Practice  

Use dynamic content caching when content is frequently displayed, is infrequently changed, and represents a fair amount of work to create with each request. Limit the size of the cache to avoid competing for heap space with other components.

As an example of appropriate caching, our bigrez.com application uses the wl:cache tag to cache the offers displayed in the left gutter of every page for a particular set of search criteria. Recalling the three questions presented previously, the offers are displayed every page hit, the offers associated with specific search criteria are relatively static, and the algorithm used to choose and prioritize offers based on the criteria is expensive. The offers display is therefore a perfect candidate for caching.

Servlets

Avoid using the SingleThreadModel for servlets. As discussed in Chapter 1, Web-Logic Server creates a pool of servlet instances for each servlet that implements the SingleThreadModel interface. Its handling of the instance pool is efficient, but the pool does create some overhead for the container and should be avoided to achieve greater performance. If you do need to use the SingleThreadModel , you should set the pool size large enough to avoid the on-the-fly creation and removal of additional servlet instances.

Use the servlet s init() method to perform expensive operations that need to take place only once. Because the Web container calls the HttpServlet.init() method right after loading the servlet, it is an excellent place to invoke heavyweight operations that you want to perform only once.

In many cases, it is possible to speed up the writing of the output stream by using a ServletOutputStream object instead of a PrintWriter . PrintWriter performs character-set conversion; therefore, you should use it only when your requirements demand it. For cases where your servlet returns only ASCII or binary data, use a ServletOutputStream to get better performance.

Understanding EJB Container Best Practices

High-volume J2EE applications often use Enterprise JavaBean (EJB) components to provide transaction, security, and object-life-cycle services for key business processes and objects. The performance of EJB components can have a dramatic effect on overall system performance because they tend to perform much more processing per request than a Web component. This section highlights a number of best practices for improving the performance of your EJB components in a WebLogic Server environment.

JNDI Lookup Strategies

J2EE applications use the Java Naming and Directory Interface (JNDI) to locate many different types of resources. Excessive calls to JNDI to look up EJB home interfaces, DataSource references, and JMS connection factories and destinations can reduce system performance significantly. To minimize the overhead of these lookup operations, you should cache and reuse the objects. Several techniques are available for caching these objects; we will discuss two of them.

For many simple Web applications, you can simply look up the objects in the servlet s init() method and cache them in instance variables in the servlet. As long as the objects themselves are thread-safe, using instance variables in this way is also thread-safe because your servlet probably uses these objects to create other objects. For example, you use an EJB home object to create or look up references to EJBs and a DataSource to get a database connection from the JDBC connection pool.

A more elegant and useful approach requires a helper or factory class that you can use throughout your application to locate and cache these types of objects. You can create a separate locator class for each type of object, or create one general-purpose locator capable of finding and caching all types of objects. Our bigrez.com application uses a general-purpose helper class, called Locator , to find and cache all EJB home interfaces. This class not only provides home interfaces using a simple interface, get-Home() , but also employs reflection to locate and return EJB bean instances with a single invocation:

 RateHomeLocal ratehome =      (RateHomeLocal)  Locator.getHome("RateHomeLocal");  // get a home PropertyLocal property =     (PropertyLocal)  Locator.getBean("PropertyLocal", 57);  // get a bean 

By having your application locate all home interfaces and bean instances through this class, you can maintain and age a single consolidated cache of home interfaces. See the downloadable source code for Locator.java and CacheMan.java for more information.

Best Practice  

Use caching to reduce the number of JNDI lookup operations for EJB home interfaces, DataSource objects, and JMS connection factories and destinations. Favoring a centralized locator class will help you consolidate lookup and caching operations in a single place and is better than using per-bean or per-servlet caching.

One caveat on caching worth noting is that the use of JNDI lookup caching can result in the client having a stale reference if the server on which the object resides shuts down. This is typically an issue only if the client using the object and the object are in separate processes. For example, stand-alone clients can avoid the problem of EJB home interface caching by deploying the EJBs to a WebLogic Server cluster. In a WebLogic Server cluster, each server in the cluster has a copy of the home object and the reference bound into JNDI is cluster-aware; that is, it knows about all of the copies of the objects in all of the servers. Should one server fail, the client-side stub can route calls around the failed server to another server with an equivalent component.

Optimizing Entity Beans

As the J2EE platform continues to mature, many companies are beginning to use entity beans as a mechanism to represent business objects in their applications. Unfortunately, entity beans have a reputation for poor performance, a reputation based largely on results from poorly designed applications using either bean-managed persistence (BMP) or earlier versions of container-managed persistence (CMP). Can you create a high-performance system that includes a heavy dose of entity beans? We believe you can, provided you understand the life cycle of an entity bean and use the right features in WebLogic Server to optimize and tune your entity beans according to how your application uses them.

First, make sure you take advantage of reference caching whenever possible. If your bean needs to look up references from JNDI and you are not using a single, centralized cache, you should do these lookups in the setEntityContext() method. Avoid looking up references in other methods, such as ejbLoad() and ejbStore(), because these methods are called more frequently than setEntityContext() . We still recommend that you use a centralized cache rather than a per-bean cache.

Second, you should always try to use CMP whenever possible. CMP gives the container more control and more visibility into the bean s attributes, relationships, and state; this allows WebLogic Server to optimize the container s behavior in ways that it simply cannot do with BMP beans. Container-managed relationships (CMR) are yet another reason to use EJB 2.0 CMP entity beans. Chapter 6 highlighted many of the important WebLogic Server EJB features related to CMP entity beans, including caching capabilities, finder optimizations, and preloading or lazy loading of attributes and relationships to improve performance. Although a detailed discussion of all CMP entity-bean optimizations available in WebLogic Server is beyond the scope of this chapter, a number of best practices stand out from the rest:

  • As discussed in Chapter 6, always set finders -load-bean to true to ensure that bean data is retrieved during finder methods, thereby avoiding the n + 1 query problem .

  • Avoid using finder methods that can return very large sets of data. While finders-load-bean can return the results in a single query, returning very large numbers of objects can fill up the bean cache, forcing other, more frequently used beans out of the cache.

  • Use optimistic concurrency and set cache-between-transactions to true to take advantage of the automatic, cluster-aware caching built in to WebLogic Server, a feature that can improve performance dramatically.

  • When an application reads an entity bean s data much more often than it modifies it, consider using read-only entity beans as a form of cache. While read-only beans have been largely superceded by the cache-between-transactions approach, the ability to set automatic time-outs and invalidate cached data programmatically provides a unique benefit for read-only beans.

  • When implementing relationships in CMP beans, make use of relationship caching to fetch related beans automatically in a single finder query where it makes sense. If your application tends always to traverse the relationship when interacting with a particular bean type, relationship caching will eliminate lazy fetching of the related beans, a subtle but insidious performance issue not unlike the n + 1 query problem. Be aware that WebLogic Server does SQL joins to pull all of the data into the container in a single query. If your entity beans have many relationships with one another, relationship caching can lead to extremely complex join queries that can kill the performance of your database. Use relationship caching where it improves the efficiency of the application.

  • Use field groups on beans having many attributes when common access patterns require only a subset of the bean attributes. Finders used to display lists of summary bean data are also good candidates for field groups. You should realize that partial loading of data can lead to data inconsistency in your entity bean because each field group might be loaded by a separate transaction. Whether this is a problem really depends on your application semantics.

  • Avoid exclusive locking unless absolutely required by system requirements. Exclusive locking imposes a large burden on the EJB container in terms of locking overhead and the opportunity for queuing and blocking during bean access. Also, you should realize that WebLogic Server currently supports exclusive locking only within a single-server instance. Any concurrent calls for the same bean instance in different servers in the cluster will effectively be using database concurrency.

    Best Practice  

    Optimize entity beans in your application by favoring CMP persistence and leveraging all of the server optimizations available. WebLogic Server s cache-between-transactions and optimistic concurrency are very powerful features that you should consider for the most active entity beans in your system.

Finally, regardless of the persistence mechanism you have chosen for your entity beans, it is important to tune the size and behavior of the bean cache to reduce passivation overhead. Recalling the discussion in Chapter 6, there are two basic types of entity-bean cache, a per-bean cache and a combined cache :

  • A per-bean cache ensures that a fixed number of cache entries are available for that bean at all times. It can be difficult to set these per-bean cache sizes properly unless you have good metrics for required cache sizes. If you go this route, start with the default value of 1000 and examine the entity-bean monitoring display in the WebLogic Administration Console to identify beans that are exceeding that limit on a routine basis for a possible increase in their per-bean cache size.

  • A combined cache sets aside a single large cache for use by all entity beans configured to use it. Multiple combined caches can be configured to host different sets of beans, and you can set the size of the combined cache outside the EJB descriptor, making it somewhat easier to manage and modify. The downside of this option is the lack of separation between beans in the cache. There is no guarantee that any given bean will have a certain number of entries in the cache at all times. When the cache fills up the server may begin passivating beans that would be better left in the cache.

Recognize that the use of cache-between-transactions and read-only entity beans also has a dramatic effect on the bean cache size required for best performance. Under normal circumstances, with database concurrency and no caching, the bean cache need be sized only to store the maximum number of concurrent bean instances likely in the system. For example, there might be 10,000 customers, but if only 100 “200 of them are accessed at any given instant a bean cache of 500 would certainly be sufficient to avoid passivation. If the customer entity bean was set to cache using cache-between-transactions , each time a new customer is used in the application the bean instance for that customer will be cached for future use. It is possible that over time all 10,000 customers will be loaded by the container, and only a bean cache exceeding that level would avoid passivation.

Best Practice  

Choosing the right type of bean cache and setting proper cache sizes is a complex, iterative process best done during load testing by monitoring bean cache usage and passivation. Optimal cache sizes are generally much larger when using entity bean caching features.

Using Appropriate Isolation Levels

Isolation is one of the four key properties of transactions: atomicity , consistency , isolation , and durability (ACID). This means that even if there are concurrent transactions occurring, the database must execute the transactions as if they are occurring one at a time. Isolation levels refer to the degree to which multiple interleaved transactions are isolated from one another. Databases use various mechanisms that control the reading and writing of transactional data to achieve transaction isolation. There are four conceptual isolation levels available for EJB components; which ones are available to your application depends on your JDBC driver and the database management system. These levels are as follows :

  • TRANSACTION_SERIALIZABLE is the highest isolation level offering the best protection, but it is also the slowest because all requests are executed serially . Using this isolation level will cause methods to obtain exclusive write locks on data. This prevents other transactions from reading, updating, or inserting data until the transaction is complete.

  • TRANSACTION_REPEATABLE_READ prevents transactions from changing data that is currently being read by another transaction.

  • TRANSACTION_READ_COMMITTED prevents a write lock from being obtained on data that is currently being changed by another transaction, so reading uncommitted data is not possible.

  • TRANSACTION_READ_UNCOMMITTED allows transactions to read uncommitted data and to be unaware when new records are added to a result set.

Higher isolation levels typically cause slower performance but provide higher degrees of safety with fewer potential data-inconsistency problems. Lower levels of isolation offer higher concurrency and better performance with a higher potential for data-inconsistency problems. Once isolation levels are understood they can be used to provide the proper level of concurrency for all deployed EJB components.

When choosing an isolation level, it is important to understand what levels your database management system supports and what the implications are for using them with your particular database. For example, Oracle uses the read committed isolation level internally. To support the serializable level, Oracle needs extra bookkeeping space and can throw errors when it is unable to serialize transactions. It is best to work with your DBA to choose an isolation level that is best for both your database and your application requirements.

Best Practice  

Review isolation levels for deployed EJB components and reduce isolation levels where appropriate. Work with your DBA to choose the right isolation levels that work with your database.

Taking Advantage of Pass-by-Reference

Enterprise JavaBeans are designed to provide location transparency : The caller may call the bean as if it coexists in the same JVM when it may actually be located on a completely different server. EJB components use Remote Method Invocation (RMI) to enable location transparency, but it comes with a cost. RMI uses serialization to pass method parameters and return values using a pass- by-value semantic ”even if the caller and the bean are in the same JVM.

This marshalling and unmarshalling of data is expensive but necessary when the caller and EJB are in two different processes. It is also necessary if the caller and the EJB are in the same process but loaded using different classloaders. If both the caller and the EJB are in the same JVM and loaded with the same class-loader, it is possible to skip the marshalling and unmarshalling and pass the arguments and return values using pass-by-reference semantics. In many applications, the functional behavior of the application won t change by switching to pass-by-reference semantics; however, certain coding styles and practices can cause functional change. This is why the EJB specification requires the use of pass-by-value semantics for all EJB calls using the EJB s home and remote interface. EJB 2.0 adds support for local interfaces that support pass-by-reference semantics.

This pass-by-reference optimization is available only between components within an enterprise application. Separate applications running in the same JVM use separate class-loaders, so attempts to pass data by reference would invariably result in a Class CastException . WebLogic Server uses serialization in these cases. Don t forget that passing data by reference allows the called method to modify the contents of the passed object, in most cases, providing either a powerful feature or a debugging nightmare depending on how you use it.

Best Practice  

Deploy related components together in the same enterprise application to allow pass-by-reference semantics and maximize performance.

Historically, WebLogic Server has optimized calls made to EJB components within the same application by passing parameters and return values by reference , rather than by value , to improve performance. Because of J2EE licensing requirements imposed by Sun, WebLogic Server 8.1 changes the default behavior to always use pass-by-value semantics when using WebLogic Server 8.1 deployment descriptors. This change will likely cause WebLogic Server users a lot of pain and frustration. Therefore, we want to emphasize that in order to take advantage of this performance optimization, you now need to enable the optimization explicitly in the weblogic-ejb-jar.xml deployment descriptor for every EJB for which you want callers to be able to use pass-by-reference semantics with the EJB s remote interface. This example shows how to enable it:

 <weblogic-enterprise-bean>     <ejb-name>CustomerBean</ejb-name>   <  enable-call-by-reference  >  true  <  /enable-call-by-reference  > </weblogic-enterprise-bean> 
Warning  

WebLogic Server 8.1 changes the default behavior for its enable-call-by-reference optimization. If you are using WebLogic Server 8.1 deployment descriptors, the new default value is false, to make WebLogic Server J2EE compatible out of the box. This is new behavior and will cause the performance of existing applications to degrade when using WebLogic Server 8.1 unless you explicitly enable the optimization in your EJB deployment descriptors.

Applying Database Access Best Practices

Efficient database access is critical to achieving high throughput and good scalability for your system. All of the other tuning and optimizations you perform, whether in the JVM, Web Container, EJB Container, or elsewhere, will be for naught if the database access in the application is slow. Worse yet, the techniques available for increasing database processing capability through hardware upgrades are generally more expensive than the simple clustering techniques available for increasing the processors available for the Web or EJB tiers of the application. You simply must start with efficient database access to achieve good overall performance.

This section will provide a number of best practices related to database access from within J2EE applications. This section is not intended to provide a complete treatment of database design or performance tuning. For more information about database performance tuning for Oracle, we recommend Oracle 9i High-Performance Tuning with STATPACK by Donald K. Burleson (McGraw-Hill Osborne Media, 2002). There are numerous books on database design, such as the classic An Introduction to Database Systems, 7th Edition by C. J. Date (Addison-Wesley, 2000) and Case*Method: Entity-Relationship Modelling by Richard Barker (Addison-Wesley, 1990).

Basic Database Principles

First, you need an efficient logical database design. Database designers tend to favor highly normalized designs requiring multi-way joins to fetch a typical business object and related data. A design that looks good in the data-modeling tool may become a real bottleneck in production. If performance is a key criterion for your system, it is important to push back in this area and work with your database designer to flatten or denormalize some critical tables.

Next , the physical database design must reflect the performance requirements of your application. Your DBA should be employing all of the optimization features available in the chosen database technology to achieve the best possible performance. Make sure to provide your DBA with a complete list of the queries your application uses, including all columns appearing in the WHERE and ORDER BY clauses, so that he or she can create the proper indexes on each table.

After creating and tuning your database, you still need to use efficient database-access coding techniques to ensure a high-performance application. Some basic rules of thumb include these:

  • Always perform table access using a good, selective index. Work with your database administrator to view statistics on your queries and correct any inefficient database access.

  • Avoid joining multiple tables unless your application logic requires it. This often means creating multiple versions of a query, one that avoids a join and fetches only limited data for each matching object and one that joins with related tables and fetches a fully- populated object.

  • Avoid using queries that return extremely large result sets. This becomes even more important when using an ORDER BY clause that does not use the same index that the query s WHERE clause uses. When this happens, the database must order the rows manually ”a very time-consuming process.

  • Cache data when the access pattern is mostly read and the hit frequency is high.

Retrieve Columns Explicitly

Use explicit column lists in queries rather than selecting all columns from a table using the SELECT * syntax. Explicitly retrieving only the needed columns avoids internal JDBC operations and reduces the amount of data transferred back to the application, improving query performance. Of course, the other benefit is that your code doesn t break if the table is altered to add new columns.

Cache Prepared Statements

Use prepared statements for database access if your application executes the same SQL statement repeatedly. The first time you execute a prepared statement the database must spend extra time parsing and compiling the statement before it can be executed. Most relational databases will then cache the statement and match new statements against the cached ones to avoid this performance penalty. Keep in mind that in order for the database to reuse a cached statement, the new statement must match the old statement exactly in all aspects except the values of bind variables.

WebLogic Server supplements this database-level caching with its own JDBC PreparedStatement cache built into its connection pooling support. Whenever your application calls prepareStatement() for a new statement, WebLogic Server will cache the PreparedStatement object returned by the JDBC connection for use during subsequent prepareStatement() requests made using this same JDBC connection. Cached statements are specific to a particular connection because that is the model that JDBC imposes; therefore, frequently used queries will likely have a cached statement per connection. Reusing JDBC PreparedStatement objects eliminates the need for parsing statements in the database, which reduces CPU usage on the database machine, improving performance for the current statement and leaving CPU cycles for other tasks . You configure a connection pool s prepared statement cache using the WebLogic Console.

Like most tuning operations, determining the proper size of the prepared statement cache is an iterative process. In general, the more prepared statements your application employs, the larger the cache should be. For example, if the application has 20 SQL statements, setting the pool s prepared statement cache size to 20 will allow WebLogic Server to cache all of the prepared statements since each pool connection has its own cache. One empirical, iterative approach for sizing the cache involves monitoring the SQL parse operations per second occurring in the database during a realistic load test. Continue increasing the size of the prepared statement cache until the number of parse operations per second stops decreasing , representing a point of diminishing returns.

Transaction Model

WebLogic Server supports both the local and distributed JTA transactions. Distributed transactions are transactions that span either multiple database connections or multiple resources. Distributed transactions require additional logging and extra network I/O, making them many times slower than local transactions. Whenever possible, use local transactions involving a single Connection object for the best performance.

As we discussed in Chapter 11, there is a three-way relationship between transaction models, JDBC DataSource settings, and the underlying JDBC driver. In most circumstances, you want to use a JTA-aware DataSource . To create a JTA-aware DataSource , you just need to make sure that the Honors Global Transactions checkbox is checked when you create the DataSource using the WebLogic Console. In previous releases of WebLogic Server, you had to create a TxDataSource in order for the DataSource to be JTA-aware.

It is very common to have multiple EJB components involved in the same unit of work. The only way to accomplish this without using an XA-compliant driver is to have all participants share the same database connection. WebLogic Server will automatically and transparently make sure that all operations use the same connection when your application is using a JTA-aware DataSource with a non-XA JDBC driver. You will need to use an XA-compliant JDBC driver if your applications use transactions that span multiple resources, such as a database and JMS or two databases. If a particular transaction involves only a single resource, WebLogic Server s JTA transaction manager will optimize the transaction to use a single-phase commit instead of the more expensive two-phase commit.

Best Practice  

Use a JTA-aware DataSource to ensure proper transaction coordination when using EJB components or involving multiple resources in a transaction. When doing distributed transactions, you will need to use an XA-compliant JDBC driver.

Commitment Control Level

JDBC connections use a commit control level of TRANSACTION_NONE with the autoCommit attribute set to true by default. These are the correct settings when an application does not need to use transactions, such as when an application invokes a stored procedure or trigger that does run under transaction commitment control. These are unusual circumstances, though; in most cases, you should set autoCommit to false to increase performance by reducing the number of commit operations. When using non-JTA-aware DataSource objects, you should explicitly call Connection.set_AutoCommit(false) before executing your queries and then call Connection_.commit() at the end of the transaction, as shown here.

 DataSource ds = (DataSource)ctx.lookup(java:comp/env/jdbc/TestDB); conn = ds.getConnection(); conn.setAutoCommit(false); ... conn.commit(); 

If you are using a JTA-aware DataSource , all connections will already have autoCommit set to false . Any attempt to invoke setAutoCommit(true) on the connection will throw a SQLException .

Best Practice  

Configure commit control properly on JDBC connections obtained from a non-JTA-aware DataSource by calling setAutoCommit(false) before executing any queries and commit() at the end of each transaction to improve performance and avoid committing after every statement.

Batch Updates

A batch update refers to a set of SQL statements submitted as a unit for processing. Sending multiple statements together can be much more efficient than sending each update separately. Always set autoCommit to false or obtain the connection from a JTA-aware DataSource when doing batch updates to avoid committing automatically when calling executeBatch() . The database will execute the SQL statements in the order they were added to the batch. The following code block illustrates this technique.

 DataSource ds = (DataSource)ctx.lookup(java:comp/env/jdbc/TestDB); Connection conn = ds.getConnection(); Statement stmt = conn.createStatement(); stmt.addBatch(INSERT INTO EMPLOYEE  +               VALUES (500, 


Mastering BEA WebLogic Server. Best Practices for Building and Deploying J2EE Applications
Mastering BEA WebLogic Server: Best Practices for Building and Deploying J2EE Applications
ISBN: 047128128X
EAN: 2147483647
Year: 2003
Pages: 125

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net