Hibernate.orgCommunity Documentation

Hibernate Developer Guide

4.0.1.Final

2012-01-11


Preface
1. Get Involved
2. Getting Started Guide
1. Database access
1.1. Connecting
1.1.1. Configuration
1.1.2. Obtaining a JDBC connection
1.2. Connection pooling
1.2.1. c3p0 connection pool
1.2.2. Proxool connection pool
1.2.3. Obtaining connections from an application server, using JNDI
1.2.4. Other connection-specific configuration
1.2.5. Optional configuration properties
1.3. Dialects
1.3.1. Specifying the Dialect to use
1.3.2. Dialect resolution
1.4. Automatic schema generation with SchemaExport
1.4.1. Customizing the mapping files
1.4.2. Running the SchemaExport tool
2. Transactions and concurrency control
2.1. Defining Transaction
2.2. Physical Transactions
2.2.1. Physical Transactions - JDBC
2.2.2. Physical Transactions - JTA
2.2.3. Physical Transactions - CMT
2.2.4. Physical Transactions - Custom
2.2.5. Physical Transactions - Legacy
2.3. Hibernate Transaction Usage
2.4. Transaction Scopes
2.4.1. Session-per-operation
2.4.2. Session-per-request
2.4.3. Conversations
2.4.4. Object identity
2.4.5. Problems and anti-patterns
2.5. Hibernate Transaction API (JTA)
2.5.1. Bean-managed transactions (BMT)
2.5.2. Container-managed transactions (CMT)
3. Persistence Contexts
3.1. Making entities persistent
3.2. Deleting entities
3.3. Obtain an entity reference without initializing its data
3.4. Obtain an entity with its data initialized
3.5. Refresh entity state
3.6. Modifying managed/persistent state
3.7. Working with detached data
3.7.1. Reattaching detached data
3.7.2. Merging detached data
3.8. Checking persistent state
3.9. Accessing Hibernate APIs from JPA
4. Batch Processing
4.1. Batch inserts
4.2. Batch updates
4.3. StatelessSession
4.4. Hibernate Query Language for DML
4.4.1. HQL for UPDATE and DELETE
4.4.2. HQL syntax for INSERT
4.4.3. More information on HQL
5. Locking
5.1. Optimistic
5.1.1. Dedicated version number
5.1.2. Timestamp
5.2. Pessimistic
5.2.1. The LockMode class
6. Caching
6.1. The query cache
6.1.1. Query cache regions
6.2. Second-level cache providers
6.2.1. Configuring your cache providers
6.2.2. Caching strategies
6.2.3. Second-level cache providers for Hibernate
6.3. Managing the cache
6.3.1. Moving items into and out of the cache
7. Services
7.1. What are services?
7.2. Service contracts
7.3. Service dependencies
7.3.1. @org.hibernate.service.spi.InjectService
7.3.2. org.hibernate.service.spi.ServiceRegistryAwareService
7.4. ServiceRegistry
7.5. Standard services
7.5.1. org.hibernate.engine.jdbc.batch.spi.BatchBuilder
7.5.2. org.hibernate.service.config.spi.ConfigurationService
7.5.3. org.hibernate.service.jdbc.connections.spi.ConnectionProvider
7.5.4. org.hibernate.service.jdbc.dialect.spi.DialectFactory
7.5.5. org.hibernate.service.jdbc.dialect.spi.DialectResolver
7.5.6. org.hibernate.engine.jdbc.spi.JdbcServices
7.5.7. org.hibernate.service.jmx.spi.JmxService
7.5.8. org.hibernate.service.jndi.spi.JndiService
7.5.9. org.hibernate.service.jta.platform.spi.JtaPlatform
7.5.10. org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider
7.5.11. org.hibernate.persister.spi.PersisterClassResolver
7.5.12. org.hibernate.persister.spi.PersisterFactory
7.5.13. org.hibernate.cache.spi.RegionFactory
7.5.14. org.hibernate.service.spi.SessionFactoryServiceRegistryFactory
7.5.15. org.hibernate.stat.Statistics
7.5.16. org.hibernate.engine.transaction.spi.TransactionFactory
7.5.17. org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor
7.6. Custom services
7.7. Special service registries
7.7.1. Boot-strap registry
7.7.2. SessionFactory registry
7.8. Using services and registries
7.9. Integrators
7.9.1. Integrator use-cases
8. Data categorizations
8.1. Value types
8.1.1. Basic types
8.1.2. Composite types
8.1.3. Collection types
8.2. Entity Types
8.3. Implications of different data categorizations
9. Mapping entities
9.1. Hierarchies
10. Mapping associations
11. HQL and JPAQL
12. Criteria
13. Native-SQL
14. JMX
15. Envers
15.1. Basics
15.2. Configuration
15.3. Additional mapping annotations
15.4. Choosing an audit strategy
15.5. Revision Log
15.5.1. Tracking entity names modified during revisions
15.6. Queries
15.6.1. Querying for entities of a class at a given revision
15.6.2. Querying for revisions, at which entities of a given class changed
15.6.3. Querying for entities modified in a given revision
15.7. Conditional auditing
15.8. Understanding the Envers Schema
15.9. Generating schema with Ant
15.10. Mapping exceptions
15.10.1. What isn't and will not be supported
15.10.2. What isn't and will be supported
15.10.3. @OneToMany+@JoinColumn
15.11. Advanced: Audit table partitioning
15.11.1. Benefits of audit table partitioning
15.11.2. Suitable columns for audit table partitioning
15.11.3. Audit table partitioning example
15.12. Envers links
A. Configuration properties
A.1. General Configuration
A.2. Database configuration
A.3. Connection pool properties
B. Revision History
Index

Working with both Object-Oriented software and Relational Databases can be cumbersome and time consuming. Development costs are significantly higher due to a paradigm mismatch between how data is represented in objects versus relational databases. Hibernate is an Object/Relational Mapping solution for Java environments. The term Object/Relational Mapping refers to the technique of mapping data from an object model representation to a relational data model representation (and visa versa). See http://en.wikipedia.org/wiki/Object-relational_mapping for a good high-level discussion.

Note

While having a strong background in SQL is not required to use Hibernate, having a basic understanding of the concepts can greatly help you understand Hibernate more fully and quickly. Probably the single best background is an understanding of data modeling principles. You might want to consider these resources as a good starting point:

Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities. It can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC. Hibernate’s design goal is to relieve the developer from 95% of common data persistence-related programming tasks by eliminating the need for manual, hand-crafted data processing using SQL and JDBC. However, unlike many other persistence solutions, Hibernate does not hide the power of SQL from you and guarantees that your investment in relational technology and knowledge is as valid as always.

Hibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the Java-based middle-tier. However, Hibernate can certainly help you to remove or encapsulate vendor-specific SQL code and will help with the common task of result set translation from a tabular representation to a graph of objects.

Hibernate connects to databases on behalf of your application. It can connect through a variety of mechanisms, including:

You can configure database connections using a properties file, an XML deployment descriptor, programmatically.


Example 1.2. hibernate.cfg.xml for a connection to the bundled HSQL database


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
    <!-- Database connection settings -->
    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
    <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
    <property name="connection.username">sa</property>
    <property name="connection.password"></property>

    <!-- JDBC connection pool (use the built-in) -->
    <property name="connection.pool_size">1</property>

    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.HSQLDialect</property>

    <!-- Enable Hibernate's automatic session context management -->
    <property name="current_session_context_class">thread</property>

    <!-- Disable the second-level cache  -->
    <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">true</property>

    <!-- Drop and re-create the database schema on startup -->
    <property name="hbm2ddl.auto">update</property>
    <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

Note

