Hibernate.orgCommunity Documentation
Hibernate OGM has very few specific APIs. For the most part, you will interact with it via either:
This chapter will only discuss the Hibernate OGM specific behaviors regarding these APIs. If you need to learn JPA or the native Hibernate APIs, check out the Hibernate ORM documentation.
We already discussed this subject earlier, have a look at Chapter 4, Configure and start Hibernate OGM for all the details.
As a reminder, it basically boils down to either:
persistence.xml
file
and create an EntityManagerFactory
the usual wayStandardServiceRegistryBuilder
and MetadataSources
to boot a SessionFactory
You know of the Java Persistence and Hibernate ORM native APIs? You are pretty much good to go. If you need a refresher, make sure you read the Hibernate ORM documentation.
A few things are a bit different though, let’s discuss them.
Most of the EntityManager
and Session
contracts are supported.
Here are the few exceptions:
Session.createCriteria
: criteria queries are not yet supported in Hibernate OGMSession.createFilter
: queries on collections are not supported yetSession
's enableFilter
, disableFilter
etc: query filters are not supported at the momentdoWork
and doReturningWork
are not implemented as they rely on JDBC connections - see
OGM-694Session
's stored procedure APIs are not supportedSession
's natural id APIs are not yet supportedSession.lock
is not fully supported at this timeEntityManager
's criteria query APIs are not supportedEntityManager
's stored procedure APIs are not supported - see
OGM-695EntityManager.lock
is not fully supported at this timeTo execute NoSQL native queries, one approach is to use OgmSession#createNativeQuery
.
You can read more about it in Section 7.2, “Using the native query language of your NoSQL”.
But let’s see how to access an OgmSession
instance.
From JPA, use the unwrap
method of EntityManager
Example 6.1. Get to an OgmSession
from an EntityManager
EntityManager entityManager = ...
OgmSession ogmSession = entityManager.unwrap(OgmSession.class);
NoSQLQuery query = ogmSession.createNativeQuery(...);
In the Hibernate native API case, you should already have access to an OgmSession
.
The OgmConfiguration
you used returns an OgmSessionFactory
.
This factory in turns produces OgmSession
.
Example 6.2. Get to an OgmSession
with Hibernate ORM native APIs
StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.applySetting( OgmProperties.ENABLED, true )
.build();
OgmSessionFactory ogmSessionFactory = new MetadataSources( registry )
.buildMetadata()
.getSessionFactoryBuilder()
.unwrap( OgmSessionFactoryBuilder.class )
.build();
OgmSession ogmSession = ogmSessionFactory.openSession();
NoSQLQuery query = ogmSession.createNativeQuery(...);
Even though some underlying NoSQL datastores do not support transaction, it is important to demarcate transaction via the Hibernate OGM APIs. Let’s see why.
Hibernate does pile up changes for as long as it can before pushing them down to the datastore.
This opens up the doors to huge optimizations (avoiding duplication, batching operations etc).
You can force changes to be sent to the datastore by calling Session.flush
or EntityManager.flush
.
In some situations - for example before some queries are executed -, Hibernate will flush automatically.
It will also flush when the transaction demarcation happens (whether there is a real transaction or not).
The best approach is to always demarcate the transaction as shown below. This avoids the needs to manually call flush and will offer future opportunities for Hibernate OGM.
Example 6.3. Explicitly demarcating transactions
Here is how you do outside of a JTA environment.
Session session = ...
Transaction transaction = session.beginTransaction();
try {
// do your work
transaction.commit(); // will flush changes to the datastore
catch (Exception e) {
transaction.rollback();
}
// or in JPA
EntityManager entityManager = ...
EntityTransaction transaction = entityManager.getTransaction();
try {
// do your work
transaction.commit(); // will flush changes to the datastore
}
catch (Exception e) {
transaction.rollback();
}
Inside a JTA environment, either the container demarcates the transaction for you
and Hibernate OGM will transparently join that transaction and flush at commit time.
Or you need to manually demarcate the transaction.
In the latter case,
it is best to start / stop the transaction before retrieving the Session
or EntityManager
as shown below.
The alternative is to call the EntityManager.joinTransaction()
once the transaction has started.
transactionManager.begin();
Session session = sessionFactory.openSession();
// do your work
transactionManager.commit(); // will flush changes to the datastore
// or in JPA
transactionManager.begin();
EntityManager entityManager = entityManagerFactory.createEntityManager();
// do your work
transactionManager.commit(); // will flush changes to the datastore
The error compensation API described in the following is an experimental feature. It will be enriched with additional features over time. This might require changes to existing method signatures and thus may break code using a previous version of the API.
Please let us know about your usage of the API and your wishes regarding futher capabilities!
If an error occurs during flushing a set of changes, some data changes may already have been applied in the datastore. If the store is non-transactional, there is no way to rollback (undo) these changes if they were already flushed. In this case it is desirable to know which changes have been applied and which ones failed in order to take appropriate action.
Hibernate OGM provides an error compensation API for this purpose.
By implementing the org.hibernate.ogm.failure.ErrorHandler
interface, you will be notified if
Use cases for the error compensation API include:
In its current form the API lays the ground for manually performing these and similar tasks, but we envision a more automated approach in future versions, e.g. for automatic retries of failed operations or the automatic application of compensating operations.
Let’s take a look at an example:
Example 6.4. Custom ErrorHandler
implementation
public class ExampleErrorHandler extends BaseErrorHandler {
@Override
public void onRollback(RollbackContext context) {
// write all applied operations to a log file
for ( GridDialectOperation appliedOperation : context.getAppliedGridDialectOperations() ) {
switch ( appliedOperation.getType() ) {
case INSERT_TUPLE:
EntityKeyMetadata entityKeyMetadata = appliedOperation.as( InsertTuple.class ).getEntityKeyMetadata();
Tuple tuple = appliedOperation.as( InsertTuple.class ).getTuple();
// write EKM and tuple to log file...
break;
case REMOVE_TUPLE:
// ...
break;
case ...
// ...
break;
}
}
}
@Override
public ErrorHandlingStrategy onFailedGridDialectOperation(FailedGridDialectOperationContext context) {
// Ignore this exception and continue
if ( context.getException() instanceof TupleAlreadyExistsException ) {
GridDialectOperation failedOperation = context.getFailedOperation();
// write to log ...
return ErrorHandlingStrategy.CONTINUE;
}
// But abort on all others
else {
return ErrorHandlingStrategy.ABORT;
}
}
}
The onRollback()
method - which is called when the transaction is rolled back (either by the user or by the container) - shows how to iterate over all methods applied prior to the rollback, examine their specific type and e.g. write them to a log file.
The onFailedGridDialectOperation()
method is called for each specific datastore operation failing.
It lets you decide whether to continue ignoring the failure, retry or abort the operation.
If ABORT
is returned, the causing exception will be re-thrown, eventually causing the current transaction to be rolled back.
If CONTINUE
is returned, that exception will be ignored, causing the current transaction to continue.
The decision whether to abort or continue can be based on the specific exception type or on the grid dialect operation which caused the failure.
In the example all exceptions of type TupleAlreadyExistsException
are ignored, whereas all other exceptions cause the current flush cycle to be aborted. You also could react to datastore-specific exceptions such as MongoDB’s MongoTimeoutException
, if needed.
Note that by extending the provided base class BaseErrorHandler
rather than implementing the interface directly,
you only need to implement those callback methods you are actually interested in.
The implementation will also not break if further callback methods are added to the ErrorHandler
interface in future releases.
Having implemented the error handler, it needs to be registered with Hibernate OGM.
To do so, specify it using the property hibernate.ogm.error_handler
,
e.g. as a persistence unit property in META-INF/persistence.xml
:
<property name="hibernate.ogm.error_handler" value="com.example.ExampleErrorHandler"/>
Some of the Hibernate OGM public contracts are geared towards either integrators
or implementors of datastore providers.
They should not be used by a regular application.
These contracts are named SPIs and are in a .spi
package.
To keep improving Hibernate OGM, we might break these SPIs between versions. If you plan on writing a datastore, come and talk to us.
Non public contracts are stored within a .impl
package.
If you see yourself using one of these classes,
beware that we can break these without notice.