org.hibernate.engine.jdbc.batch.spi.BatchBuilder
org.hibernate.service.config.spi.ConfigurationService
org.hibernate.service.jdbc.connections.spi.ConnectionProvider
org.hibernate.service.jdbc.dialect.spi.DialectFactory
org.hibernate.service.jdbc.dialect.spi.DialectResolver
org.hibernate.engine.jdbc.spi.JdbcServices
org.hibernate.service.jmx.spi.JmxService
org.hibernate.service.jndi.spi.JndiService
org.hibernate.service.jta.platform.spi.JtaPlatform
org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider
org.hibernate.persister.spi.PersisterClassResolver
org.hibernate.persister.spi.PersisterFactory
org.hibernate.cache.spi.RegionFactory
org.hibernate.service.spi.SessionFactoryServiceRegistryFactory
org.hibernate.stat.Statistics
org.hibernate.engine.transaction.spi.TransactionFactory
org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor
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.
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.
Use Hibernate and report any bugs or issues you find. See http://hibernate.org/issuetracker.html for details.
Try your hand at fixing some bugs or implementing enhancements. Again, see http://hibernate.org/issuetracker.html.
Engage with the community using mailing lists, forums, IRC, or other ways listed at http://hibernate.org/community.html.
Help improve or translate this documentation. Contact us on the developer mailing list if you have interest.
Spread the word. Let the rest of your organization know about the benefits of Hibernate.
Hibernate connects to databases on behalf of your application. It can connect through a variety of mechanisms, including:
Stand-alone built-in connection pool
javax.sql.DataSource
Two different third-party opensource JDBC connection pools:
c3p0
proxool
The built-in connection pool is not intended for production environments.
You can configure database connections using a properties file, an XML deployment descriptor, programmatically.
Example 1.1. hibernate.properties
for a c3p0 connection pool
hibernate.connection.driver_class = org.postgresql.Driver hibernate.connection.url = jdbc:postgresql://localhost/mydatabase hibernate.connection.username = myuser hibernate.connection.password = secret hibernate.c3p0.min_size=5 hibernate.c3p0.max_size=20 hibernate.c3p0.timeout=1800 hibernate.c3p0.max_statements=50 hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
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>
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.
Example 1.3. Specifying the mapping files directly
You can obtain a org.hibernate.cfg.Configuration
instance by instantiating it
directly and specifying XML mapping documents. If the mapping files are in the classpath, use method
addResource()
.
Configuration cfg = new Configuration()
.addResource("Item.hbm.xml")
.addResource("Bid.hbm.xml");
Example 1.4. Letting Hibernate find the mapping files for you
The addClass()
method directs Hibernate to search the CLASSPATH for the mapping
files, eliminating hard-coded file names. In the following example, it searches for
org/hibernate/auction/Item.hbm.xml
and
org/hibernate/auction/Bid.hbm.xml
.
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class);
Example 1.5. Specifying configuration properties
Configuration cfg = new Configuration()
.addClass(org.hibernate.auction.Item.class)
.addClass(org.hibernate.auction.Bid.class)
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
.setProperty("hibernate.order_updates", "true");
Other ways to configure Hibernate programmatically
Pass an instance of java.util.Properties
to
Configuration.setProperties()
.
Set System properties using java
-D
property
=value
After you configure the Most important Hibernate JDBC properties, you can use method
openSession
of class org.hibernate.SessionFactory
to open
sessions. Sessions will obtain JDBC connections as needed based on the provided configuration.
Most important Hibernate JDBC properties
hibernate.connection.driver_class
hibernate.connection.url
hibernate.connection.username
hibernate.connection.password
hibernate.connection.pool_size
All available Hibernate settings are defined as constants and discussed on the
org.hibernate.cfg.AvailableSettings
interface. See its source code or
JavaDoc for details.
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.
C3P0 is an open source JDBC connection pool distributed along with Hibernate in the lib/
directory. Hibernate uses its org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider
for
connection pooling if you set the hibernate.c3p0.* properties. properties.
Important configuration properties for the c3p0 connection pool
hibernate.c3p0.min_size
hibernate.c3p0.max_size
hibernate.c3p0.timeout
hibernate.c3p0.max_statements
Proxool is another open source JDBC connection pool distributed along with Hibernate in the
lib/
directory. Hibernate uses its
org.hibernate.service.jdbc.connections.internal.ProxoolConnectionProvider
for connection pooling if you set the
hibernate.proxool.* properties. Unlike c3p0, proxool requires some additional configuration
parameters, as described by the Proxool documentation available at http://proxool.sourceforge.net/configure.html.
Table 1.1. Important configuration properties for the Proxool connection pool
Property | Description |
---|---|
hibernate.proxool.xml | Configure Proxool provider using an XML file (.xml is appended automatically) |
hibernate.proxool.properties | Configure the Proxool provider using a properties file (.properties is appended automatically) |
hibernate.proxool.existing_pool | Whether to configure the Proxool provider from an existing pool |
hibernate.proxool.pool_alias | Proxool pool alias to use. Required. |
To use Hibernate inside an application server, configure Hibernate to obtain connections from an application
server javax.sql.Datasource
registered in JNDI, by setting at least one of the following
properties:
Important Hibernate properties for JNDI datasources
hibernate.connection.datasource (required)
hibernate.jndi.url
hibernate.jndi.class
hibernate.connection.username
hibernate.connection.password
JDBC connections obtained from a JNDI datasource automatically participate in the container-managed transactions of the application server.
You can pass arbitrary connection properties by prepending hibernate.connection
to the
connection property name. For example, specify a charSet connection property as
hibernate.connection.charSet.
You can define your own plugin strategy for obtaining JDBC connections by implementing the interface
org.hibernate.service.jdbc.connections.spi.ConnectionProvider
and specifying your custom
implementation with the hibernate.connection.provider_class property.
In addition to the properties mentioned in the previous sections, Hibernate includes many other optional properties. See Appendix A, Configuration properties for a more complete list.
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.
Table 1.2. Supported database dialects
Database | Dialect |
---|---|
DB2 | org.hibernate.dialect.DB2Dialect |
DB2 AS/400 | org.hibernate.dialect.DB2400Dialect |
DB2 OS390 | org.hibernate.dialect.DB2390Dialect |
Firebird | org.hibernate.dialect.FirebirdDialect |
FrontBase | org.hibernate.dialect.FrontbaseDialect |
HypersonicSQL | org.hibernate.dialect.HSQLDialect |
Informix | org.hibernate.dialect.InformixDialect |
Interbase | org.hibernate.dialect.InterbaseDialect |
Ingres | org.hibernate.dialect.IngresDialect |
Microsoft SQL Server 2005 | org.hibernate.dialect.SQLServer2005Dialect |
Microsoft SQL Server 2008 | org.hibernate.dialect.SQLServer2008Dialect |
Mckoi SQL | org.hibernate.dialect.MckoiDialect |
MySQL | org.hibernate.dialect.MySQLDialect |
MySQL with InnoDB | org.hibernate.dialect.MySQL5InnoDBDialect |
MySQL with MyISAM | org.hibernate.dialect.MySQLMyISAMDialect |
Oracle 8i | org.hibernate.dialect.Oracle8iDialect |
Oracle 9i | org.hibernate.dialect.Oracle9iDialect |
Oracle 10g | org.hibernate.dialect.Oracle10gDialect |
Pointbase | org.hibernate.dialect.PointbaseDialect |
PostgreSQL | org.hibernate.dialect.PostgreSQLDialect |
Progress | org.hibernate.dialect.ProgressDialect |
SAP DB | org.hibernate.dialect.SAPDBDialect |
Sybase ASE 15.5 | org.hibernate.dialect.SybaseASE15Dialect |
Sybase ASE 15.7 | org.hibernate.dialect.SybaseASE157Dialect |
Sybase Anywhere | org.hibernate.dialect.SybaseAnywhereDialect |
The developer may manually specify the Dialect to use by setting the
hibernate.dialect configuration property to the name of a specific
org.hibernate.dialect.Dialect
class to use.
Assuming a org.hibernate.service.jdbc.connections.spi.ConnectionProvider
has been
set up, Hibernate will attempt to automatically determine the Dialect to use based on the
java.sql.DatabaseMetaData
reported by a
java.sql.Connection
obtained from that
org.hibernate.service.jdbc.connections.spi.ConnectionProvider
.
This functionality is provided by a series of
org.hibernate.service.jdbc.dialect.spi.DialectResolver
instances registered
with Hibernate internally. Hibernate comes with a standard set of recognitions. If your application
requires extra Dialect resolution capabilities, it would simply register a custom implementation
of org.hibernate.service.jdbc.dialect.spi.DialectResolver
as follows:
Registered org.hibernate.service.jdbc.dialect.spi.DialectResolver
are
prepended to an internal list of resolvers, so they take precedence
before any already registered resolvers including the standard one.
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.
You must specify a SQL Dialect via the hibernate.dialect property when using this tool, because DDL is highly vendor-specific. See Section 1.3, “Dialects” for information.
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.
Table 1.3. Elements and attributes provided for customizing mapping files
Name | Type of value | Description |
---|---|---|
length | number | Column length |
precision | number | Decimal precision of column |
scale | number | Decimal scale of column |
not-null |
| Whether a column is allowed to hold null values |
unique |
| Whether values in the column must be unique |
index | string | The name of a multi-column index |
unique-key | string | The name of a multi-column unique constraint |
foreign-key | string | The name of the foreign key constraint generated for an association. This applies to
<one-to-one>, <many-to-one>, <key>, and <many-to-many> mapping
elements. inverse="true" sides are skipped by SchemaExport. |
sql-type | string | Overrides the default column type. This applies to the <column> element only. |
default | string | Default value for the column |
check | string | An SQL check constraint on either a column or atable |
Procedure 1.1. Customizing the schema
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"/>
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"/>
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"/>
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>
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>
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>
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>
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>
The SchemaExport tool writes a DDL script to standard output, executes the DDL statements, or both.
Example 1.7. SchemaExport syntax
java -cp hibernate_classpaths org.hibernate.tool.hbm2ddl.SchemaExportoptions
mapping_files
Table 1.4. SchemaExport Options
Option | Description |
---|---|
--quiet | do not output the script to standard output |
--drop | only drop the tables |
--create | only create the tables |
--text | do not export to the database |
--output= | output the ddl script to a file |
--naming= | select a NamingStrategy |
--config= | read Hibernate configuration from an XML file |
--properties= | read database properties from a file |
--format | format the generated SQL nicely in the script |
--delimiter= | set an end-of-line delimiter for the script |
Example 1.8. Embedding SchemaExport into your application
Configuration cfg = ....;
new SchemaExport(cfg).create(false, true);
It is important to understand that the term transaction has many related, yet different meanings in regards to persistence and Object/Relational Mapping. In most use cases these definitions align, but that is not always the case.
It might refer to the physical transaction with the database.
It might refer to the logical notion of a transaction as related to a persistence context.
It might refer to the application notion of a Unit-of-Work, as defined by the archetypal pattern.
This documentation largely treats the physical and logic notions of transaction as one-in-the-same.
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:
It allows Hibernate to understand the transaction semantics of the environment. Are we operating in a JTA environment? Is a physical transaction already currently active? etc.
It acts as a factory for org.hibernate.Transaction
instances which
are used to allow applications to manage and check the state of transactions.
org.hibernate.Transaction
is Hibernate's notion of a logical
transaction. JPA has a similar notion in the
javax.persistence.EntityTransaction
interface.
org.hibernate.engine.transaction.spi.TransactionFactory
is a standard
Hibernate service. See Section 7.5.16, “org.hibernate.engine.transaction.spi.TransactionFactory
” for details.
JDBC-based transaction management leverages the JDBC defined methods
java.sql.Connection.commit()
and
java.sql.Connection.rollback()
(JDBC does not define and explicit
method of beginning a transaction). In Hibernate, this approach is represented by the
org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
class.
JTA-based transaction management leverages the JTA
javax.transaction.TransactionManager
interface as obtained from
org.hibernate.service.jta.platform.spi.JtaPlatform
API. This approach
is represented by the
org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory
class.
See Section 7.5.9, “org.hibernate.service.jta.platform.spi.JtaPlatform
” for information on integration with the underlying JTA
system.
CMT-based transaction management leverages the
javax.transaction.UserTransaction
interface as obtained from
org.hibernate.service.jta.platform.spi.JtaPlatform
API. This approach
is represented by the
org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
class.
See Section 7.5.9, “org.hibernate.service.jta.platform.spi.JtaPlatform
” for information on integration with the underlying JTA
system.
Its is also possible to plug in ones own transaction approach by implementing the
org.hibernate.engine.transaction.spi.TransactionFactory
contract.
The default service initiator has built-in support for understanding custom transaction approaches
via the hibernate.transaction.factory_class
which can name either:
The instance of org.hibernate.engine.transaction.spi.TransactionFactory
to use.
The name of a class implementing
org.hibernate.engine.transaction.spi.TransactionFactory
to use. The expectation is that the implementation class have a no-argument constructor.
During development of 4.0, most of these classes named here were moved to new packages. To help facilitate upgrading, Hibernate will also recognize the legacy names here for a short period of time.
org.hibernate.transaction.JDBCTransactionFactory
is mapped to
org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
org.hibernate.transaction.JTATransactionFactory
is mapped to
org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory
org.hibernate.transaction.CMTTransactionFactory
is mapped to
org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
Hibernate uses JDBC connections and JTA resources directly, without adding any additional locking behavior. It is important for you to become familiar with the JDBC, ANSI SQL, and transaction isolation specifics of your database management system.
Hibernate does not lock objects in memory. The behavior defined by the isolation level of your database
transactions does not change when you use Hibernate. The Hibernate
org.hibernate.Session
acts as a transaction-scoped cache providing
repeatable reads for lookup by identifier and entity queries and not-reporting queries that return scalar
values.
To reduce lock contention in the database, a database transaction needs to be as short as possible. Long
database transactions prevent your application from scaling to a highly-concurrent load. Do not hold a
database transaction open during end-user-level work, but open it after the end-user-level work is finished.
This is concept is referred to as transactional write-behind
.
Session-per-operation refers to the anti-pattern of opening and closing a
Session
for each database call in a single thread. It is also an anti-pattern in terms of
database transactions. Group your database calls into a planned sequence. In the same way, do not auto-commit
after every SQL statement in your application. Hibernate disables, or expects the application server to disable,
auto-commit mode immediately. Database transactions are never optional. All communication with a database must
be encapsulated by a transaction. Avoid auto-commit behavior for reading data, because many small transactions
are unlikely to perform better than one clearly-defined unit of work, and are more difficult to maintain and
extend.
For more anti-patterns to avoid, see Section 2.4.5, “Problems and anti-patterns”.
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:
ServletFilter
AOP interceptor with a pointcut on the service methods
A proxy/interception container
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:
Procedure 2.1. An example of a long-running conversation
The first screen of a dialog opens. The data seen by the user is loaded in a particular
Session
and database transaction. The user is free to modify the objects.
The user uses a UI element to save their work after five minutes of editing. The modifications are made persistent. The user also expects to have exclusive access to the data during the edit session.
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.
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.
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.
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.
If you use bean-managed transactions (BMT), Hibernate tells the application server to start and end a BMT transaction if you use the Transaction API. The transaction management code is identical to the non-managed environment.
Example 2.3. BMT idiom
// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
Alternately, you can use a transaction-bound Session. If you need the
getCurrentSession()
functionality for easy context propagation, use the JTA
UserTransaction API directly.
Example 2.4. BMT idiom with getCurrentSession()
// BMT idiom with getCurrentSession()
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work on Session bound to transaction
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
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.
Once you've created a new entity instance (using the standard new
operator) it is in
new
state. You can make it persistent by associating it to either a
org.hibernate.Session
or
javax.persistence.EntityManager
Example 3.1. Example of making an entity persistent
DomesticCat fritz = new DomesticCat(); fritz.setColor(Color.GINGER); fritz.setSex('M'); fritz.setName("Fritz"); session.save(fritz);
DomesticCat fritz = new DomesticCat(); fritz.setColor(Color.GINGER); fritz.setSex('M'); fritz.setName("Fritz"); entityManager.persist(fritz);
org.hibernate.Session
also has a method named persist
which follows the exact semantic defined in the JPA specification for the persist
method. It is this method on org.hibernate.Session
to which the
Hibernate javax.persistence.EntityManager
implementation delegates.
If the DomesticCat
entity type has a generated identifier, the value is associated
to the instance when the save
or persist
is called. If the
identifier is not automatically generated, the application-assigned (usually natural) key value has to be
set on the instance before save
or persist
is called.
Entities can also be deleted.
Example 3.2. Example of deleting an entity
session.delete( anAuthor );
entityManager.remove( anAuthor );
It is important to note that Hibernate itself can handle deleting detached state. JPA, however, disallows
it. The implication here is that the entity instance passed to the
org.hibernate.Session
delete
method can be either
in managed or detached state, while the entity instance passed to remove
on
javax.persistence.EntityManager
must be in managed state.
Sometimes referred to as lazy loading, the ability to obtain a reference to an entity without having to load its data is hugely important. The most common case being the need to create an association between an entity and another, existing entity.
Example 3.3. Example of obtaining an entity reference without initializing its data
Book book = new Book(); book.setAuthor( session.load( Author.class, authorId ) );
Book book = new Book(); book.setAuthor( entityManager.getReference( Author.class, authorId ) );
The above works on the assumption that the entity is defined to allow lazy loading, generally through use of runtime proxies. For more information see Chapter 9, Mapping entities. In both cases an exception will be thrown later if the given entity does not refer to actual database state if and when the application attempts to use the returned proxy.
It is also quite common to want to obtain an entity along with with its data, for display for example.
Example 3.4. Example of obtaining an entity reference with its data initialized
return session.get( Author.class, authorId );
return entityManager.find( Author.class, authorId );
In both cases null is returned if no matching database row was found.
You can reload an entity instance and it's collections at any time.
Example 3.5. Example of refreshing entity state
Cat cat = session.get( Cat.class, catId ); ... session.refresh( cat );
Cat cat = entityManager.find( Cat.class, catId ); ... entityManager.refresh( cat );
One case where this is useful is when it is known that the database state has changed since the data was read. Refreshing allows the current database state to be pulled into the entity instance and the persistence context.
Another case where this might be useful is when database triggers are used to initialize some of the
properties of the entity. Note that only the entity instance and its collections are refreshed unless you
specify REFRESH
as a cascade style of any associations. However, please note that
Hibernate has the capability to handle this automatically through its notion of generated properties.
See Chapter 9, Mapping entities for information.
Entities in managed/persistent state may be manipulated by the application and any changes will be automatically detected and persisted when the persistence context is flushed. There is no need to call a particular method to make your modifications persistent.
Example 3.6. Example of modifying managed state
Cat cat = session.get( Cat.class, catId ); cat.setName( "Garfield" ); session.flush(); // generally this is not explicitly needed
Cat cat = entityManager.find( Cat.class, catId ); cat.setName( "Garfield" ); entityManager.flush(); // generally this is not explicitly needed
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.
Reattachment is the process of taking an incoming entity instance that is in detached state and re-associating it with the current persistence context.
JPA does not provide for this model. This is only available through Hibernate
org.hibernate.Session
.
Example 3.7. Example of reattaching a detached entity
session.update( someDetachedCat );
session.saveOrUpdate( someDetachedCat );
The method name update
is a bit misleading here. It does not mean that an
SQL
UPDATE
is immediately performed. It does, however, mean that an
SQL
UPDATE
will be performed when the persistence context is flushed
since Hibernate does not know its previous state against which to compare for changes.
Provided the entity is detached, update
and saveOrUpdate
operate exactly the same.
Merging is the process of taking an incoming entity instance that is in detached state and copying its data over onto a new instance that is in managed state.
Example 3.8. Visualizing merge
Object detached = ...; Object managed = entityManager.find( detached.getClass(), detached.getId() ); managed.setXyz( detached.getXyz() ); ... return managed;
That is not exactly what happens, but its a good visualization.
Example 3.9. Example of merging a detached entity
Cat theManagedInstance = session.merge( someDetachedCat );
Cat theManagedInstance = entityManager.merge( someDetachedCat );
An application can verify the state of entities and collections in relation to the persistence context.
Example 3.10. Examples of verifying managed state
assert session.contains( cat );
assert entityManager.contains( cat );
Example 3.11. Examples of verifying laziness
if ( Hibernate.isInitialized( customer.getAddress() ) { //display address if loaded } if ( Hibernate.isInitialized( customer.getOrders ) ) { //display orders if loaded } if (Hibernate.isPropertyInitialized(customer, "detailedBio") ) { //display property detailedBio if loaded }
javax.persistence.PersistenceUnitUtil jpaUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil(); if ( jpaUtil.isLoaded( customer.getAddress() ) { //display address if loaded } if ( jpaUtil.isLoaded( customer.getOrders ) ) { //display orders if loaded } if (jpaUtil.isLoaded(customer, "detailedBio") ) { //display property detailedBio if loaded }
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
Example 3.12. Alternative JPA means to verify laziness
javax.persistence.PersistenceUtil jpaUtil = javax.persistence.Persistence.getPersistenceUtil(); if ( jpaUtil.isLoaded( customer.getAddress() ) { //display address if loaded } if ( jpaUtil.isLoaded( customer.getOrders ) ) { //display orders if loaded } if (jpaUtil.isLoaded(customer, "detailedBio") ) { //display property detailedBio if loaded }
The following example shows an antipattern for batch inserts.
Example 4.1. Naive way to insert 100000 lines with Hibernate
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
}
tx.commit();
session.close();
This fails with exception OutOfMemoryException
after around 50000 rows on most
systems. The reason is that Hibernate caches all the newly inserted Customer instances in the session-level
cache. There are several ways to avoid this problem.
Before batch processing, enable JDBC batching. To enable JDBC batching, set the property hibernate.jdbc.batch_size to an integer between 10 and 50.
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
.
When you make new objects persistent, employ methods flush()
and
clear()
to the session regularly, to control the size of the first-level cache.
Example 4.2. Flushing and clearing the Session
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
When you retriev and update data, flush()
and clear()
the
session regularly. In addition, use method scroll()
to take advantage of server-side
cursors for queries that return many rows of data.
Example 4.3. Using scroll()
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
if ( ++count % 20 == 0 ) {
//flush a batch of updates and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
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:
Features and behaviors not provided by StatelessSession
a first-level cache
interaction with any second-level or query cache
transactional write-behind or automatic dirty checking
Limitations of StatelessSession
Operations performed using a stateless session never cascade to associated instances.
Collections are ignored by a stateless session.
Operations performed via a stateless session bypass Hibernate's event model and interceptors.
Due to the lack of a first-level cache, Stateless sessions are vulnerable to data aliasing effects.
A stateless session is a lower-level abstraction that is much closer to the underlying JDBC.
Example 4.4. Using a StatelessSession
StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
.scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
session.update(customer);
}
tx.commit();
session.close();
The Customer
instances returned by the query are immediately detached. They are never
associated with any persistence context.
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).
Example 4.5. Psuedo-syntax for UPDATE and DELETE statements using HQL
( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?
The ?
suffix indications an optional parameter. The FROM
and
WHERE
clauses are each optional.
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.
Example 4.6. Executing an HQL UPDATE, using the Query.executeUpdate()
method
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
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.
Example 4.7. Updating the version of timestamp
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
If you use the VERSIONED
statement, you cannot use custom version types, which use class
org.hibernate.usertype.UserVersionType
.
Example 4.8. A HQL DELETE
statement
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer c where c.name = :oldName";
// or String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
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.
Example 4.9. Pseudo-syntax for INSERT statements
INSERT INTO EntityNameproperties_list
select_statement
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.
Example 4.10. HQL INSERT statement
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();
This section is only a brief overview of HQL. For more information, see Chapter 11, HQL and JPAQL.
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 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 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.
A version or timestamp property can never be null for a detached instance. Hibernate detects any instance with a null version or timestamp as transient, regardless of other unsaved-value strategies that you specify. Declaring a nullable version or timestamp property is an easy way to avoid problems with transitive reattachment in Hibernate, especially useful if you use assigned identifiers or composite keys.
The version number mechanism for optimistic locking is provided through a @Version
annotation.
Example 5.1. The @Version annotation
@Entity
public class Flight implements Serializable {
...
@Version
@Column(name="OPTLOCK")
public Integer getVersion() { ... }
}
Here, the version property is mapped to the OPTLOCK
column, and the entity manager uses it
to detect conflicting updates, and prevent the loss of updates that would be overwritten by a
last-commit-wins strategy.
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.
If the version number is generated by the database, such as a trigger, use the annotation
@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)
.
Example 5.2. Declaring a version property in hbm.xml
<version
column="version_column"
name="propertyName"
type="typename"
access="field|property|ClassName"
unsaved-value="null|negative|undefined"
generated="never|always"
insert="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>
column | The name of the column holding the version number. Optional, defaults to the property name. |
name | The name of a property of the persistent class. |
type | The type of the version number. Optional, defaults to
|
access | Hibernate's strategy for accessing the property value. Optional, defaults to
|
unsaved-value | Indicates that an instance is newly instantiated and thus unsaved. This distinguishes it
from detached instances that were saved or loaded in a previous session. The default value,
|
generated | Indicates that the version property value is generated by the database. Optional, defaults
to |
insert | Whether or not to include the |
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.
Example 5.3. Using timestamps for optimistic locking
@Entity
public class Flight implements Serializable {
...
@Version
public Date getLastUpdate() { ... }
}
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
|
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 |
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
|
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.
Hibernate always uses the locking mechanism of the database, and never lock objects in memory.
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 |
LockMode.UPGRADE_NOWAIT | acquired upon explicit user request using a |
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 |
The explicit user request mentioned above occurs as a consequence of any of the following actions:
A call to Session.load()
, specifying a LockMode.
A call to Session.lock()
.
A call to Query.setLockMode()
.
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.
Procedure 6.1. Enabling the query cache
Set the hibernate.cache.use_query_cache property to true
.
This setting creates two new cache regions:
org.hibernate.cache.internal.StandardQueryCache
holds the cached query results.
org.hibernate.cache.spi.UpdateTimestampsCache
holds timestamps of the most recent updates to
queryable tables. These timestamps validate results served from the query cache.
Adjust the cache timeout of the underlying cache region
If you configure your underlying cache implementation to use expiry or timeouts, set the cache timeout of the
underlying cache region for the UpdateTimestampsCache
to a higher value than the timeouts of any
of the query caches. It is possible, and recommended, to set the UpdateTimestampsCache region never to
expire. To be specific, a LRU (Least Recently Used) cache expiry policy is never appropriate.
Enable results caching for specific queries
Since most queries do not benefit from caching of their results, you need to enable caching for individual
queries, e ven after enabling query caching overall. To enable results caching for a particular query, call
org.hibernate.Query.setCacheable(true)
. This call allows the query to look for
existing cache results or add its results to the cache when it is executed.
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.
For fine-grained control over query cache expiration policies, specify a named cache region for a particular
query by calling Query.setCacheRegion()
.
Example 6.1. Method setCacheRegion
List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
.setEntity("blogger", blogger)
.setMaxResults(15)
.setCacheable(true)
.setCacheRegion("frontpages")
.list();
To force the query cache to refresh one of its regions and disregard any cached results in the region, call
org.hibernate.Query.setCacheMode(CacheMode.REFRESH)
. In conjunction with the region defined for the
given query, Hibernate selectively refreshes the results cached in that particular region. This is much more
efficient than bulk eviction of the region via org.hibernate.SessionFactory.evictQueries()
.
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.
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”.
Table 6.1. Possible values for Shared Cache Mode
Value | Description |
---|---|
ENABLE_SELECTIVE |
Entities are not cached unless you explicitly mark them as cachable. This is the default and recommended value. |
DISABLE_SELECTIVE |
Entities are cached unless you explicitly mark them as not cacheable. |
ALL |
All entities are always cached even if you mark them as not cacheable. |
NONE |
No entities are cached even if you mark them as cacheable. This option basically disables second-level caching. |
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.
When possible, define the cache concurrency strategy per entity rather than globally. Use the
@org.hibernate.annotations.Cache
annotation.
Example 6.2. Configuring cache providers using annotations
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Forest { ... }
You can cache the content of a collection or the identifiers, if the collection contains other entities. Use
the @Cache
annotation on the Collection property.
@Cache
can take several attributes.
Attributes of @Cache
annotation
The given cache concurrency strategy, which may be:
NONE
READ_ONLY
NONSTRICT_READ_WRITE
READ_WRITE
TRANSACTIONAL
The cache region. This attribute is optional, and defaults to the fully-qualified class name of the class, or the qually-qualified role name of the collection.
Whether or not to include all properties.. Optional, and can take one of two possible values.
A value of all
includes all properties. This is the default.
A value of non-lazy
only includes non-lazy properties.
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.
The caching strategy. This attribute is required, and can be any of the following values.
transactional
read-write
nonstrict-read-write
read-only
The name of the second-level cache region. This optional attribute defaults to the class or collection role name.
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
.
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.
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()
.
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()
.
To use the read-write strategy in a clustered environment, the underlying cache implementation must support locking. The build-in cache providers do not support locking.
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
.
Cache | Interface | Supported strategies | ||
---|---|---|---|---|
HashTable (testing only) |
| |||
EHCache |
| |||
OSCache |
| |||
SwarmCache |
| |||
JBoss Cache 1.x |
| |||
JBoss Cache 2.x |
|
Actions that add an item to internal cache of the Session
save()
update()
saveOrUpdate()
load()
get()
list()
iterate()
scroll()
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()
.
Example 6.4. Evicting an item from the first-level cache
ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
while ( cats.next() ) {
Cat cat = (Cat) cats.get(0);
doSomethingWithACat(cat);
sess.evict(cat);
}
The Session provides a contains()
method to determine if an instance belongs to the
session cache.
Example 6.5. Second-level cache eviction
You can evict the cached state of an instance, entire class, collection instance or entire collection role,
using methods of SessionFactory
.
sessionFactory.getCache().containsEntity(Cat.class, catId); // is this particular Cat currently in the cache
sessionFactory.getCache().evictEntity(Cat.class, catId); // evict a particular Cat
sessionFactory.getCache().evictEntityRegion(Cat.class); // evict all Cats
sessionFactory.getCache().evictEntityRegions(); // evict all entity data
sessionFactory.getCache().containsCollection("Cat.kittens", catId); // is this particular collection currently in the cache
sessionFactory.getCache().evictCollection("Cat.kittens", catId); // evict a particular collection of kittens
sessionFactory.getCache().evictCollectionRegion("Cat.kittens"); // evict all kitten collections
sessionFactory.getCache().evictCollectionRegions(); // evict all collection data
The CacheMode controls how a particular session interacts with the second-level cache.
CacheMode.NORMAL | reads items from and writes them to the second-level cache. |
CacheMode.GET | reads items from the second-level cache, but does not write to the second-level cache except to update data. |
CacheMode.PUT | writes 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. |
After enabling statistics, you can browse the contents of a second-level cache or query cache region.
Procedure 6.2. Enabling Statistics
Set hibernate.generate_statistics
to true
.
Optionally, set hibernate.cache.use_structured_entries
to true
, to cause
Hibernate to store the cache entries in a human-readable format.
Example 6.6. Browsing the second-level cache entries via the Statistics API
Map cacheEntries = sessionFactory.getStatistics()
.getSecondLevelCacheStatistics(regionName)
.getEntries();
org.hibernate.engine.jdbc.batch.spi.BatchBuilder
org.hibernate.service.config.spi.ConfigurationService
org.hibernate.service.jdbc.connections.spi.ConnectionProvider
org.hibernate.service.jdbc.dialect.spi.DialectFactory
org.hibernate.service.jdbc.dialect.spi.DialectResolver
org.hibernate.engine.jdbc.spi.JdbcServices
org.hibernate.service.jmx.spi.JmxService
org.hibernate.service.jndi.spi.JndiService
org.hibernate.service.jta.platform.spi.JtaPlatform
org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider
org.hibernate.persister.spi.PersisterClassResolver
org.hibernate.persister.spi.PersisterFactory
org.hibernate.cache.spi.RegionFactory
org.hibernate.service.spi.SessionFactoryServiceRegistryFactory
org.hibernate.stat.Statistics
org.hibernate.engine.transaction.spi.TransactionFactory
org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor
Services are classes that provide Hibernate with pluggable implementations of various types of functionality. Specifically they are implementations of certain service contract interfaces. The interface is known as the service role; the implementation class is know as the service implementation. Generally speaking, users can plug in alternate implementations of all standard service roles (overriding); they can also define additional services beyond the base set of service roles (extending).
The basic requirement for a service is to implement the marker interface
org.hibernate.service.Service
. Hibernate uses this internally for some
basic type safety.
Optionally, the service can also implement the
org.hibernate.service.spi.Startable
and
org.hibernate.service.spi.Stoppable
interfaces to receive notifications
of being started and stopped. Another optional service contract is
org.hibernate.service.spi.Manageable
which marks the service as manageable
in JMX provided the JMX integration is enabled.
Services are allowed to declare dependencies on other services using either of 2 approaches.
Any method on the service implementation class accepting a single parameter and annotated with
@InjectService
is considered requesting injection of another service.
By default the type of the method parameter is expected to be the service role to be injected. If the
parameter type is different than the service role, the serviceRole
attribute
of the InjectService
should be used to explicitly name the role.
By default injected services are considered required, that is the start up will fail if a named
dependent service is missing. If the service to be injected is optional, the
required
attribute of the InjectService
should be declared as false
(default is true
).
The second approach is a pull approach where the service implements the optional service interface
org.hibernate.service.spi.ServiceRegistryAwareService
which declares
a single injectServices
method. During startup, Hibernate will inject the
org.hibernate.service.ServiceRegistry
itself into services which
implement this interface. The service can then use the ServiceRegistry
reference to locate any additional services it needs.
The central service API, aside from the services themselves, is the
org.hibernate.service.ServiceRegistry
interface. The main purpose of
a service registry is to hold, manage and provide access to services.
Service registries are hierarchical. Services in one registry can depend on and utilize services in that same registry as well as any parent registries.
Use org.hibernate.service.ServiceRegistryBuilder
to build a
org.hibernate.service.ServiceRegistry
instance.
Defines strategy for how Hibernate manages JDBC statement batching
org.hibernate.engine.jdbc.batch.internal.BatchBuilderInitiator
org.hibernate.engine.jdbc.batch.internal.BatchBuilderImpl
Provides access to the configuration settings, combining those explicitly provided as well
as those contributed by any registered
org.hibernate.integrator.spi.Integrator
implementations
org.hibernate.service.config.internal.ConfigurationServiceInitiator
org.hibernate.service.config.internal.ConfigurationServiceImpl
Defines the means in which Hibernate can obtain and release
java.sql.Connection
instances for its use.
org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator
org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider
-
provides connection pooling based on integration with the C3P0 connection pooling library
org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl
-
provides connection managed delegated to a
javax.sql.DataSource
org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl
-
provides rudimentary connection pooling based on simple custom pool. Note intended
production use!
org.hibernate.service.jdbc.connections.internal.ProxoolConnectionProvider
-
provides connection pooling based on integration with the proxool connection pooling library
org.hibernate.service.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
-
Provides no connection support. Indicates the user will supply connections to Hibernate directly.
Not recommended for use.
Contract for Hibernate to obtain org.hibernate.dialect.Dialect
instance to use. This is either explicitly defined by the
hibernate.dialect property or determined by the
Section 7.5.5, “org.hibernate.service.jdbc.dialect.spi.DialectResolver
” service which is a delegate to this service.
org.hibernate.service.jdbc.dialect.internal.DialectFactoryInitiator
org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl
Provides resolution of org.hibernate.dialect.Dialect
to use based on
information extracted from JDBC metadata.
The standard resolver implementation acts as a chain, delegating to a series of individual
resolvers. The standard Hibernate resolution behavior is contained in
org.hibernate.service.jdbc.dialect.internal.StandardDialectResolver
.
org.hibernate.service.jdbc.dialect.internal.DialectResolverInitiator
also consults with the hibernate.dialect_resolvers setting for any
custom resolvers.
org.hibernate.service.jdbc.dialect.internal.DialectResolverInitiator
org.hibernate.service.jdbc.dialect.internal.DialectResolverSet
Special type of service that aggregates together a number of other services and provides a higher-level set of functionality.
org.hibernate.engine.jdbc.internal.JdbcServicesInitiator
org.hibernate.engine.jdbc.internal.JdbcServicesImpl
Provides simplified access to JMX related features needed by Hibernate.
org.hibernate.service.jmx.internal.JmxServiceInitiator
org.hibernate.service.jmx.internal.DisabledJmxServiceImpl
-
A no-op implementation when JMX functionality is disabled.
org.hibernate.service.jmx.internal.JmxServiceImpl
-
Standard implementation of JMX handling
Provides simplified access to JNDI related features needed by Hibernate.
org.hibernate.service.jndi.internal.JndiServiceInitiator
org.hibernate.service.jndi.internal.JndiServiceImpl
Provides an abstraction from the underlying JTA platform when JTA features are used.
org.hibernate.service.jta.platform.internal.JtaPlatformInitiator
JtaPlatformInitiator
provides mapping against the legacy,
now-deprecated org.hibernate.transaction.TransactionManagerLookup
names internally for the Hibernate-provided
org.hibernate.transaction.TransactionManagerLookup
implementations.
org.hibernate.service.jta.platform.internal.BitronixJtaPlatform
-
Integration with the Bitronix stand-alone transaction manager.
org.hibernate.service.jta.platform.internal.BorlandEnterpriseServerJtaPlatform
-
Integration with the transaction manager as deployed within a Borland Enterprise Server
org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform
-
Integration with the transaction manager as deployed within a JBoss Application Server
org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform
-
Integration with the JBoss Transactions stand-alone transaction manager
org.hibernate.service.jta.platform.internal.JOTMJtaPlatform
-
Integration with the JOTM stand-alone transaction manager
org.hibernate.service.jta.platform.internal.JOnASJtaPlatform
-
Integration with the JOnAS transaction manager.
org.hibernate.service.jta.platform.internal.JRun4JtaPlatform
-
Integration with the transaction manager as deployed in a JRun 4 application server.
org.hibernate.service.jta.platform.internal.NoJtaPlatform
-
No-op version when no JTA set up is configured
org.hibernate.service.jta.platform.internal.OC4JJtaPlatform
-
Integration with transaction manager as deployed in an OC4J (Oracle) application
server.
org.hibernate.service.jta.platform.internal.OrionJtaPlatform
-
Integration with transaction manager as deployed in an Orion application server.
org.hibernate.service.jta.platform.internal.ResinJtaPlatform
-
Integration with transaction manager as deployed in a Resin application server.
org.hibernate.service.jta.platform.internal.SunOneJtaPlatform
-
Integration with transaction manager as deployed in a Sun ONE (7 and above)
application server.
org.hibernate.service.jta.platform.internal.TransactionManagerLookupBridge
-
Provides a bridge to legacy (and deprecated)
org.hibernate.transaction.TransactionManagerLookup
implementations
org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform
-
Integration with transaction manager as deployed in a WebSphere Application Server
(6 and above).
org.hibernate.service.jta.platform.internal.WebSphereJtaPlatform
-
Integration with transaction manager as deployed in a WebSphere Application Server
(4, 5.0 and 5.1).
org.hibernate.service.jta.platform.internal.WeblogicJtaPlatform
-
Integration with transaction manager as deployed in a Weblogic application server.
A variation of Section 7.5.3, “org.hibernate.service.jdbc.connections.spi.ConnectionProvider
” providing access to JDBC
connections in multi-tenant environments.
N/A
Intended that users provide appropriate implementation if needed.
Contract for determining the appropriate
org.hibernate.persister.entity.EntityPersister
or org.hibernate.persister.collection.CollectionPersister
implementation class to use given an entity or collection mapping.
org.hibernate.persister.internal.PersisterClassResolverInitiator
org.hibernate.persister.internal.StandardPersisterClassResolver
Factory for creating
org.hibernate.persister.entity.EntityPersister
and org.hibernate.persister.collection.CollectionPersister
instances.
org.hibernate.persister.internal.PersisterFactoryInitiator
org.hibernate.persister.internal.PersisterFactoryImpl
Integration point for Hibernate's second level cache support.
org.hibernate.cache.internal.RegionFactoryInitiator
org.hibernate.cache.ehcache.EhCacheRegionFactory
org.hibernate.cache.infinispan.InfinispanRegionFactory
org.hibernate.cache.infinispan.JndiInfinispanRegionFactory
org.hibernate.cache.internal.NoCachingRegionFactory
org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
Factory for creating
org.hibernate.service.spi.SessionFactoryServiceRegistry
instances which acts as a specialized
org.hibernate.service.ServiceRegistry
for
org.hibernate.SessionFactory
scoped services. See
Section 7.7.2, “SessionFactory registry” for more details.
org.hibernate.service.internal.SessionFactoryServiceRegistryFactoryInitiator
org.hibernate.service.internal.SessionFactoryServiceRegistryFactoryImpl
Contract for exposing collected statistics. The statistics are collected through the
org.hibernate.stat.spi.StatisticsImplementor
contract.
org.hibernate.stat.internal.StatisticsInitiator
Defines a hibernate.stats.factory setting to allow
configuring the
org.hibernate.stat.spi.StatisticsFactory
to use internally
when building the actual
org.hibernate.stat.Statistics
instance.
org.hibernate.stat.internal.ConcurrentStatisticsImpl
The default org.hibernate.stat.spi.StatisticsFactory
implementation builds a
org.hibernate.stat.internal.ConcurrentStatisticsImpl
instance.
Strategy defining how Hibernate's org.hibernate.Transaction
API maps to the underlying transaction approach.
org.hibernate.stat.internal.StatisticsInitiator
Defines a hibernate.stats.factory setting to allow
configuring the
org.hibernate.stat.spi.StatisticsFactory
to use internally
when building the actual
org.hibernate.stat.Statistics
instance.
org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
-
A JTA-based strategy in which Hibernate is not controlling the transactions. An
important distinction here is that interaction with the underlying JTA implementation
is done through the
javax.transaction.TransactionManager
org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
-
A non-JTA strategy in which the transactions are managed using the JDBC
java.sql.Connection
org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory
-
A JTA-based strategy in which Hibernate *may* be controlling the transactions. An
important distinction here is that interaction with the underlying JTA
implementation is done through the
javax.transaction.UserTransaction
Contract for extracting statements from import.sql
scripts.
org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractorInitiator
org.hibernate.tool.hbm2ddl.SingleLineSqlCommandExtractor
Treads each line as a complete SQL statement. Comment lines shall start with
--
, //
or /*
character sequence.
org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor
Supports instructions/comments and quoted strings spread over multiple lines. Each statement must end with semicolon.
Once a org.hibernate.service.ServiceRegistry
is built it is considered
immutable; the services themselves might accept re-configuration, but immutability here means
adding/replacing services. So another role provided by the
org.hibernate.service.ServiceRegistryBuilder
is to allow tweaking of the services
that will be contained in the org.hibernate.service.ServiceRegistry
generated from it.
There are 2 means to tell a org.hibernate.service.ServiceRegistryBuilder
about
custom services.
Implement a org.hibernate.service.spi.BasicServiceInitiator
class
to control on-demand construction of the service class and add it to the
org.hibernate.service.ServiceRegistryBuilder
via its
addInitiator
method.
Just instantiate the service class and add it to the
org.hibernate.service.ServiceRegistryBuilder
via its
addService
method.
Either approach the adding a service approach or the adding an initiator approach are valid for extending a registry (adding new service roles) and overriding services (replacing service implementations).
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.
Coming soon... See https://hibernate.onjira.com/browse/HHH-6656
Applications, add-ons and others all need to integrate with Hibernate which used to require something, usually the application, to coordinate registering the pieces of each integration needed onm behalf of each integrator. The intent of this service is to allow those integrators to be discovered and to have them integrate themselves with Hibernate.
This service focuses on the discovery aspect. It leverages the Java
java.util.ServiceLoader
capability provided by the
org.hibernate.service.classloading.spi.ClassLoaderService
in order to discover implementations of the
org.hibernate.integrator.spi.Integrator
contract.
Integrators would simply define a file named
/META-INF/services/org.hibernate.integrator.spi.Integrator
and make it
available on the classpath. java.util.ServiceLoader
covers the
format of this file in detail, but essentially it list classes by FQN that implement the
org.hibernate.integrator.spi.Integrator
one per line.
While it is best practice to treat instances of all the registry types as targeting a given
org.hibernate.SessionFactory
, the instances of services in this group
explicitly belong to a single org.hibernate.SessionFactory
. The
difference is a matter of timing in when they need to be initiated. Generally they need access to the
org.hibernate.SessionFactory
to be initiated. This special registry is
org.hibernate.service.spi.SessionFactoryServiceRegistry
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.
There is a 3rd method defined on org.hibernate.integrator.spi.Integrator
,
an overloaded form of integrate
accepting a
org.hibernate.metamodel.source.MetadataImplementor
instead of
org.hibernate.cfg.Configuration
. This form is intended for use with the new
metamodel code scheduled for completion in 5.0
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.
Example 7.1. Registering event listeners
public class MyIntegrator implements org.hibernate.integrator.spi.Integrator { public void integrate( Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { // As you might expect, an EventListenerRegistry is the thing with which event listeners are registered It is a // service so we look it up using the service registry final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); // If you wish to have custom determination and handling of "duplicate" listeners, you would have to add an // implementation of the org.hibernate.event.service.spi.DuplicationStrategy contract like this eventListenerRegistry.addDuplicationStrategy( myDuplicationStrategy ); // EventListenerRegistry defines 3 ways to register listeners: // 1) This form overrides any existing registrations with eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, myCompleteSetOfListeners ); // 2) This form adds the specified listener(s) to the beginning of the listener chain eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledFirst ); // 3) This form adds the specified listener(s) to the end of the listener chain eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast ); } }
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.
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 type | Database type | JDBC type | Type registry |
---|---|---|---|
org.hibernate.type.StringType | string | VARCHAR | string, java.lang.String |
org.hibernate.type.MaterializedClob | string | CLOB | materialized_clob |
org.hibernate.type.TextType | string | LONGVARCHAR | text |
org.hibernate.type.CharacterType | char, java.lang.Character | CHAR | char, java.lang.Character |
org.hibernate.type.BooleanType | boolean | BIT | boolean, java.lang.Boolean |
org.hibernate.type.NumericBooleanType | boolean | INTEGER, 0 is false, 1 is true | numeric_boolean |
org.hibernate.type.YesNoType | boolean | CHAR, 'N'/'n' is false, 'Y'/'y' is true. The uppercase value is written to the database. | yes_no |
org.hibernate.type.TrueFalseType | boolean | CHAR, 'F'/'f' is false, 'T'/'t' is true. The uppercase value is written to the database. | true_false |
org.hibernate.type.ByteType | byte, java.lang.Byte | TINYINT | byte, java.lang.Byte |
org.hibernate.type.ShortType | short, java.lang.Short | SMALLINT | short, java.lang.Short |
org.hibernate.type.IntegerTypes | int, java.lang.Integer | INTEGER | int, java.lang.Integer |
org.hibernate.type.LongType | long, java.lang.Long | BIGINT | long, java.lang.Long |
org.hibernate.type.FloatType | float, java.lang.Float | FLOAT | float, java.lang.Float |
org.hibernate.type.DoubleType | double, java.lang.Double | DOUBLE | double, java.lang.Double |
org.hibernate.type.BigIntegerType | java.math.BigInteger | NUMERIC | big_integer |
org.hibernate.type.BigDecimalType | java.math.BigDecimal | NUMERIC | big_decimal, java.math.bigDecimal |
org.hibernate.type.TimestampType | java.sql.Timestamp | TIMESTAMP | timestamp, java.sql.Timestamp |
org.hibernate.type.TimeType | java.sql.Time | TIME | time, java.sql.Time |
org.hibernate.type.DateType | java.sql.Date | DATE | date, java.sql.Date |
org.hibernate.type.CalendarType | java.util.Calendar | TIMESTAMP | calendar, java.util.Calendar |
org.hibernate.type.CalendarDateType | java.util.Calendar | DATE | calendar_date |
org.hibernate.type.CurrencyType | java.util.Currency | VARCHAR | currency, java.util.Currency |
org.hibernate.type.LocaleType | java.util.Locale | VARCHAR | locale, java.utility.locale |
org.hibernate.type.TimeZoneType | java.util.TimeZone | VARCHAR, using the TimeZone ID | timezone, java.util.TimeZone |
org.hibernate.type.UrlType | java.net.URL | VARCHAR | url, java.net.URL |
org.hibernate.type.ClassType | java.lang.Class | VARCHAR, using the class name | class, java.lang.Class |
org.hibernate.type.BlobType | java.sql.Blob | BLOB | blog, java.sql.Blob |
org.hibernate.type.ClobType | java.sql.Clob | CLOB | clob, java.sql.Clob |
org.hibernate.type.BinaryType | primitive byte[] | VARBINARY | binary, byte[] |
org.hibernate.type.MaterializedBlobType | primitive byte[] | BLOB | materized_blob |
org.hibernate.type.ImageType | primitive byte[] | LONGVARBINARY | image |
org.hibernate.type.BinaryType | java.lang.Byte[] | VARBINARY | wrapper-binary |
org.hibernate.type.CharArrayType | char[] | VARCHAR | characters, char[] |
org.hibernate.type.CharacterArrayType | java.lang.Character[] | VARCHAR | wrapper-characters, Character[], java.lang.Character[] |
org.hibernate.type.UUIDBinaryType | java.util.UUID | BINARY | uuid-binary, java.util.UUID |
org.hibernate.type.UUIDCharType | java.util.UUID | CHAR, can also read VARCHAR | uuid-char |
org.hibernate.type.PostgresUUIDType | java.util.UUID | PostgreSQL UUID, through Types#OTHER, which complies to the PostgreSQL JDBC driver definition | pg-uuid |
org.hibernate.type.SerializableType | implementors of java.lang.Serializable | VARBINARY | 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. |
Composite types, or embedded types, as they are called by the Java Persistence API, have traditionally been called components in Hibernate. All of these terms mean the same thing.
Components represent aggregations of values into a single Java type. An example is an
Address
class, which aggregates street, city, state, and postal code. A composite type
behaves in a similar way to an entity. They are each classes written specifically for an application. They may
both include references to other application-specific classes, as well as to collections and simple JDK
types. The only distinguishing factors are that a component does not have its own lifecycle or define an
identifier.
Entities are application-specific classes which correlate to rows in a table, using a unique identifier. Because of the requirement for a unique identifier, ntities exist independently and define their own lifecycle. As an example, deleting a Membership should not delete the User or the Group. For more information, see the chapter on Persistent Classes.
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
The aim of Hibernate Envers is to provide historical versioning of your application's entity data. Much like source control management tools such as Subversion or Git, Hibernate Envers manages a notion of revisions if your application data through the use of audit tables. Each transaction relates to one global revision number which can be used to identify groups of changes (much like a change set in source control). As the revisions are global, having a revision number, you can query for various entities at that revision, retrieving a (partial) view of the database at that revision. You can find a revision number having a date, and the other way round, you can get the date at which a revision was committed.
To audit changes that are performed on an entity, you only need two things: the
hibernate-envers
jar on the classpath and an @Audited
annotation
on the entity.
Unlike in previous versions, you no longer need to specify listeners in the Hibernate configuration file. Just putting the Envers jar on the classpath is enough - listeners will be registered automatically.
And that's all - you can create, modify and delete the entites as always. If you look at the generated
schema for your entities, or at the data persisted by Hibernate, you will notice that there are no changes.
However, for each audited entity, a new table is introduced - entity_table_AUD
,
which stores the historical data, whenever you commit a transaction.
Instead of annotating the whole class and auditing all properties, you can annotate
only some persistent properties with @Audited
. This will cause only
these properties to be audited.
The audit (history) of an entity can be accessed using the AuditReader
interface, which
can be obtained having an open <listeral>EntityManager</listeral> or Session
via
the AuditReaderFactory
. See the javadocs for these classes for details on the
functionality offered.
It is possible to configure various aspects of Hibernate Envers behavior, such as table names, etc.
Table 15.1. Envers Configuration Properties
Property name | Default value | Description |
---|---|---|
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”.
|
The following configuration options have been added recently and should be regarded as experimental:
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.
The default audit strategy persists the audit data together with a start revision. For each row inserted, updated or deleted in an audited table, one or more rows are inserted in the audit tables, together with the start revision of its validity. Rows in the audit tables are never updated after insertion. Queries of audit information use subqueries to select the applicable rows in the audit tables. These subqueries are notoriously slow and difficult to index.
The alternative is a validity audit strategy. This strategy stores the start-revision and the end-revision of audit information. For each row inserted, updated or deleted in an audited table, one or more rows are inserted in the audit tables, together with the start revision of its validity. But at the same time the end-revision field of the previous audit rows (if available) are set to this revision. Queries on the audit information can then use 'between start and end revision' instead of subqueries as used by the default audit strategy.
The consequence of this strategy is that persisting audit information will be a bit slower, because of the extra updates involved, but retrieving audit information will be a lot faster. This can be improved by adding extra indexes.
When Envers starts a new revision, it creates a new revision entity which stores information about the revision. By default, that includes just
int/Integer
or
long/Long
). Essentially the primary key of the revision
long/Long
or
java.util.Date
value representing the instant at which the revision was made.
When using a java.util.Date
, instead of a long/Long
for
the revision timestamp, take care not to store it to a column data type which will loose precision.
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.
First, you will need to tell Envers about the entity you wish to use. Your entity must use the
@org.hibernate.envers.RevisionEntity
annotation. It must
define the 2 attributes described above annotated with
@org.hibernate.envers.RevisionNumber
and
@org.hibernate.envers.RevisionTimestamp
, respectively. You can extend
from org.hibernate.envers.DefaultRevisionEntity
, if you wish, to inherit all
these required behaviors.
Simply add the custom revision entity as you do your normal entities. Envers will "find it". Note
that it is an error for there to be multiple entities marked as
@org.hibernate.envers.RevisionEntity
Second, you need to tell Envers how to create instances of your revision entity which is handled
by the newRevision
method of the
org.jboss.envers.RevisionListener
interface.
You tell Envers your custom org.hibernate.envers.RevisionListener
implementation to use by specifying it on the
@org.hibernate.envers.RevisionEntity
annotation, using the
value
attribute. If your RevisionListener
class is inaccessible from @RevisionEntity
(e.g. exists in a different
module), set org.hibernate.envers.revision_listener property to it's fully
qualified name. Class name defined by the configuration parameter overrides revision entity's
value
attribute.
@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.
Example 15.1. Example of storing username with revision
ExampleRevEntity.java
package org.hibernate.envers.example;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.DefaultRevisionEntity;
import javax.persistence.Entity;
@Entity
@RevisionEntity(ExampleListener.class)
public class ExampleRevEntity extends DefaultRevisionEntity {
private String username;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
}
ExampleListener.java
package org.hibernate.envers.example;
import org.hibernate.envers.RevisionListener;
import org.jboss.seam.security.Identity;
import org.jboss.seam.Component;
public class ExampleListener implements RevisionListener {
public void newRevision(Object revisionEntity) {
ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity;
Identity identity =
(Identity) Component.getInstance("org.jboss.seam.security.identity");
exampleRevEntity.setUsername(identity.getUsername());
}
}
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:
true
. In this case
org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity
will
be implicitly used as the revision log entity.
org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity
class.
@Entity @RevisionEntity public class ExtendedRevisionEntity extends DefaultTrackingModifiedEntitiesRevisionEntity { ... }
@org.hibernate.envers.ModifiedEntityNames
annotation. The property is
required to be of Set<String>
type.
@Entity @RevisionEntity public class AnnotatedTrackingRevisionEntity { ... @ElementCollection @JoinTable(name = "REVCHANGES", joinColumns = @JoinColumn(name = "REV")) @Column(name = "ENTITYNAME") @ModifiedEntityNames private Set<String> modifiedEntityNames; ... }
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() .forEntitiesAtRevision(MyEntity.class, revisionNumber);
You can then specify constraints, which should be met by the entities returned, by
adding restrictions, which can be obtained using the AuditEntity
factory class. For example, to select only entities, where the "name" property
is equal to "John":
query.add(AuditEntity.property("name").eq("John"));
And to select only entites that are related to a given entity:
query.add(AuditEntity.property("address").eq(relatedEntityInstance)); // or query.add(AuditEntity.relatedId("address").eq(relatedEntityId));
You can limit the number of results, order them, and set aggregations and projections
(except grouping) in the usual way.
When your query is complete, you can obtain the results by calling the
getSingleResult()
or getResultList()
methods.
A full query, can look for example like this:
List personsAtAddress = getAuditReader().createQuery() .forEntitiesAtRevision(Person.class, 12) .addOrder(AuditEntity.property("surname").desc()) .add(AuditEntity.relatedId("address").eq(addressId)) .setFirstResult(4) .setMaxResults(2) .getResultList();
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 AuditEntity.revisionNumber()
you can specify constraints, projections
and order on the revision number, in which the audited entity was modified
similarly, using AuditEntity.revisionProperty(propertyName)
you can specify constraints,
projections and order on a property of the revision entity, corresponding to the revision
in which the audited entity was modified
AuditEntity.revisionType()
gives you access as above to the type of
the revision (ADD, MOD, DEL).
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
.
The basic query allows retrieving entity names and corresponding Java classes changed in a specified revision:
Set<Pair<String, Class>> modifiedEntityTypes = getAuditReader() .getCrossTypeRevisionChangesReader().findEntityTypes(revisionNumber);
Other queries (also accessible from org.hibernate.envers.CrossTypeRevisionChangesReader
):
List<Object> findEntities(Number)
- Returns snapshots of all audited entities changed (added, updated and removed) in a given revision.
Executes n+1
SQL queries, where n
is a number of different entity
classes modified within specified revision.
List<Object> findEntities(Number, RevisionType)
- Returns snapshots of all audited entities changed (added, updated or removed) in a given revision
filtered by modification type. Executes n+1
SQL queries, where n
is a number of different entity classes modified within specified revision.
Map<RevisionType, List<Object>> findEntitiesGroupByRevisionType(Number)
- Returns a map containing lists of entity snapshots grouped by modification operation (e.g.
addition, update and removal). Executes 3n+1
SQL queries, where n
is a number of different entity classes modified within specified revision.
Note that methods described above can be legally used only when default mechanism of tracking changed entity names is enabled (see Section 15.5.1, “Tracking entity names modified during revisions”).
Envers persists audit data in reaction to various Hibernate events (e.g. post update, post insert, and
so on), using a series of even listeners from the org.hibernate.envers.event
package. By default, if the Envers jar is in the classpath, the event listeners are auto-registered with
Hibernate.
Conditional auditing can be implemented by overriding some of the Envers event listeners. To use customized Envers event listeners, the following steps are needed:
hibernate.listeners.envers.autoRegister
Hibernate property to false
.
org.hibernate.envers.eventEnversPostInsertEventListenerImpl
class. Place the conditional-auditing logic in the subclasses, call the super method if auditing
should be performed.
org.hibernate.integrator.spi.Integrator
,
similar to org.hibernate.envers.event.EnversIntegrator
. Use your event listener
classes instead of the default ones.
META-INF/services/org.hibernate.integrator.spi.Integrator
file to your jar.
The file should contain the fully qualified name of the class implementing the interface.
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.
Audit table columns
id of the original entity (this can be more then one column in the case of composite primary keys)
revision number - an integer. Matches to the revision number in the revision entity table.
revision type - a small integer
audited fields from the original entity
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;
Bags (the corresponding Java type is List), as they can contain non-unique elements. The reason is that persisting, for example a bag of String-s, violates a principle of relational databases: that each table is a set of tuples. In case of bags, however (which require a join table), if there is a duplicate element, the two tuples corresponding to the elements will be the same. Hibernate allows this, however Envers (or more precisely: the database connector) will throw an exception when trying to persist two identical elements, because of a unique constraint violation.
There are at least two ways out if you need bag semantics:
use an indexed collection, with the @IndexColumn
annotation, or
provide a unique id for your elements with the @CollectionId
annotation.
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.
Because audit tables tend to grow indefinitely they can quickly become really large. When the audit tables have grown to a certain limit (varying per RDBMS and/or operating system) it makes sense to start using table partitioning. SQL table partitioning offers a lot of advantages including, but certainly not limited to:
Improved query performance by selectively moving rows to various partitions (or even purging old rows)
Faster data loads, index creation, etc.
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.
End revision information is not available for the default AuditStrategy.
Therefore the following Envers configuration options are required:
org.hibernate.envers.audit_strategy
=
org.hibernate.envers.strategy.ValidityAuditStrategy
org.hibernate.envers.audit_strategy_validity_store_revend_timestamp
=
true
Optionally, you can also override the default values following properties:
org.hibernate.envers.audit_strategy_validity_end_rev_field_name
org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name
For more information, see ???.
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:
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.
Some partitions for audit data that is potentially interesting.
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:
Table 15.3. Salaries - audit table
Year | Revision type | Revision timestamp | Salary (USD) | End revision timestamp |
---|---|---|---|---|
2006 | ADD | 2007-04-01 | 3300 | null |
2007 | ADD | 2008-04-01 | 35 | 2008-04-02 |
2007 | MOD | 2008-04-02 | 3500 | null |
2008 | ADD | 2009-04-01 | 3700 | 2009-07-01 |
2008 | MOD | 2009-07-01 | 4100 | 2010-02-01 |
2008 | MOD | 2010-02-01 | 4000 | null |
2009 | ADD | 2010-04-01 | 4500 | null |
To partition this data, the 'level of interestingness' must be defined. Consider the following:
For fiscal year 2006 there is only one revision. It has the oldest revision timestamp of all audit rows, but should still be regarded as interesting because it is the latest modification for this fiscal year in the salary table; its end revision timestamp is null.
Also note that it would be very unfortunate if in 2011 there would be an update of the salary for fiscal year 2006 (which is possible in until at least 10 years after the fiscal year) and the audit information would have been moved to a slow disk (based on the age of the revision timestamp). Remember that in this case Envers will have to update the end revision timestamp of the most recent audit row.
There are two revisions in the salary of fiscal year 2007 which both have nearly the same revision timestamp and a different end revision timestamp. On first sight it is evident that the first revision was a mistake and probably uninteresting. The only interesting revision for 2007 is the one with end revision timestamp null.
Based on the above, it is evident that only the end revision timestamp is suitable for audit table partitioning. The revision timestamp is not suitable.
A possible partitioning scheme for the salary table would be as follows:
end revision timestamp year = 2008
This partition contains audit data that is not very (or no longer) interesting.
end revision timestamp year = 2009
This partition contains audit data that is potentially interesting.
end revision timestamp year >= 2010 or null
This partition contains the most interesting audit data.
This partitioning scheme also covers the potential problem of the update of the end revision timestamp, which occurs if a row in the audited table is modified. Even though Envers will update the end revision timestamp of the audit row to the system date at the instant of modification, the audit row will remain in the same partition (the 'extension bucket').
And sometime in 2011, the last partition (or 'extension bucket') is split into two new partitions:
end revision timestamp year = 2010
This partition contains audit data that is potentially interesting (in 2011).
end revision timestamp year >= 2011 or null
This partition contains the most interesting audit data and is the new 'extension bucket'.
JIRA issue tracker (when adding issues concerning Envers, be sure to select the "envers" component!)
hibernate.dialect | A fully-qualified classname |
The classname of a Hibernate
In most cases Hibernate can choose the correct |
hibernate.show_sql |
| Write all SQL statements to the console. This is an alternative to setting the log category org.hibernate.SQL to debug. |
hibernate.format_sql |
| Pretty-print the SQL in the log and console. |
hibernate.default_schema | A schema name | Qualify unqualified table names with the given schema or tablespace in generated SQL. |
hibernate.default_catalog | A catalog name | Qualifies unqualified table names with the given catalog in generated SQL. |
hibernate.session_factory_name | A JNDI name | The org.hibernate.SessionFactory is automatically bound to this name in JNDI
after it is created. |
hibernate.max_fetch_depth | A value between 0 and 3 | Sets 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 |
| Default size for Hibernate batch fetching of associations. |
hibernate.default_entity_mode | One of | Default mode for entity representation for all sessions opened from this
SessionFactory |
hibernate.order_updates |
| 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 |
| Causes Hibernate to collect statistics for performance tuning. |
hibernate.use_identifier_rollback |
| If true, generated identifier properties are reset to default values when objects are deleted. |
hibernate.use_sql_comments |
| If true, Hibernate generates comments inside the SQL, for easier debugging. |
Table A.1. JDBC properties
Property | Example | Purpose |
---|---|---|
hibernate.jdbc.fetch_size | 0 or an integer | A non-zero value determines the JDBC fetch size, by calling
Statement.setFetchSize() . |
hibernate.jdbc.batch_size | A value between | A non-zero value causes Hibernate to use JDBC2 batch updates. |
hibernate.jdbc.batch_versioned_data |
| Set this property to |
hibernate.jdbc.factory_class | The fully-qualified class name of the factory | Select a custom |
hibernate.jdbc.use_scrollable_resultset |
| Enables Hibernate to use JDBC2 scrollable resultsets. This property is only relevant for user-supplied JDBC connections. Otherwise, Hibernate uses connection metadata. |
hibernate.jdbc.use_streams_for_binary |
| Use streams when writing or reading binary or serializable types to or from JDBC. This is a system-level property. |
hibernate.jdbc.use_get_generated_keys |
| Allows Hibernate to use JDBC3 |
Table A.2. Cache Properties
Property | Example | Purpose |
---|---|---|
hibernate.cache.provider_class | Fully-qualified classname | The classname of a custom CacheProvider. |
hibernate.cache.use_minimal_puts |
| Optimizes second-level cache operation to minimize writes, at the cost of more frequent reads. This is most useful for clustered caches and is enabled by default for clustered cache implementations. |
hibernate.cache.use_query_cache |
| Enables the query cache. You still need to set individual queries to be cachable. |
hibernate.cache.use_second_level_cache |
| Completely disable the second level cache, which is enabled by default for classes which specify a <cache> mapping. |
hibernate.cache.query_cache_factory | Fully-qualified classname | A custom QueryCache interface. The default is the built-in
StandardQueryCache . |
hibernate.cache.region_prefix | A string | A prefix for second-level cache region names. |
hibernate.cache.use_structured_entries |
| Forces Hibernate to store data in the second-level cache in a more human-readable format. |
Table A.3. Transactions properties
Property | Example | Purpose |
---|---|---|
hibernate.transaction.factory_class | A fully-qualified classname | The classname of a TransactionFactory to use with Hibernate Transaction API. The
default is JDBCTransactionFactory ). |
jta.UserTransaction | A JNDI name | The |
hibernate.transaction.manager_lookup_class | A fully-qualified classname | The classname of a |
hibernate.transaction.flush_before_completion |
| Causes the session be flushed during the before completion phase of the transaction. If possible, use built-in and automatic session context management instead. |
hibernate.transaction.auto_close_session |
| Causes the session to be closed during the after completion phase of the transaction. If possible, use built-in and automatic session context management instead. |
Each of the properties in the following table are prefixed by hibernate.
. It has been removed
in the table to conserve space.
Table A.4. Miscellaneous properties
Property | Example | Purpose |
---|---|---|
current_session_context_class | One of | Supply a custom strategy for the scoping of the |
factory_class |
| Chooses the HQL parser implementation. |
query.substitutions |
| Map from tokens in Hibernate queries to SQL tokens, such as function or literal names. |
hbm2ddl.auto |
| Validates or exports schema DDL to the database when the SessionFactory is
created. With create-drop , the database schema is dropped when the
SessionFactory is closed explicitly. |
cglib.use_reflection_optimizer |
| If enabled, Hibernate uses CGLIB instead of runtime reflection. This is a system-level property. Reflection is useful for troubleshooting. Hibernate always requires CGLIB even if you disable the optimizer. You cannot set this property in hibernate.cfg.xml. |
c3p0 connection pool properties
hibernate.c3p0.min_size
hibernate.c3p0.max_size
hibernate.c3p0.timeout
hibernate.c3p0.max_statements
Table A.5. Proxool connection pool properties
Property | Description |
---|---|
hibernate.proxool.xml | Configure Proxool provider using an XML file (.xml is appended automatically) |
hibernate.proxool.properties | Configure the Proxool provider using a properties file (.properties is appended automatically) |
hibernate.proxool.existing_pool | Whether to configure the Proxool provider from an existing pool |
hibernate.proxool.pool_alias | Proxool pool alias to use. Required. |
For information on specific configuration of Proxool, refer to the Proxool documentation available from http://proxool.sourceforge.net/.
Revision History | |||
---|---|---|---|
Revision 0-0 | Mon Jan 17 2011 | ||
|
Copyright © 2011 Red Hat, Inc.