The DTD in the configuration file used in this example is required.

An instance of object org.hibernate.cfg.Configuration represents an entire set of mappings of an application's Java types to an SQL database. The org.hibernate.cfg.Configuration builds an immutable org.hibernate.SessionFactory, and compiles the mappings from various XML mapping files. You can specify the mapping files directly, or Hibernate can find them for you.




Other ways to configure Hibernate programmatically

  • Pass an instance of java.util.Properties to Configuration.setProperties().

  • Set System properties using java -Dproperty=value

Hibernate's internal connection pooling algorithm is rudimentary, and is provided for development and testing purposes. Use a third-party pool for best performance and stability. To use a third-party pool, replace the hibernate.connection.pool_size property with settings specific to your connection pool of choice. This disables Hibernate's internal connection pool.

Although SQL is relatively standardized, each database vendor uses a subset of supported syntax. This is referred to as a dialect. Hibernate handles variations across these dialects through its org.hibernate.dialect.Dialect class and the various subclasses for each vendor dialect.


SchemaExport is a Hibernate utility which generates DDL from your mapping files. The generated schema includes referential integrity constraints, primary and foreign keys, for entity and collection tables. It also creates tables and sequences for mapped identifier generators.

Before Hibernate can generate your schema, you must customize your mapping files.

Hibernate provides several elements and attributes to customize your mapping files. They are listed in Table 1.3, “Elements and attributes provided for customizing mapping files”, and a logical order of customization is presented in Customizing the schema.


Procedure 1.1. Customizing the schema

  1. Set the length, precision, and scale of mapping elements.

    Many Hibernate mapping elements define optional attributes named length, precision, and scale.

    
    <property name="zip" length="5"/>
    <property name="balance" precision="12" scale="2"/>
  2. Set the not-null, UNIQUE, unique-key attributes.

    The not-null and UNIQUE attributes generate constraints on table columns.

    The unique-key attribute groups columns in a single, unique key constraint. Currently, the specified value of the unique-key attribute does not name the constraint in the generated DDL. It only groups the columns in the mapping file.

    
    <many-to-one name="bar" column="barId" not-null="true"/>
    <element column="serialNumber" type="long" not-null="true" unique="true"/>

    <many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>
    <property name="employeeId" unique-key="OrgEmployee"/>
  3. Set the index and foreign-key attributes.

    The index attribute specifies the name of an index for Hibernate to create using the mapped column or columns. You can group multiple columns into the same index by assigning them the same index name.

    A foreign-key attribute overrides the name of any generated foreign key constraint.

    
    <many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>
  4. Set child <column> elements.

    Many mapping elements accept one or more child <column> elements. This is particularly useful for mapping types involving multiple columns.

    
    <property name="name" type="my.customtypes.Name"/>
        <column name="last" not-null="true" index="bar_idx" length="30"/>
        <column name="first" not-null="true" index="bar_idx" length="20"/>
        <column name="initial"/>
    </property>
  5. Set the default attribute.

    The default attribute represents a default value for a column. Assign the same value to the mapped property before saving a new instance of the mapped class.

    
    <property name="credits" type="integer" insert="false">
        <column name="credits" default="10"/>
    </property>
    <version name="version" type="integer" insert="false">
        <column name="version" default="0"/>
    </property>
  6. Set the sql-type attribure.

    Use the sql-type attribute to override the default mapping of a Hibernate type to SQL datatype.

    
    <property name="balance" type="float">
        <column name="balance" sql-type="decimal(13,3)"/>
    </property>
  7. Set the check attribute.

    use the check attribute to specify a check constraint.

    
    <property name="foo" type="integer">
      <column name="foo" check="foo > 10"/>
    </property>
    <class name="Foo" table="foos" check="bar < 100.0">
      ...
      <property name="bar" type="float"/>
    </class>
  8. Add <comment> elements to your schema.

    Use the <comment> element to specify comments for the generated schema.

    
    <class name="Customer" table="CurCust">
      <comment>Current customers only</comment>
      ...
    </class>

Hibernate uses the JDBC API for persistence. In the world of Java there are 2 well defined mechanism for dealing with transactions in JDBC: JDBC, itself, and JTA. Hibernate supports both mechanisms for integrating with transactions and allowing applications to manage physical transactions.

The first concept in understanding Hibernate transaction support is the org.hibernate.engine.transaction.spi.TransactionFactory interface which serves 2 main functions:

org.hibernate.engine.transaction.spi.TransactionFactory is a standard Hibernate service. See Section 7.5.16, “org.hibernate.engine.transaction.spi.TransactionFactory for details.

The most common pattern in a multi-user client/server application is session-per-request. In this model, a client sends a request to the server, where the Hibernate persistence layer is running. Hibernate opens a new Session, and all database operations are executed in this unit of work. After the work is completed, and the server prepares the response for the client, the session is flushed and closed. Use a single database transaction to serve the clients request, starting and committing it when you open and close the Session. The relationship between the transaction and the Session is one-to-one.

Hibernate provides built-in management of the current session to simplify the session-per-request pattern. Start a transaction to process a server request, and end the transaction before sending the response to the client. Common solutions include:

An EJB container is a standardized way to implement cross-cutting aspects such as transaction demarcation on EJB session beans in a declarative manner with CMT. If you prefer programmatic transaction demarcation, see Section 2.5, “Hibernate Transaction API (JTA)”.

To access a current session to process the request, call method sessionFactory.getCurrentSession(). The returned Session is scoped to the current database transaction. You need to configure this for either resource-local or JTA environments

You can extend the scope of a Session and database transaction until the view is rendered. This is especially useful in servlet applications that include a separate rendering phase after the request is processed. To extending the database transaction until view rendering, implement your own interceptor. This implementation is difficult if you rely on EJBs with container-managed transactions. A transaction is completed when an EJB method returns, before rendering of any view can start. Search the Hibernate forums for tips and examples relating to this Open Session in View pattern.

If session-per-request does not seem well-suited for your application, refer to Section 2.4.3, “Conversations” for another choice.

The session-per-request pattern is not the only way of designing units of work. Many business processes require a whole series of interactions with the user that are interleaved with database accesses. In web and enterprise applications, it is not acceptable for a database transaction to span a user interaction. Consider the following example:

From the point of view of the user, this unit of work is a long-running conversation or application transaction. There are many ways to implement this in your application.

A first naive implementation might keep the Session and database transaction open while the user is editing, using database-level locks to prevent other users from modifying the same data and to guarantee isolation and atomicity. This is an anti-pattern, because lock contention is a bottleneck which will prevent scalability in the future.

Several database transactions are used to implement the conversation. In this case, maintaining isolation of business processes becomes the partial responsibility of the application tier. A single conversation usually spans several database transactions. It is atomic if only one of these database transactions, typically the last one, stores the updated data. All others only read data. A common way to receive this data is through a wizard-style dialog spanning several request/response cycles. Hibernate includes some features which make this easy to implement.

Automatic Versioning

Hibernate can perform automatic optimistic concurrency control for you. It can automatically detect if a concurrent modification occurred during user think time. Check for this at the end of the conversation.

Detached Objects

If you decide to use the session-per-request pattern, all loaded instances will be in the detached state during user think time. Hibernate allows you to reattach the objects and persist the modifications. The pattern is called session-per-request-with-detached-objects. Automatic versioning is used to isolate concurrent modifications.

Extended (or Long) Session

Extended (or Long) Session: the Hibernate Session can be disconnected from the underlying JDBC connection after the database transaction has been committed and reconnected when a new client request occurs. This pattern is known as session-per-conversation and makes even reattachment unnecessary. Automatic versioning is used to isolate concurrent modifications and the Session will not be allowed to be flushed automatically, but explicitly.

