How Does the Business Logic Tier Cache Data?

The business logic tier stores data it receives from a database and clients in two types of caches: an entity cache and a view cache. In this topic, we describe what you should know about the caches when you program with Business Components for Java.

Why does the business logic tier use caches?

The business logic tier caches business component instances in memory for three main reasons:

What classes make up the caches

In simple terms, the view cache stores view rows returned from database queries. The entity cache stores entity object instances.

An instance extending from oracle.jbo.server.EntityImpl represents one row in the entity cache.

An instance extending from oracle.jbo.server.EntityDefImpl is the container that holds instances of the EntityImpl class in the entity cache.

An instance extending from oracle.jbo.server.ViewRowImpl represents one row in the view cache.

An instance extending from oracle.jbo.server.ViewObjectImpl creates row sets that contain instances extending from the ViewRowImpl class.

Top-level application modules have their own caches

To use the business logic tier, a client creates a top-level application module instance (also called a root application module), which can contain view object instances, view link instances, and nested application module instances. Each top-level application module instance has its own caches and connection to the database (session). The instance also provides a transaction context for the view object instances it contains, including those in any nested application module instances. So the top-level application module instance is a container for view object instances and nested application module instances that share caches, a connection, and transactions.

View object queries populate caches

Each view object typically contains a SQL query. When the view object query executes, each row of the query's result set is cached in a view row:

By default, the view object does not fetch all rows in the result set at once, unless specified. For example, the first method will bring in the first row and leave the rest out of the cache. The next method brings in the next row, and so on.

Entity objects can bring data into the entity cache

