[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13. Performance considerations

However easy it is to implement a persistence layer with BeanKeeper, still, special care may need to be exercised to create an application which has the right performance. There are a few rules which generally help to design your object model, and your application:

What is the metric of performance? It is really hard to measure the performance of an ORM (Object/Relation Mapping) software, because there is no definite (standard) set of operations or situations you could think of, which represents real-world usage of all possible applications. That's why, you probably have to think about the possible usage patterns of your specific application, and determine which functions or operation combinations you would like to compare.

The following sections describe the operations of BeanKeeper, and their usual performance with the number of sql operations it has to execute to complete said operation.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.1 Save operations

To save() an object, the following operations occur:

The current version of the object is read from the database, using one select operation, if the object exists and it seems it was modified but too long ago for the library to remember, or from a different node.

All changed non-container type attributes are saved using one save and one insert operation, as many times as many sub-classes are changed. This means, a simple class (with no subclasses, and only primitive attributes) is usually saved with 1 update and 1 insert operation.

Saving container type (Collection,Set,List,Map) attributes is described in a separate chapter.

If the object is related to a non-existing object, either directly through an attribute reference, or through a container type, that object will be saved too.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.2 Remove

The remove() method executes exactly as many update operations, as the number of subclasses the object has. Subclasses inside the java.* package are not considered real (persisted) subclasses, so those, and interface types don't count.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.3 Transactioning

All transactions have possible overhead sql executions, if there was some save() or remove() operations inside the transaction. It is worth to note, that if you don't explicitly define a transaction, those operations still open their own transaction to run in.

When commit()-ing a transaction, it has to execute an update statement for all modified tables. If none were modified, then none will be executed.

This also means, if you have multiple save() or remove() operations in a batch operation, it is recommended, that you execute all in a single transaction. This way, there will be only a single transaction overhead, as opposed to as many transaction overheads, as many save() or remove() calls you make.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.4 The "find" query

To compute the performance of a query operation is not a simple task. Generally, simple queries will cause less physical statements to run, and complex (not usual) queries will cause more select statements to be executed.

The first thing to consider, is that the library takes care of result list paging, and all figures below apply to a single page. A page is by default 30 rows/objects, which may grow to as much as 2500 (by default), if linear iteration is detected. So the first thing you do, is compute the number of pages in which you get the data, and you must muliply the below numbers with the number of pages.

In a simple case, when the object you find() has only primitive attributes, there will be exactly one select statement executed.

All container type attributes will cause 0 physical statements on loading, because they only start to work, when first used.

There will be exactly one select statement for all non-primitive, non-container type attributes however, in all reference depths! This means, that first, we must determine the reference depth for an object. If the object only contains primitive attributes, the reference depth is 0. If the object refers directly to other objects, but those object do not refer to anything, then the reference depth is 1. If those refer again to other objects, then 2, etc. On each level of reference, count the number of attributes which refer to other objects, and add them for all depths to compute the total number of selects the library need to execute to load the whole reference tree.

For example if a Book refers to a single Author, and the Author refers to a single Address object, and a single Contact object, then selecting Books will result in a reference tree, which has 2 levels. On the first depth it has 1 attribute of reference (book.author), on the second, it has 2 (author.address, author.contact). So the total number of selects to load a page of books is 1 select (for the book) + 1+2 selects to load the reference tree (4 total).

And the above is only the simplified version. If the loaded object is not a specific (persisted) class, but an interface type, or an object from the java.* hierarchy (for example java.lang.Object), then the objects are not loaded with one select. In this case, usually there is one select to determine which type matches the select anyway, and there is one select, for each persisted (normal) class the library has to load. In this case the above equations will have to be multiplied by this number. So if you select for a java.lang.Comparable, and there are 3 of your classes implementing this interface, then 3 selects will be executed for this query operation.

If the a class hierarchy is big, so the number of subclasses (and superclasses) for a class exceed a limit (by default 16), then the loading will again become more complex. In this case, the page number will be limited to 16, and again one select is required to determine which exact classes will be on the page, and one select, for each class.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.5 "View" queries

All view queries are exactly one select. Relations will not be resolved, container types will not be loaded, only primitive attributes will be usable.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.6 Containers

All container types use standard queries to load. This means, all conform to the loading performance described in the previous chapters. So if all items in a container are of the same class, and this class contains only primitive attributes, then the loading takes one select per page. If they are more complex, then the loading will be more complex too.

In general, containers are saved when the parent object is saved. So until then modification operations rarely need a physical statement to execute. The following tables describe the performance characteristics of the given container type:


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.6.1 List

A list is an ordered item container, which can hold duplications of the same item.

add

0 sql operations, but may need the items near the inserted index. If that's not on the current page, then loading may occur.

addAll

An add operation for each item in the collection given.

clear

0 sql

contains

1 sql operation.

containsAll

A contains operation for each item in the collection given.

equals

Potential linear iteration on list and operand too.

get

Similar to LazyList. If the requested item is on the current page, then no loading occurs. Else the required page is loaded.

hashCode

Linear iteration on all items.

indexOf

2 sql selects

isEmpty

May require operation if size is not yet known.

lastIndexOf

2 sql selects

remove

If index is given, then no operation. If object is given, an indexOf operation.

removeAll

A remove for all items given.

retainAll

Linear operation in this list, and a contains() call for each item in the given collection.

set

A remove and an add.

size

Possible operations if size is not yet known.

When a List is saved, there is one update for each removed item, there is one insert for all added items, and one update if the list was cleared. Additionally, in a few occasions the implementation may need to re-index the list, if it ran out of indexes. In this case there is an insert and update for all items. Re-indexing occurs, if there were many insertions between existing items. There will be no re-indexing ever, if adding only to the list's begining or end.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.6.2 Set (and Collection)

A set is an unordered item container, which can not hold duplications of the same item.

add

A contains operation.

addAll

An add operation for each item in the collection given.

clear

0 sql operations.

contains

1 sql operation.

containsAll

A contains operation for each item in the collection given.

equals

1 sql operation.

hashCode

Linear iteration on all items.

remove

A contains operation.

removeAll

A remove for all items given.

retainAll

Linear operation in this list, and a contains() call for each item in the given collection.

size

Possible operations if size is not yet known.

When a Set is saved, there is one update for each removed item, there is one insert for all added items, and one update, if the set was cleared.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

13.6.3 Map

A map is a mapping of String keys to objects.

put

A contains operation.

putAll

An put operation for each item in the collection given.

clear

0 sql operations.

containsValue

1 sql operation.

containsKey

1 sql operation.

equals

1 sql operation.

get

1 sql select.

hashCode

Linear iteration on all items.

remove

A get operation.

removeAll

A remove for all items given.

retainAll

Linear operation in this list, and a contains() call for each item in the given collection.

size

Possible operations if size is not yet known.

When a Map is saved, there is one update for each removed item, there is one insert for all added items, and one update, if the map was cleared.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Robert Brautigam on November, 21 2009 using texi2html 1.78.