Session-per-request-with-detached-objects and session-per-conversation each have advantages and disadvantages.

An application can concurrently access the same persistent state in two different Sessions. However, an instance of a persistent class is never shared between two Session instances. Therefore, two different notions of identity exist: Database identity and JVM identity.



For objects attached to a particular Session, the two notions are equivalent, and JVM identity for database identity is guaranteed by Hibernate. The application might concurrently access a business object with the same identity in two different sessions, the two instances are actually different, in terms of JVM identity. Conflicts are resolved using an optimistic approach and automatic versioning at flush/commit time.

This approach places responsibility for concurrency on Hibernate and the database. It also provides the best scalability, since expensive locking is not needed to guarantee identity in single-threaded units of work. The application does not need to synchronize on any business object, as long as it maintains a single thread per Session. Within a Session the application can safely use the == operator to compare objects.

However, an application that uses the == operator outside of a Session may introduce problems.. If you put two detached instances into the same Set, they might use the same database identity, which means they represent the same row in the database. They would not be guaranteed to have the same JVM identity if they are in a detached state. Override the equals and hashCode methods in persistent classes, so that they have their own notion of object equality. Never use the database identifier to implement equality. Instead, use a business key that is a combination of unique, typically immutable, attributes. The database identifier changes if a transient object is made persistent. If the transient instance, together with detached instances, is held in a Set, changing the hash-code breaks the contract of the Set. Attributes for business keys can be less stable than database primary keys. You only need to guarantee stability as long as the objects are in the same Set.This is not a Hibernate issue, but relates to Java's implementation of object identity and equality.

Avoid the anti-patterns session-per-user-session or session-per-application, for the most part. The reasons are outlined in Section 2.4.1, “Session-per-operation”. In addition, keep the following points in mind when choosing patterns for your application.

A Session is not thread-safe.

Resources, such as HTTP requests, session beans, or Swing workers, cause race conditions if a Session instance is shared. If you keep your Hibernate Session in your HttpSession, consider synchronizing access to your Http session. Otherwise, a reloaded application may use the same Session in two concurrently-running threads.

If Hibernate throws an exception, you must rollback your database transaction and close the Session immediately.

In addition to the above, if your Session is bound to the application, you must stop the application. Rolling back the database transaction does not put your business objects back into their state at the start of the transaction. The database state and the business objects will be out of sync. Since exceptions are not recoverable, this is typically not a problem. You must start over after rollback anyway.

The Session caches every object that is in a persistent state.

A persistent state refers to objects that are watched and checked for dirty state by Hibernate. If you keep the cache open for a long time or load too much data, it grows to fill available memory, causing an OutOfMemoryException.

To solve this problem, you can call clear() and evict() to manage the Session cache. Alternately, you can use a Stored Procedure if you need mass data operations. Keeping a Session open for the duration of a user session reduces the probability of stale data.

If your persistence layer runs in an application server, such as behind EJB session beans, every datasource connection obtained by Hibernate is automatically part of the global JTA transaction. You can also install a standalone JTA implementation and use it without EJB. Hibernate offers two strategies for JTA integration.

With CMT, transaction demarcation is declared in session bean deployment descriptors, rather than performed in a programmatic manner. This reduces the amount of code.


In a CMT/EJB, rollback happens automatically as well. An unhandled RuntimeException thrown by a session bean method tells the container to set the global transaction to rollback. You do not need to intervene, and you get automatic propagation of the Current Session bound to the transaction.

When configuring Hibernate's transaction factory, choose org.hibernate.transaction.JTATransactionFactory if you use JTA directly (BMT), and org.hibernate.transaction.CMTTransactionFactory in a CMT session bean. Set the hibernate.transaction.manager_lookup_class property as well. Ensure that your hibernate.current_session_context_class is either unset, or is set to jta.

The getCurrentSession() method has one downside in a JTA environment. If you use it, after_statement connection release mode is also used by default. Due to a limitation of the JTA specification, Hibernate cannot automatically clean up any unclosed ScrollableResults or Iterator instances returned by scroll() or iterate(). Release the underlying database cursor by calling ScrollableResults.close() or Hibernate.close(Iterator) explicitly from a finally block. Try to avoid using scroll() or iterate() from the JTA or CMT code.

Both the org.hibernate.Session API and javax.persistence.EntityManager API represent a context for dealing with persistent data. This concept is called a persistence context. Persistent data has a state in relation to both a persistence context and the underlying database.

Entity states

  • new, or transient - the entity has just been instantiated and is not associated with a persistence context. It has no persistent representation in the database and no identifier value has been assigned.

  • managed, or persistent - the entity has an associated identifier and is associated with a persistence context.

  • detached - the entity has an associated identifier, but is no longer associated with a persistence context (usually because the persistence context was closed or the instance was evicted from the context)

  • removed - the entity has an associated identifier and is associated with a persistence context, however it is scheduled for removal from the database.

In Hibernate native APIs, the persistence context is defined as the org.hibernate.Session. In JPA, the persistence context is defined by javax.persistence.EntityManager. Much of the org.hibernate.Session and javax.persistence.EntityManager methods deal with moving entities between these states.

Detachment is the process of working with data outside the scope of any persistence context. Data becomes detached in a number of ways. Once the persistence context is closed, all data that was associated with it becomes detached. Clearing the persistence context has the same effect. Evicting a particular entity from the persistence context makes it detached. And finally, serialization will make the deserialized form be detached (the original instance is still managed).

Detached data can still be manipulated, however the persistence context will no longer automatically know about these modification and the application will need to intervene to make the changes persistent.

An application can verify the state of entities and collections in relation to the persistence context.



In JPA there is an alternative means to check laziness using the following javax.persistence.PersistenceUtil pattern. However, the javax.persistence.PersistenceUnitUtil is recommended where ever possible


The following example shows an antipattern for batch inserts.


Before batch processing, enable JDBC batching. To enable JDBC batching, set the property hibernate.jdbc.batch_size to an integer between 10 and 50.

Note

Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator.

If the above approach is not appropriate, you can disable the second-level cache, by setting hibernate.cache.use_second_level_cache to false.

StatelessSession is a command-oriented API provided by Hibernate. Use it to stream data to and from the database in the form of detached objects. A StatelessSession has no persistence context associated with it and does not provide many of the higher-level life cycle semantics. Some of the things not provided by a StatelessSession include:


The insert(), update(), and delete() operations defined by the StatelessSession interface operate directly on database rows. They cause the corresponding SQL operations to be executed immediately. They have different semantics from the save(), saveOrUpdate(), and delete() operations defined by the Session interface.

DML, or Data Markup Language, refers to SQL statements such as INSERT, UPDATE, and DELETE. Hibernate provides methods for bulk SQL-style DML statement execution, in the form of Hibernate Query Language (HQL).


The FROM clause can only refer to a single entity, which can be aliased. If the entity name is aliased, any property references must be qualified using that alias. If the entity name is not aliased, then it is illegal for any property references to be qualified.

Joins, either implicit or explicit, are prohibited in a bulk HQL query. You can use sub-queries in the WHERE clause, and the sub-queries themselves can contain joins.


In keeping with the EJB3 specification, HQL UPDATE statements, by default, do not effect the version or the timestamp property values for the affected entities. You can use a versioned update to force Hibernate to reset the version or timestamp property values, by adding the VERSIONED keyword after the UPDATE keyword.


Note

If you use the VERSIONED statement, you cannot use custom version types, which use class org.hibernate.usertype.UserVersionType.