If you look up an entire entity object with the findByPrimaryKey method, all of its attribute values are added to the entity cache. (It gives priority to data in the cache, and then brings in data from database if it's not already there.) Using a view object to look up data is more flexible, in that you can bring in any attributes that are relevant to the task and leave out those that are not. The view object executes a query, and if the attribute doesn't already contain data, it adds the data for that attribute.

Each queried database row is represented in the view cache

Each view object instance has its own view cache. After a query, each returned database row is represented by a row in the view cache. Each column is represented by a corresponding attribute in the row. Attributes that did not receive a value remain empty in the row.

One query can create multiple row sets

If a query returns multiple row sets because of a bind variable, multiple row sets can be created. For example, if the query is SELECT DEPTNO, DNAME, LOC FROM DEPT WHERE LOC=:1, and the bind values are SAN JOSE, CHICAGO, and NEW YORK, there can be three row sets, one for each location, in the view cache.

Entity attribute values are placed in the appropriate entity cache

Each entity object instance has its own entity cache. After a view object query, in each returned row, the values of view attributes that map to entity attributes are placed in the appropriate entity cache. Remember that view objects can map to more than one entity object. So different attributes in a row can end up in more than one entity cache. Each row in an entity cache is uniquely identified by its primary key. (So if you specify more than one entity for a view object, you must always include the primary key of each entity.) Attributes whose values were not populated by the query remain empty in the row.

Whenever a view attribute that is mapped to an entity object is retrieved or set, it delegates to the getter or setter in the entity object.

View object instances share entity caches

Each view object instance contained by a top-level application module (including nested application modules) shares entity caches with all other view object instances in the application module. This means that when shared data is modified in one view, all other views show the modification.

Queries are populated with cache data, if it exists

When a view object executes a query, the business logic tier uses modified data in the entity cache, if it exists. If an entity object instance has been modified, the query returns the entity cache's data instead of new data from the database. If the entity object instance does not exist or is unmodified, the query returns data from the database. This ensures that unposted changes are not overwritten in the business logic tier.The entity object checks its cached data against database data at locking time.

Data types are translated

The business logic tier uses Java data types, while the database uses SQL types. For an Oracle Object Type, a domain (a Java class) is used to represent it. Domains can be predefined in Business Components for Java or you can create your own custom domains. The business logic tier takes care of translating the data types between the database and business logic tier. For a list of predefined mappings, see Business Component Data Types. You can also specify custom mappings.

Domain validation occurs immediately

Validation placed in a domain validate method occurs immediately after a query. If an invalid value is returned from the database, and it does not pass domain validation, it needs to be corrected before data is placed in the entity or view cache. However, other types of validation are not triggered when data is brought in from the database, so if the database contains invalid data that is not caught by domain validation, this data can enter the caches.

Invalid data entered by a user cannot be placed in the caches

Invalid attribute values entered by a user do not pass to the view or entity caches. If a view attribute is based on an entity attribute, when a user enters a value in a form and it does not pass business logic validation (in the setAttribute, setAttribute, or validateEntity methods, validation rules, or composition), the value does not stay in the cache. After the user changes the value to a valid value, it can enter the entity cache. If you want to store invalid data, so it displays in the form, you need to store it in the client program.

View object caching can be prevented

You can use the setForwardOnly method in a view object to prevent data from entering the view cache. This is called "forward only mode." Only one row is worked with at a time, and the data cannot be changed. If you won't be changing data and there is not a lot of repeated data, using forward only mode can save memory resources and time, because fewer objects are created. Forward only mode is useful when you are fetching data and reading it once, such as formatting data for a web page, or for batch operations that proceed linearly. Note that if the view object is based on one or more entity objects, the data does pass to the entity cache in the normal manner, but no rows are added to the view cache.

View objects can assist entity objects

Sometimes you want to write entity object code that uses view objects, for example, because it is more efficient to use a SQL query than to write the same functionality in Java. Typically, these view objects are not part of any user interface.

Fault-in ensures that business logic has values it needs

If business logic needs an attribute value from a row, and the value hasn't been brought into an entity object or view cache through a query, fault-in occurs. All empty attributes in the row are populated from the database so that business logic can complete. As an optimization technique, you can prevent fault-in by making sure that all attributes needed by business logic are included in view object queries. This prevents extra SQL statements from being executed and creates a clean separation between entity objects and view objects. However, you may want to keep rarely accessed attributes out of the view object to conserve memory.

When all attributes are brought into the entity cache

There are four conditions under which all attributes of a row in the entity cache are brought in:

When rows are removed from a cache

When a view object or application module is destroyed, the associated view caches are also destroyed. You can also explicitly clear view caches by calling certain methods, including clearVOCaches on the application module level, clearEntityCache on the transaction level, and clearCache on the view object level.

When no view caches point to an instance in the entity cache, and the entity instance hasn't been modified, the instance becomes a candidate for clearing. When the rows are cleared depends on the Java VM you are using. The Java VM usually removes them when it needs more memory.

How rows in the database are locked

There are two types of locking: pessimistic and optimistic. Or you can choose not to use locking at all, which is typically not the best choice. You usually use optimistic locking when you are optimistic that two users will never modify the same row at the same time, due to the nature of your application. Pessimistic locking is the most conservative form of locking.

There are two circumstances when a lock needs to be acquired:

When the transaction associated with a top-level application module uses pessimistic locking (the default), and the row is an existing row in the database, the business logic tier attempts to lock a database row the first time one of its attributes is successfully modified, including passing any validation. (If the entity object is part of a composition, the framework first tries to lock the row for the top-level entity object in the composition.) If it can lock the row, the application module instance can modify the row. If the business logic tier cannot lock the row, it throws an exception and the value returns to what it was before the modification.

When the transaction of a top-level application module uses optimistic locking, at post time, the business logic tier attempts to lock each row corresponding to any deleted or modified entity object instances. (If the entity object is part of a composition, the framework first tries to lock the row associated with the top-level entity object.) If any of the attempts to lock are unsuccessful, an exception is thrown.

Locking is defined in the oracle.jbo.Transaction interface through the setLockingMode method. You can set the locking mode in the application module, view object, or entity object, but usually it's set in the application module. After an entity object is successfully modified in the entity cache (after it acquires a lock), all view object instances who have queried data for the row are notified so clients can refresh their displayed data.

After locking a row, the entity object verifies that the data retrieved from the database during the lock acquisition matches the unmodified data in the entity cache. If not, an oracle.jbo.RowInconsistException is thrown. This ensures that if another program in another database session has modified that row, that the application does not unknowingly overwrite that modification.

Entity objects post to the database

In general, the commit cycle follows these steps:

  1. The top-level application module has a transaction list. This application module directs each entity object instance to execute its doDML method to process its deletions, changes, and new data. For compositions, parents are modified first; otherwise, there is no guaranteed order that changes are processed.

  2. After the posts are complete, the transaction list cleared.

  3. The data is committed or rolled back in the database.

  4. The locks are released.

The order that you insert rows in a database matters for associations. For example, if you create employee and then create the department housing employee, you'll get a database error. For compositions, the framework takes care of posting changes to the parents first.

Only valid rows are posted. The entity object makes sure each row is valid before posting.


Related topics
About Validation Logic
What Is an Entity Object?
What is a View Object?