Method Query.executeUpdate() returns an int value, which indicates the number of entities effected by the operation. This may or may not correlate to the number of rows effected in the database. An HQL bulk operation might result in multiple SQL statements being executed, such as for joined-subclass. In the example of joined-subclass, a DELETE against one of the subclasses may actually result in deletes in the tables underlying the join, or further down the inheritance hierarchy.


Only the INSERT INTO ... SELECT ... form is supported. You cannot specify explicit values to insert.

The properties_list is analogous to the column specification in the SQL INSERT statement. For entities involved in mapped inheritance, you can only use properties directly defined on that given class-level in the properties_list. Superclass properties are not allowed and subclass properties are irrelevant. In other words, INSERT statements are inherently non-polymorphic.

The select_statement can be any valid HQL select query, but the return types must match the types expected by the INSERT. Hibernate verifies the return types during query compilation, instead of expecting the database to check it. Problems might result from Hibernate types which are equivalent, rather than equal. One such example is a mismatch between a property defined as an org.hibernate.type.DateType and a property defined as an org.hibernate.type.TimestampType, even though the database may not make a distinction, or may be capable of handling the conversion.

If id property is not specified in the properties_list, Hibernate generates a value automatically. Automatic generation is only available if you use ID generators which operate on the database. Otherwise, Hibernate throws an exception during parsing. Available in-database generators are org.hibernate.id.SequenceGenerator and its subclasses, and objects which implement org.hibernate.id.PostInsertIdentifierGenerator. The most notable exception is org.hibernate.id.TableHiLoGenerator, which does not expose a selectable way to get its values.

For properties mapped as either version or timestamp, the insert statement gives you two options. You can either specify the property in the properties_list, in which case its value is taken from the corresponding select expressions, or omit it from the properties_list, in which case the seed value defined by the org.hibernate.type.VersionType is used.


Locking refers to actions taken to prevent data in a relational database from changing between the time it is read and the time that it is used.

Your locking strategy can be either optimistic or pessimistic.

Locking strategies

Optimistic

Optimistic locking ssumes that multiple transactions can complete without affecting each other, and that therefore transactions can proceed without locking the data resources that they affect. Before committing, each transaction verifies that no other transaction has modified its data. If the check reveals conflicting modifications, the committing transaction rolls back[1].

Pessimistic

Pessimistic locking assumes that concurrent transactions will conflict with each other, and requires resources to be locked after they are read and only unlocked after the application has finished using the data.

Hibernate provides mechanisms for implementing both types of locking in your applications.

When your application uses long transactions or conversations that span several database transactions, you can store versioning data, so that if the same entity is updated by two conversations, the last to commit changes is informed of the conflict, and does not override the other conversation's work. This approach guarantees some isolation, but scales well and works particularly well in Read-Often Write-Sometimes situations.

Hibernate provides two different mechanisms for storing versioning information, a dedicated version number or a timestamp.

The version number mechanism for optimistic locking is provided through a @Version annotation.


The version column can be any kind of type, as long as you define and implement the appropriate UserVersionType.

Your application is forbidden from altering the version number set by Hibernate. To artificially increase the version number, see the documentation for properties LockModeType.OPTIMISTIC_FORCE_INCREMENT or LockModeType.PESSIMISTIC_FORCE_INCREMENTcheck in the Hibernate Entity Manager reference documentation.

Database-generated version numbers

If the version number is generated by the database, such as a trigger, use the annotation @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).


Timestamps are a less reliable way of optimistic locking than version numbers, but can be used by applications for other purposes as well. Timestamping is automatically used if you the @Version annotation on a Date or Calendar.


Hibernate can retrieve the timestamp value from the database or the JVM, by reading the value you specify for the @org.hibernate.annotations.Source annotation. The value can be either org.hibernate.annotations.SourceType.DB or org.hibernate.annotations.SourceType.VM. The default behavior is to use the database, and is also used if you don't specify the annotation at all.

The timestamp can also be generated by the database instead of Hibernate, if you use the @org.hibernate.annotations.Generated(GenerationTime.ALWAYS) annotation.

Example 5.4. The timestamp element in hbm.xml


<timestamp
        column="timestamp_column"
        name="propertyName"
        access="field|property|ClassName"
        unsaved-value="null|undefined"
        source="vm|db"
        generated="never|always"
        node="element-name|@attribute-name|element/@attribute|."
/>
column

The name of the column which holds the timestamp. Optional, defaults to the property namel

name

The name of a JavaBeans style property of Java type Date or Timestamp of the persistent class.

access

The strategy Hibernate uses to access the property value. Optional, defaults to property.

unsaved-value

A version property which indicates than instance is newly instantiated, and unsaved. This distinguishes it from detached instances that were saved or loaded in a previous session. The default value of undefined indicates that Hibernate uses the identifier property value.

source

Whether Hibernate retrieves the timestamp from the database or the current JVM. Database-based timestamps incur an overhead because Hibernate needs to query the database each time to determine the incremental next value. However, database-derived timestamps are safer to use in a clustered environment. Not all database dialects are known to support the retrieval of the database's current timestamp. Others may also be unsafe for locking, because of lack of precision.

generated

Whether the timestamp property value is generated by the database. Optional, defaults to never.


Typically, you only need to specify an isolation level for the JDBC connections and let the database handle locking issues. If you do need to obtain exclusive pessimistic locks or re-obtain locks at the start of a new transaction, Hibernate gives you the tools you need.

The LockMode class defines the different lock levels that Hibernate can acquire.

LockMode.WRITE

acquired automatically when Hibernate updates or inserts a row.

LockMode.UPGRADE

acquired upon explicit user request using SELECT ... FOR UPDATE on databases which support that syntax.

LockMode.UPGRADE_NOWAIT

acquired upon explicit user request using a SELECT ... FOR UPDATE NOWAIT in Oracle.

LockMode.READ

acquired automatically when Hibernate reads data under Repeatable Read or Serializable isolation level. It can be re-acquired by explicit user request.

LockMode.NONE

The absence of a lock. All objects switch to this lock mode at the end of a Transaction. Objects associated with the session via a call to update() or saveOrUpdate() also start out in this lock mode.

The explicit user request mentioned above occurs as a consequence of any of the following actions:

If you call Session.load() with option UPGRADE or UPGRADE_NOWAIT, and the requested object is not already loaded by the session, the object is loaded using SELECT ... FOR UPDATE. If you call load() for an object that is already loaded with a less restrictive lock than the one you request, Hibernate calls lock() for that object.

Session.lock() performs a version number check if the specified lock mode is READ, UPGRADE, or UPGRADE_NOWAIT. In the case of UPGRADE or UPGRADE_NOWAIT, SELECT ... FOR UPDATE syntax is used.

If the requested lock mode is not supported by the database, Hibernate uses an appropriate alternate mode instead of throwing an exception. This ensures that applications are portable.

If you have queries that run over and over, with the same parameters, query caching provides performance gains.

Caching introduces overhead in the area of transactional processing. For example, if you cache results of a query against an object, Hibernate needs to keep track of whether any changes have been committed against the object, and invalidate the cache accordingly. In addition, the benefit from caching query results is limited, and highly dependent on the usage patterns of your application. For these reasons, Hibernate disables the query cache by default.

The query cache does not cache the state of the actual entities in the cache. It caches identifier values and results of value type. Therefore, always use the query cache in conjunction with the second-level cache for those entities which should be cached as part of a query result cache.

Hibernate is compatible with several second-level cache providers. None of the providers support all of Hibernate's possible caching strategies. Section 6.2.3, “Second-level cache providers for Hibernate” lists the providers, along with their interfaces and supported caching strategies. For definitions of caching strategies, see Section 6.2.2, “Caching strategies”.

You can configure your cache providers using either annotations or mapping files.

Entities

By default, entities are not part of the second-level cache, and their use is not recommended. If you absolutely must use entities, set the shared-cache-mode element in persistence.xml, or use property javax.persistence.sharedCache.mode in your configuration. Use one of the values in Table 6.1, “Possible values for Shared Cache Mode”.


Set the global default cache concurrency strategy The cache concurrency strategy with the hibernate.cache.default_cache_concurrency_strategy configuration property. See Section 6.2.2, “Caching strategies” for possible values.

Note

When possible, define the cache concurrency strategy per entity rather than globally. Use the @org.hibernate.annotations.Cache annotation.


Example 6.3. Configuring cache providers using mapping files


<cache
    usage="transactional"
    region="RegionName"
    include="all"
/>

Just as in the Example 6.2, “Configuring cache providers using annotations”, you can provide attributes in the mapping file. There are some specific differences in the syntax for the attributes in a mapping file.

usage

The caching strategy. This attribute is required, and can be any of the following values.

  • transactional

  • read-write

  • nonstrict-read-write

  • read-only

region

The name of the second-level cache region. This optional attribute defaults to the class or collection role name.

include

Whether properties of the entity mapped with lazy=true can be cached when attribute-level lazy fetching is enabled. Defaults to all and can also be non-lazy.

Instead of <cache>, you can use <class-cache> and <collection-cache> elements in hibernate.cfg.xml.


read-only

A read-only cache is good for data that needs to be read often but not modified. It is simple, performs well, and is safe to use in a clustered environment.

nonstrict read-write

Some applications only rarely need to modify data. This is the case if two transactions are unlikely to try to update the same item simultaneously. In this case, you do not need strict transaction isolation, and a nonstrict-read-write cache might be appropriate. If the cache is used in a JTA environment, you must specify hibernate.transaction.manager_lookup_class. In other environments, ensore that the transaction is complete before you call Session.close() or Session.disconnect().

read-write

A read-write cache is appropriate for an application which needs to update data regularly. Do not use a read-write strategy if you need serializable transaction isolation. In a JTA environment, specify a strategy for obtaining the JTA TransactionManager by setting the property hibernate.transaction.manager_lookup_class. In non-JTA environments, be sure the transaction is complete before you call Session.close() or Session.disconnect().

transactional

The transactional cache strategy provides support for transactional cache providers such as JBoss TreeCache. You can only use such a cache in a JTA environment, and you must first specify hibernate.transaction.manager_lookup_class.

CacheInterfaceSupported strategies  
HashTable (testing only) 
  • read-only

  • nontrict read-write

  • read-write

  
EHCache 
  • read-only

  • nontrict read-write

  • read-write

  
OSCache 
  • read-only

  • nontrict read-write

  • read-write

  
SwarmCache 
  • read-only

  • nontrict read-write

  
JBoss Cache 1.x 
  • read-only

  • transactional

  
JBoss Cache 2.x 
  • read-only

  • transactional

  
Syncing or removing a cached item

The state of an object is synchronized with the database when you call method flush(). To avoid this synchronization, you can remove the object and all collections from the first-level cache with the evict() method. To remove all items from the Session cache, use method Session.clear().


Determining whether an item belongs to the Session cache

The Session provides a contains() method to determine if an instance belongs to the session cache.


The CacheMode controls how a particular session interacts with the second-level cache.

CacheMode.NORMALreads items from and writes them to the second-level cache.
CacheMode.GETreads items from the second-level cache, but does not write to the second-level cache except to update data.
CacheMode.PUTwrites items to the second-level cache. It does not read from the second-level cache. It bypasses the effect of hibernate.cache.use_minimal_puts and forces a refresh of the second-level cache for all items read from the database.
Notes

Provides an abstraction from the underlying JTA platform when JTA features are used.

Initiator

org.hibernate.service.jta.platform.internal.JtaPlatformInitiator

Implementations
Notes

A variation of Section 7.5.3, “org.hibernate.service.jdbc.connections.spi.ConnectionProvider providing access to JDBC connections in multi-tenant environments.

Initiator

N/A

Implementations

Intended that users provide appropriate implementation if needed.

The boot-strap registry holds services that absolutely have to be available for most things to work. The main service here is the Section 7.7.1.1.1, “org.hibernate.service.classloading.spi.ClassLoaderService which is a perfect example. Even resolving configuration files needs access to class loading services (resource look ups). This is the root registry (no parent) in normal use.

Instances of boot-strap registries are built using the org.hibernate.service.BootstrapServiceRegistryBuilder builder.

The org.hibernate.integrator.spi.Integrator is intended to provide a simple means for allowing developers to hook into the process of building a functioning SessionFactory. The The org.hibernate.integrator.spi.Integrator interface defines 2 methods of interest: integrate allows us to hook into the building process; disintegrate allows us to hook into a SessionFactory shutting down.

See Section 7.7.1.1.2, “org.hibernate.integrator.spi.IntegratorService

The main use cases for an org.hibernate.integrator.spi.Integrator right now are registering event listeners and providing services (see org.hibernate.integrator.spi.ServiceContributingIntegrator). With 5.0 we plan on expanding that to allow altering the metamodel describing the mapping between object and relational models.


Hibernate understands both the Java and JDBC representations of application data. The ability to read and write object data to a database is called marshalling, and is the function of a Hibernate type. A type is an implementation of the org.hibernate.type.Type interface. A Hibernate type describes various aspects of behavior of the Java type such as how to check for equality and how to clone values.

Usage of the word type

A Hibernate type is neither a Java type nor a SQL datatype. It provides information about both of these.

When you encounter the term type in regards to Hibernate, it may refer to the Java type, the JDBC type, or the Hibernate type, depending on context.

Hibernate categorizes types into two high-level groups: Section 8.1, “Value types” and Section 8.2, “Entity Types”.

A value type does not define its own lifecycle. It is, in effect, owned by an Section 8.2, “Entity Types”, which defines its lifecycle. Value types are further classified into three sub-categories.

Basic value types usually map a single database value, or column, to a single, non-aggregated Java type. Hibernate provides a number of built-in basic types, which follow the natural mappings recommended in the JDBC specifications. You can override these mappings and provide and use alternative mappings. These topics are discussed further on.

Table 8.1. Basic Type Mappings

Hibernate typeDatabase typeJDBC typeType registry
org.hibernate.type.StringTypestringVARCHARstring, java.lang.String
org.hibernate.type.MaterializedClobstringCLOBmaterialized_clob
org.hibernate.type.TextTypestringLONGVARCHARtext
org.hibernate.type.CharacterTypechar, java.lang.CharacterCHARchar, java.lang.Character
org.hibernate.type.BooleanTypebooleanBITboolean, java.lang.Boolean
org.hibernate.type.NumericBooleanTypebooleanINTEGER, 0 is false, 1 is truenumeric_boolean
org.hibernate.type.YesNoTypebooleanCHAR, 'N'/'n' is false, 'Y'/'y' is true. The uppercase value is written to the database.yes_no
org.hibernate.type.TrueFalseTypebooleanCHAR, 'F'/'f' is false, 'T'/'t' is true. The uppercase value is written to the database.true_false
org.hibernate.type.ByteTypebyte, java.lang.ByteTINYINTbyte, java.lang.Byte
org.hibernate.type.ShortTypeshort, java.lang.ShortSMALLINTshort, java.lang.Short
org.hibernate.type.IntegerTypesint, java.lang.IntegerINTEGERint, java.lang.Integer
org.hibernate.type.LongTypelong, java.lang.LongBIGINTlong, java.lang.Long
org.hibernate.type.FloatTypefloat, java.lang.FloatFLOATfloat, java.lang.Float
org.hibernate.type.DoubleTypedouble, java.lang.DoubleDOUBLEdouble, java.lang.Double
org.hibernate.type.BigIntegerTypejava.math.BigIntegerNUMERICbig_integer
org.hibernate.type.BigDecimalTypejava.math.BigDecimalNUMERICbig_decimal, java.math.bigDecimal
org.hibernate.type.TimestampTypejava.sql.TimestampTIMESTAMPtimestamp, java.sql.Timestamp
org.hibernate.type.TimeTypejava.sql.TimeTIMEtime, java.sql.Time
org.hibernate.type.DateTypejava.sql.DateDATEdate, java.sql.Date
org.hibernate.type.CalendarTypejava.util.CalendarTIMESTAMPcalendar, java.util.Calendar
org.hibernate.type.CalendarDateTypejava.util.CalendarDATEcalendar_date
org.hibernate.type.CurrencyTypejava.util.CurrencyVARCHARcurrency, java.util.Currency
org.hibernate.type.LocaleTypejava.util.LocaleVARCHARlocale, java.utility.locale
org.hibernate.type.TimeZoneTypejava.util.TimeZoneVARCHAR, using the TimeZone IDtimezone, java.util.TimeZone
org.hibernate.type.UrlTypejava.net.URLVARCHARurl, java.net.URL
org.hibernate.type.ClassTypejava.lang.ClassVARCHAR, using the class nameclass, java.lang.Class
org.hibernate.type.BlobTypejava.sql.BlobBLOBblog, java.sql.Blob
org.hibernate.type.ClobTypejava.sql.ClobCLOBclob, java.sql.Clob
org.hibernate.type.BinaryTypeprimitive byte[]VARBINARYbinary, byte[]
org.hibernate.type.MaterializedBlobTypeprimitive byte[]BLOBmaterized_blob
org.hibernate.type.ImageTypeprimitive byte[]LONGVARBINARYimage
org.hibernate.type.BinaryTypejava.lang.Byte[]VARBINARYwrapper-binary
org.hibernate.type.CharArrayTypechar[]VARCHARcharacters, char[]
org.hibernate.type.CharacterArrayTypejava.lang.Character[]VARCHARwrapper-characters, Character[], java.lang.Character[]
org.hibernate.type.UUIDBinaryTypejava.util.UUIDBINARYuuid-binary, java.util.UUID
org.hibernate.type.UUIDCharTypejava.util.UUIDCHAR, can also read VARCHARuuid-char
org.hibernate.type.PostgresUUIDTypejava.util.UUIDPostgreSQL UUID, through Types#OTHER, which complies to the PostgreSQL JDBC driver definitionpg-uuid
org.hibernate.type.SerializableTypeimplementors of java.lang.SerializableVARBINARY Unlike the other value types, multiple instances of this type are registered. It is registered once under java.io.Serializable, and registered under the specific java.io.Serializable implementation class names.

The most basic form of mapping in Hibernate is mapping a persistent entity class to a database table. You can expand on this concept by mapping associated classes together. ??? shows a Person class with a

It is possible to configure various aspects of Hibernate Envers behavior, such as table names, etc.

Table 15.1. Envers Configuration Properties

Property nameDefault valueDescription
org.hibernate.envers.audit_table_prefix String that will be prepended to the name of an audited entity to create the name of the entity, that will hold audit information.
org.hibernate.envers.audit_table_suffix _AUD String that will be appended to the name of an audited entity to create the name of the entity, that will hold audit information. If you audit an entity with a table name Person, in the default setting Envers will generate a Person_AUD table to store historical data.
org.hibernate.envers.revision_field_name REV Name of a field in the audit entity that will hold the revision number.
org.hibernate.envers.revision_type_field_name REVTYPE Name of a field in the audit entity that will hold the type of the revision (currently, this can be: add, mod, del).
org.hibernate.envers.revision_on_collection_change true Should a revision be generated when a not-owned relation field changes (this can be either a collection in a one-to-many relation, or the field using "mappedBy" attribute in a one-to-one relation).
org.hibernate.envers.do_not_audit_optimistic_locking_field true When true, properties to be used for optimistic locking, annotated with @Version, will be automatically not audited (their history won't be stored; it normally doesn't make sense to store it).
org.hibernate.envers.store_data_at_delete false Should the entity data be stored in the revision when the entity is deleted (instead of only storing the id and all other properties as null). This is not normally needed, as the data is present in the last-but-one revision. Sometimes, however, it is easier and more efficient to access it in the last revision (then the data that the entity contained before deletion is stored twice).
org.hibernate.envers.default_schema null (same schema as table being audited) The default schema name that should be used for audit tables. Can be overridden using the @AuditTable(schema="...") annotation. If not present, the schema will be the same as the schema of the table being audited.
org.hibernate.envers.default_catalog null (same catalog as table being audited) The default catalog name that should be used for audit tables. Can be overridden using the @AuditTable(catalog="...") annotation. If not present, the catalog will be the same as the catalog of the normal tables.
org.hibernate.envers.audit_strategy org.hibernate.envers.strategy.DefaultAuditStrategy The audit strategy that should be used when persisting audit data. The default stores only the revision, at which an entity was modified. An alternative, the org.hibernate.envers.strategy.ValidityAuditStrategy stores both the start revision and the end revision. Together these define when an audit row was valid, hence the name ValidityAuditStrategy.
org.hibernate.envers.audit_strategy_validity_end_rev_field_name REVEND The column name that will hold the end revision number in audit entities. This property is only valid if the validity audit strategy is used.
org.hibernate.envers.audit_strategy_validity_store_revend_timestamp false Should the timestamp of the end revision be stored, until which the data was valid, in addition to the end revision itself. This is useful to be able to purge old Audit records out of a relational database by using table partitioning. Partitioning requires a column that exists within the table. This property is only evaluated if the ValidityAuditStrategy is used.
org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name REVEND_TSTMP Column name of the timestamp of the end revision until which the data was valid. Only used if the ValidityAuditStrategy is used, and org.hibernate.envers.audit_strategy_validity_store_revend_timestamp evaluates to true
org.hibernate.envers.track_entities_changed_in_revision false Should entity types, that have been modified during each revision, be tracked. The default implementation creates REVCHANGES table that stores entity names of modified persistent objects. Single record encapsulates the revision identifier (foreign key to REVINFO table) and a string value. For more information refer to Section 15.5.1, “Tracking entity names modified during revisions” and Section 15.6.3, “Querying for entities modified in a given revision”.

Important

The following configuration options have been added recently and should be regarded as experimental:

  1. org.hibernate.envers.track_entities_changed_in_revision

The name of the audit table can be set on a per-entity basis, using the @AuditTable annotation. It may be tedious to add this annotation to every audited entity, so if possible, it's better to use a prefix/suffix.

If you have a mapping with secondary tables, audit tables for them will be generated in the same way (by adding the prefix and suffix). If you wish to overwrite this behaviour, you can use the @SecondaryAuditTable and @SecondaryAuditTables annotations.

If you'd like to override auditing behaviour of some fields/properties inherited from @Mappedsuperclass or in an embedded component, you can apply the @AuditOverride(s) annotation on the subtype or usage site of the component.

If you want to audit a relation mapped with @OneToMany+@JoinColumn, please see Section 15.10, “Mapping exceptions” for a description of the additional @AuditJoinTable annotation that you'll probably want to use.

If you want to audit a relation, where the target entity is not audited (that is the case for example with dictionary-like entities, which don't change and don't have to be audited), just annotate it with @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED). Then, when reading historic versions of your entity, the relation will always point to the "current" related entity.

If you'd like to audit properties of a superclass of an entity, which are not explicitly audited (which don't have the @Audited annotation on any properties or on the class), you can list the superclasses in the auditParents attribute of the @Audited annotation. Please note that auditParents feature has been deprecated. Use @AuditOverride(forClass = SomeEntity.class, isAudited = true/false) instead.

After the basic configuration it is important to choose the audit strategy that will be used to persist and retrieve audit information. There is a trade-off between the performance of persisting and the performance of querying the audit information. Currently there two audit strategies.

When Envers starts a new revision, it creates a new revision entity which stores information about the revision. By default, that includes just

Envers handles this information as an entity. By default it uses its own internal class to act as the entity, mapped to the REVINFO table. You can, however, supply your own approach to collecting this information which might be useful to capture additional details such as who made a change or the ip address from which the request came. There are 2 things you need to make this work.

@Entity
@RevisionEntity( MyCustomRevisionListener.class )
public class MyCustomRevisionEntity {
    ...
}

public class MyCustomRevisionListener implements RevisionListener {
    public void newRevision(Object revisionEntity) {
        ( (MyCustomRevisionEntity) revisionEntity )...;
    }
}

An alternative method to using the org.hibernate.envers.RevisionListener is to instead call the getCurrentRevision method of the org.hibernate.envers.AuditReader interface to obtain the current revision, and fill it with desired information. The method accepts a persist parameter indicating whether the revision entity should be persisted prior to returning from this method. true ensures that the returned entity has access to its identifier value (revision number), but the revision entity will be persisted regardless of whether there are any audited entities changed. false means that the revision number will be null, but the revision entity will be persisted only if some audited entities have changed.


By default entity types that have been changed in each revision are not being tracked. This implies the necessity to query all tables storing audited data in order to retrieve changes made during specified revision. Envers provides a simple mechanism that creates REVCHANGES table which stores entity names of modified persistent objects. Single record encapsulates the revision identifier (foreign key to REVINFO table) and a string value.

Tracking of modified entity names can be enabled in three different ways:

Users, that have chosen one of the approaches listed above, can retrieve all entities modified in a specified revision by utilizing API described in Section 15.6.3, “Querying for entities modified in a given revision”.

Users are also allowed to implement custom mechanism of tracking modified entity types. In this case, they shall pass their own implementation of org.hibernate.envers.EntityTrackingRevisionListener interface as the value of @org.hibernate.envers.RevisionEntity annotation. EntityTrackingRevisionListener interface exposes one method that notifies whenever audited entity instance has been added, modified or removed within current revision boundaries.

Example 15.2. Custom implementation of tracking entity classes modified during revisions

                    CustomEntityTrackingRevisionListener.java

public class CustomEntityTrackingRevisionListener
             implements EntityTrackingRevisionListener {
    @Override
    public void entityChanged(Class entityClass, String entityName,
                              Serializable entityId, RevisionType revisionType,
                              Object revisionEntity) {
        String type = entityClass.getName();
        ((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityType(type);
    }

    @Override
    public void newRevision(Object revisionEntity) {
    }
}
                    CustomTrackingRevisionEntity.java

@Entity
@RevisionEntity(CustomEntityTrackingRevisionListener.class)
public class CustomTrackingRevisionEntity {
    @Id
    @GeneratedValue
    @RevisionNumber
    private int customId;

    @RevisionTimestamp
    private long customTimestamp;

    @OneToMany(mappedBy="revision", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
    private Set<ModifiedEntityTypeEntity> modifiedEntityTypes =
                                              new HashSet<ModifiedEntityTypeEntity>();
    
    public void addModifiedEntityType(String entityClassName) {
        modifiedEntityTypes.add(new ModifiedEntityTypeEntity(this, entityClassName));
    }
    
    ...
}
                    ModifiedEntityTypeEntity.java

@Entity
public class ModifiedEntityTypeEntity {
    @Id
    @GeneratedValue
    private Integer id;

    @ManyToOne
    private CustomTrackingRevisionEntity revision;
    
    private String entityClassName;
    
    ...
}
CustomTrackingRevisionEntity revEntity =
    getAuditReader().findRevision(CustomTrackingRevisionEntity.class, revisionNumber);
Set<ModifiedEntityTypeEntity> modifiedEntityTypes = revEntity.getModifiedEntityTypes()

You can think of historic data as having two dimension. The first - horizontal - is the state of the database at a given revision. Thus, you can query for entities as they were at revision N. The second - vertical - are the revisions, at which entities changed. Hence, you can query for revisions, in which a given entity changed.

The queries in Envers are similar to Hibernate Criteria, so if you are common with them, using Envers queries will be much easier.

The main limitation of the current queries implementation is that you cannot traverse relations. You can only specify constraints on the ids of the related entities, and only on the "owning" side of the relation. This however will be changed in future releases.

Please note, that queries on the audited data will be in many cases much slower than corresponding queries on "live" data, as they involve correlated subselects.

In the future, queries will be improved both in terms of speed and possibilities, when using the valid-time audit strategy, that is when storing both start and end revisions for entities. See ???.

The entry point for this type of queries is:

AuditQuery query = getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true);

You can add constraints to this query in the same way as to the previous one. There are some additional possibilities:

Using these methods, you can order the query results by revision number, set projection or constraint the revision number to be greater or less than a specified value, etc. For example, the following query will select the smallest revision number, at which entity of class MyEntity with id entityId has changed, after revision number 42:

Number revision = (Number) getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true)
    .setProjection(AuditEntity.revisionNumber().min())
    .add(AuditEntity.id().eq(entityId))
    .add(AuditEntity.revisionNumber().gt(42))
    .getSingleResult();

The second additional feature you can use in queries for revisions is the ability to maximalize/minimize a property. For example, if you want to select the revision, at which the value of the actualDate for a given entity was larger then a given value, but as small as possible:

Number revision = (Number) getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true)
    // We are only interested in the first revision
    .setProjection(AuditEntity.revisionNumber().min())
    .add(AuditEntity.property("actualDate").minimize()
        .add(AuditEntity.property("actualDate").ge(givenDate))
        .add(AuditEntity.id().eq(givenEntityId)))
    .getSingleResult();

The minimize() and maximize() methods return a criteria, to which you can add constraints, which must be met by the entities with the maximized/minimized properties.

You probably also noticed that there are two boolean parameters, passed when creating the query. The first one, selectEntitiesOnly, is only valid when you don't set an explicit projection. If true, the result of the query will be a list of entities (which changed at revisions satisfying the specified constraints).

If false, the result will be a list of three element arrays. The first element will be the changed entity instance. The second will be an entity containing revision data (if no custom entity is used, this will be an instance of DefaultRevisionEntity). The third will be the type of the revision (one of the values of the RevisionType enumeration: ADD, MOD, DEL).

The second parameter, selectDeletedEntities, specifies if revisions, in which the entity was deleted should be included in the results. If yes, such entities will have the revision type DEL and all fields, except the id, null.

For each audited entity (that is, for each entity containing at least one audited field), an audit table is created. By default, the audit table's name is created by adding a "_AUD" suffix to the original table name, but this can be overridden by specifying a different suffix/prefix in the configuration or per-entity using the @org.hibernate.envers.AuditTable annotation.

The primary key of the audit table is the combination of the original id of the entity and the revision number - there can be at most one historic entry for a given entity instance at a given revision.

The current entity data is stored in the original table and in the audit table. This is a duplication of data, however as this solution makes the query system much more powerful, and as memory is cheap, hopefully this won't be a major drawback for the users. A row in the audit table with entity id ID, revision N and data D means: entity with id ID has data D from revision N upwards. Hence, if we want to find an entity at revision M, we have to search for a row in the audit table, which has the revision number smaller or equal to M, but as large as possible. If no such row is found, or a row with a "deleted" marker is found, it means that the entity didn't exist at that revision.

The "revision type" field can currently have three values: 0, 1, 2, which means ADD, MOD and DEL, respectively. A row with a revision of type DEL will only contain the id of the entity and no data (all fields NULL), as it only serves as a marker saying "this entity was deleted at that revision".

Additionally, there is a <term>revision entity</term> table which contains the information about the global revision. By default the generated table is named REVINFO and contains just 2 columns: ID and TIMESTAMP. A row is inserted into this table on each new revision, that is, on each commit of a transaction, which changes audited data. The name of this table can be configured, the name of its columns as well as adding additional columns can be achieved as discussed in Section 15.5, “Revision Log”.

While global revisions are a good way to provide correct auditing of relations, some people have pointed out that this may be a bottleneck in systems, where data is very often modified. One viable solution is to introduce an option to have an entity "locally revisioned", that is revisions would be created for it independently. This wouldn't enable correct versioning of relations, but wouldn't also require the REVINFO table. Another possibility is to introduce a notion of "revisioning groups": groups of entities which share revision numbering. Each such group would have to consist of one or more strongly connected component of the graph induced by relations between entities. Your opinions on the subject are very welcome on the forum! :)

If you'd like to generate the database schema file with the Hibernate Tools Ant task, you'll probably notice that the generated file doesn't contain definitions of audit tables. To generate also the audit tables, you simply need to use org.hibernate.tool.ant.EnversHibernateToolTask instead of the usual org.hibernate.tool.ant.HibernateToolTask. The former class extends the latter, and only adds generation of the version entities. So you can use the task just as you used to.

For example:

<target name="schemaexport" depends="build-demo"
  description="Exports a generated schema to DB and file">
  <taskdef name="hibernatetool"
    classname="org.hibernate.tool.ant.EnversHibernateToolTask"
    classpathref="build.demo.classpath"/>

  <hibernatetool destdir=".">
    <classpath>
      <fileset refid="lib.hibernate" />
      <path location="${build.demo.dir}" />
      <path location="${build.main.dir}" />
    </classpath>
    <jpaconfiguration persistenceunit="ConsolePU" />
    <hbm2ddl
      drop="false"
      create="true"
      export="false"
      outputfilename="versioning-ddl.sql"
      delimiter=";"
      format="true"/>
  </hibernatetool>
</target>

Will generate the following schema:

    create table Address (
        id integer generated by default as identity (start with 1),
        flatNumber integer,
        houseNumber integer,
        streetName varchar(255),
        primary key (id)
    );

    create table Address_AUD (
        id integer not null,
        REV integer not null,
        flatNumber integer,
        houseNumber integer,
        streetName varchar(255),
        REVTYPE tinyint,
        primary key (id, REV)
    );

    create table Person (
        id integer generated by default as identity (start with 1),
        name varchar(255),
        surname varchar(255),
        address_id integer,
        primary key (id)
    );

    create table Person_AUD (
        id integer not null,
        REV integer not null,
        name varchar(255),
        surname varchar(255),
        REVTYPE tinyint,
        address_id integer,
        primary key (id, REV)
    );

    create table REVINFO (
        REV integer generated by default as identity (start with 1),
        REVTSTMP bigint,
        primary key (REV)
    );

    alter table Person
        add constraint FK8E488775E4C3EA63
        foreign key (address_id)
        references Address;
    

When a collection is mapped using these two annotations, Hibernate doesn't generate a join table. Envers, however, has to do this, so that when you read the revisions in which the related entity has changed, you don't get false results.

To be able to name the additional join table, there is a special annotation: @AuditJoinTable, which has similar semantics to JPA's @JoinTable.

One special case are relations mapped with @OneToMany+@JoinColumn on the one side, and @ManyToOne+@JoinColumn(insertable=false, updatable=false) on the many side. Such relations are in fact bidirectional, but the owning side is the collection (see alse here).

To properly audit such relations with Envers, you can use the @AuditMappedBy annotation. It enables you to specify the reverse property (using the mappedBy element). In case of indexed collections, the index column must also be mapped in the referenced entity (using @Column(insertable=false, updatable=false), and specified using positionMappedBy. This annotation will affect only the way Envers works. Please note that the annotation is experimental and may change in the future.

Generally SQL tables must be partitioned on a column that exists within the table. As a rule it makes sense to use either the end revision or the end revision timestamp column for partioning of audit tables.

The reason why the end revision information should be used for audit table partioning is based on the assumption that audit tables should be partionioned on an 'increasing level of interestingness', like so:

  1. A couple of partitions with audit data that is not very (or no longer) interesting. This can be stored on slow media, and perhaps even be purged eventually.

  2. Some partitions for audit data that is potentially interesting.

  3. One partition for audit data that is most likely to be interesting. This should be stored on the fastest media, both for reading and writing.

In order to determine a suitable column for the 'increasing level of interestingness', consider a simplified example of a salary registration for an unnamed agency.

Currently, the salary table contains the following rows for a certain person X:


The salary for the current fiscal year (2010) is unknown. The agency requires that all changes in registered salaries for a fiscal year are recorded (i.e. an audit trail). The rationale behind this is that decisions made at a certain date are based on the registered salary at that time. And at any time it must be possible reproduce the reason why a certain decision was made at a certain date.

The following audit information is available, sorted on in order of occurrence:


  1. Hibernate main page

  2. Forum

  3. JIRA issue tracker (when adding issues concerning Envers, be sure to select the "envers" component!)

  4. IRC channel

  5. Envers Blog

  6. FAQ

hibernate.dialectA fully-qualified classname

The classname of a Hibernate org.hibernate.dialect.Dialect from which Hibernate can generate SQL optimized for a particular relational database.

In most cases Hibernate can choose the correct org.hibernate.dialect.Dialect implementation based on the JDBC metadata returned by the JDBC driver.

hibernate.show_sql

true or false

Write all SQL statements to the console. This is an alternative to setting the log category org.hibernate.SQL to debug.
hibernate.format_sql

true or false

Pretty-print the SQL in the log and console.
hibernate.default_schemaA schema nameQualify unqualified table names with the given schema or tablespace in generated SQL.
hibernate.default_catalogA catalog nameQualifies unqualified table names with the given catalog in generated SQL.
hibernate.session_factory_nameA JNDI nameThe org.hibernate.SessionFactory is automatically bound to this name in JNDI after it is created.
hibernate.max_fetch_depthA value between 0 and 3Sets a maximum depth for the outer join fetch tree for single-ended associations. A single-ended assocation is a one-to-one or many-to-one assocation. A value of 0 disables default outer join fetching.
hibernate.default_batch_fetch_size

4,8, or 16

Default size for Hibernate batch fetching of associations.
hibernate.default_entity_mode

One of dynamic-map, dom4j, pojo

Default mode for entity representation for all sessions opened from this SessionFactory
hibernate.order_updates

true or false

Forces Hibernate to order SQL updates by the primary key value of the items being updated. This reduces the likelihood of transaction deadlocks in highly-concurrent systems.
hibernate.generate_statistics

true or false

Causes Hibernate to collect statistics for performance tuning.
hibernate.use_identifier_rollback

true or false

If true, generated identifier properties are reset to default values when objects are deleted.
hibernate.use_sql_comments

true or false

If true, Hibernate generates comments inside the SQL, for easier debugging.



Note

Each of the properties in the following table are prefixed by hibernate.. It has been removed in the table to conserve space.


Revision History
Revision 0-0Mon Jan 17 2011Dude McPants
Initial creation of book by publican