| Recent Entries |
|
10. Apr 2013
|
||
|
03. Apr 2013
|
||
|
06. Sep 2012
|
||
|
04. Apr 2012
|
||
|
08. Mar 2012
|
||
|
09. Feb 2012
|
||
|
24. Jan 2012
|
||
|
11. Jan 2012
|
||
|
09. Jan 2012
|
||
|
15. Dec 2011
|
||
|
10. Nov 2011
|
||
|
29. Sep 2011
|
||
|
14. Sep 2011
|
At flush-time, Hibernate needs to know what entity state has become dirty (changed) so that it knows what data needs to be written to the database and what data does not. Historically Hibernate only defined one means of such dirtiness checking, but has since added multiple additional schemes which seem to be not as well known. The intent for this blog is to start a base for improving the documentation around dirty checking and these various options.
flush-time state comparison strategy
In such a scheme, the loaded state
of an entity is kept around as part of the persistence context (Session/EntityManager). This loaded state is the last known state of the entity data as it exists in the database. That happens whenever the entity is loaded or whenever we update the entity from that persistence context. But either way, we have a last, well-known state
of the entity in the database as part of this transaction.
This strategy, then, at flush time, performs a deep comparison between that loaded state and the entity's current state. This happens for every entity associated with the persistence context on every flush, which can be a bit of a performance drain in long running persistence contexts and batch processing use-cases. In fact this is why the documentation discusses the importance of evicting as part of batch processing use-cases, but the same thing applies to long running persistence contexts. The best way to mitigate the performance impact of this strategy is to either:
- minimize the number of flushes that occur on a given persistence context (always a good idea anyway)
- minimize the number of entities that are associated with the persistence context during each flush
Such a strategy falls into a category of dirtiness checking that I call computed. Initially, this was the only strategy supported by Hibernate. However even as far back as 3.0 (which dates back 8-9 years ago!) Hibernate started branching out into tracked
dirtiness checking strategies. The most well known general approach to this is using bytecode enhancement.
Tracked approach : bytecode enhancement
Hibernate's first foray into tracked strategies was through bytecode enhancement, which was how most other JPA providers did tracked dirty checking. This is a process whereby your Java Class's bytecode is read and enhanced (changed) to introduce new bytecode level instructions. Hibernate supports this as an Ant task and also through runtime enhancment, although runtime enhancement currently requires a JPA container and container-managed EntityManagerFactory bootstrapping. Basically, Hibernate does not provide a Java agent to perform the enhancement at classload time. We are not against it so much as we just do not have time and no one has contributed such support to-date; though if you are interested... ;)
The enhancement weaves in support for the entity itself keeping track of when its state has changed. At flush time we can then check that flag rather than performing the state comparison computation.
To apply the build-time enhancement using the Ant task, you'd specify the Ant task like:
<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath ... >
</taskdef>
<instrument verbose="true">
<fileset dir="${yourClassesDir}">
<include name="*.class"/>
</fileset>
</instrument>
</target>
which performs the enhancement/instrumentation on your classes as defined by the nested Ant fileset. It is important that the classpath used to define the task contain the Hibernate jar, all its dependencies and your classes.
To use runtime enhancement, simply enable the setting hibernate.ejb.use_class_enhancer to true. Again, this requires that your application be using JPA container-managed EMF bootstrapping.
There are also third party maven plugins support Hibernate bytecode enhancement within a Maven build.
Be aware that work is under way (actually its first steps are already in the 4.2 release) to revamp bytecode enhancement support. See HHH-7667 and HHH-7963 for details.
Tracked approach : delegation
A new feature added in 4.1 allows all dirtiness checking to be delegated to application code. See HHH-3910 and HHH-6998 for complete design discussions. But essentially this feature allows your application to control how dirtiness checking happens. The idea is that your application model classes are monitoring their own dirtiness. Imagine an entity class like:
@Entity
public class MyEntity {
private Long id;
private String name;
@Id
public Long getId() { ... }
public void setId(Long id) { ... }
public Long getName() { ... }
public void setName(String name) {
if ( ! areEqual( this.name, name ) ) {
trackChange( "name", this.name );
}
this.name = name;
}
private Map<String,?> tracker;
private void trackChange(String attributeName, Object value) {
if ( tracker == null ) {
tracker = new HashMap<String,Object>();
}
else if ( tracker.containsKey( attributeName ) {
// no need to re-put, we want to keep the original value
return;
}
tracker.put( attributeName, value );
}
public boolean hadDirtyAttributes() {
return tracker != null && ! tracker.isEmpty();
}
public Set<String> getDirtyAttributeNames() {
return tracker.keySet();
}
public void resetDirtiness() {
if ( tracker != null ) {
tracker.clear();
}
}
}
Using the org.hibernate.CustomEntityDirtinessStrategy introduced in 4.1 as part of HHH-3910 we can easily tie the entity's intrinsic dirty checking into Hibernate's dirty checking:
public class CustomEntityDirtinessStrategyImpl implements CustomEntityDirtinessStrategy {
@Override
public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) {
// we only manage dirty checking for MyEntity instances (for this example; a CustomEntityDirtinessStrategy
// manage dirty checking for any number of entity types).
return MyEntity.class.isInstance( entity );
}
@Override
public boolean isDirty(Object entity, EntityPersister persister, Session session) {
return ( (MyEntity) entity ).hadDirtyAttributes();
}
@Override
public void findDirty(Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext) {
final MyEntity myEntity = (MyEntity) entity;
final Set<String> dirtyAttributeNames = entity.getDirtyAttributeNames();
dirtyCheckContext.doDirtyChecking(
new AttributeChecker() {
@Override
public boolean isDirty(AttributeInformation attributeInformation) {
return dirtyAttributeNames.contains( attributeInformation.getName() );
}
}
);
}
@Override
public void resetDirty(Object entity, EntityPersister persister, Session session) {
return ( (MyEntity) entity ).resetDirtiness();
}
}
This delegation could even be combined with some custom bytecode enhancement that you apply to your domain model in order to weave in dirtiness tracking. It also fits very nicely with dynamic models (using Map-backed proxies, etc).
There can currently be only one CustomEntityDirtinessStrategy associated with a SessionFactory/EntityManagerFactory. The CustomEntityDirtinessStrategy implementation to use is defined by the hibernate.entity_dirtiness_strategy setting.
Conclusion
Hopefully this gives a more clear picture of the possibilities for managing dirtiness checking in Hibernate. If things are still unclear discuss in comments and I'll try to aggregate all constructive criticisms and suggestions into the docs.
The soon-to-be final JPA 2.1 specification adds standardized support for dealing with JDBC CallableStatements (stored procedure and function calls). Arun Gupta has a decent summary of the initial JPA 2.1 features, including Stored procedure support, at https://blogs.oracle.com/arungupta/entry/jpa_2_1_early_draft. Standardized here means both across providers as well as across database vendors. Pretty sweet. As much as I liked the idea of standarized support for handling callable statements, I was not overly thrilled with certain aspects of the proposed JPA StoredProcedureQuery API. My worries were mainly around how the outputs were accessed, especially when multiple results are expected. Let's first look at a simple example of a procedure returning a result:
StoredProcedreQuery query = entityManager.createStoredProcedureQuery( "top10SalesmenByQuarter", Employee.class ); query.registerStoredProcedureParameter( "quarter", String.class, ParameterMode.IN ); query.setParameter( "quarter", "Q1-2000" ); List top10Salesmen = query.getResultList(); ...
Nothing too odious there.
However, imagine that we instead want to call a procedure that has a mix of update counts and results. This is where, in my humble opinion, the StoredProcedureQuery gets a bit dodgy. Largely it tries to follow the JDBC paradigm for accessing mixed returns. The argument for that approach of course is that it is familiar to developers familiar with the JDBC API. Lets take an example:
StoredProcedreQuery query = entityManager.createStoredProcedureQuery( "mixedReturns" );
...
while( 1==1 ) {
boolean isResult = query.hasMoreResults();
if ( isResult ) {
handleResult( query.getResultList() );
}
else {
int updateCount = query.getUpdateCount();
// complete exit condition is ( ! query.hasMoreResults() && query.getUpdateCount != -1 )
if ( updateCount == -1 ) {
break;
}
handleUpdateCount( updateCount );
}
}
...
To me, thats not very user friendly. However I was not able to get proposed changes to that API in. So I instead decided to develop an alternate API; A Hibernate-native API accessed through Session. The above query, using that API would look like:
org.hibernate.procedure.ProcedureCall call = entityManager.unwrap( Session.class ).createStoredProcedureCall( "mixedReturns" );
...
org.hibernate.procedure.ProcedureResult callResult = call.getResult();
while ( callResult.hasMoreReturns() ) {
final org.hibernate.result.Return return = callResult.getNextReturn();
if ( org.hibernate.result.ResultSetReturn.class.isInstance( return ) ) {
handleResult( ( (org.hibernate.result.ResultSetReturn) return ).getResultList() );
}
else {
handleUpdateCount( (org.hibernate.result.UpdateCountReturn) return ).getUpdateCount() );
}
}
...
Both APIs support processing of multiple ResultSets too. If return classes or result-set-mappings are supplied, they apply to all of the processed ResultSets:
StoredProcedreQuery query = entityManager.createStoredProcedureQuery( "top_and_bottom_salesmen_by_quarter", Employee.class );
query.registerStoredProcedureParameter( "quarter", String.class, ParameterMode.IN );
query.registerStoredProcedureParameter( "top_salesmen", String.class, ParameterMode.REF_CURSOR );
query.registerStoredProcedureParameter( "bottom_salesmen", String.class, ParameterMode.REF_CURSOR );
query.setParameter( "quarter", "Q1-2000" );
// we will end up with 2 result lists, where each list contains elements of type Employee. Pretty sweet!
boolean isResult = query.hasMoreResults();
while( isResult ) {
handleResult( query.getResultList() );
}
...
ProcedureCall call = entityManager.unwrap( Session.class ).createStoredProcedureCall( "top_and_bottom_salesmen_by_quarter", Employee.class );
call.registerParameter( "quarter", String.class, ParameterMode.IN );
call.registerParameter( "top_salesmen", String.class, ParameterMode.REF_CURSOR );
call.registerParameter( "bottom_salesmen", String.class, ParameterMode.REF_CURSOR );
call.setParameter( "quarter", "Q1-2000" );
// we will end up with 2 result lists, where each list contains elements of type Employee. Pretty sweet!
ProcedureResult callResult = call.getResult();
while ( callResult.hasMoreReturns() ) {
final ResultSetReturn rtn = (ResultSetReturn) callResult.getNextReturn();
handleResult( rtn.getResultList() );
}
...
The Hibernate team is pleased to announce today's release of Hibernate 4.3.0.Beta1 which targets the (still not finalized) JPA 2.1 specification which is part of the upcoming Java EE 7 platform. This is the first release targeting JPA 2.1 support. As mentioned, JPA 2.1 is not completely finalized so this support should be considered a preview. JPA 2.1 defines a number of enhancements. I won't go in depth in each of them here as I plan to follow up with separate in-depth blog posts for some of these features. However, the web abounds with good summaries of the new features; for example:
- https://blogs.oracle.com/arungupta/entry/jpa_2_1_early_draft
- https://blogs.oracle.com/arungupta/entry/jpa_2_1_schema_generation
One feature that I did not see discussed much is the notion of entity graphs
. This Beta1 release has very limited support for entity graphs
. You can define entity graphs, but at the moment they are not taken into account while loading data. This will be the focus of Beta2.
4.3 continues building on the OSGi support begun with 4.2, and also contains many other improvements and fixes. For the full break down of changes, see the changelog.
As usual, the artifacts have been uploaded to the JBoss Nexus repository (which is synchronized to Maven Central regularly) and the release bundles have been uploaded to the Hibernate SourceForge in both ZIP and TGZ formats.
Hibernate ORM version 4.1.7 has just been released. This is a bug fix release containing 27 fixes. See the changelog for the complete list of fixes. Again, 4.1.7 benefited from a lot of community involvement. We really appreciate all the help in terms of pull requests, testing, comments, ideas, etc.
As usual, the artifacts have been uploaded to the JBoss Nexus repository (which is synchronized to Maven Central regularly) and the release bundles have been uploaded to the Hibernate SourceForge in both ZIP and TGZ formats.
Hibernate ORM version 4.1.2 has just been released. This is a minor bug fix release containing 35 bug fixes. See the changelog for the complete list of fixes. Specific fixes of note include:
- Numerous fixes for 4.1-introduced load-by-natural-id feature. Many thanks to Eric Dalquist and Guenther Demetz for really hitting that feature hard, helping shake out issues and often developing fixes. Open Source at its finest! The most significant improvement here was removing the reliance on auto-flushing when performing a load-by-natural-id (HHH-7206)
- A few fixes (HHH-7017, HHH-7020, HHH-7090) related to org.hibernate.SharedSessionBuilder which is the contract used to build sessions from other sessions.
- HHH-7190 - Potentially major performance hit relating to proxy generation
- Update Infinispan dependency to version 5.1.2. Also support for natural-id caching added to the hibernate-infinispan integration.
As usual, the artifacts have been uploaded to the JBoss Nexus repository (and will be synchronized to Maven Central in a day or two) and the release bundles have been uploaded to the Hibernate SourceForge in both ZIP and TGZ formats.
FYI, I forgot to name this release with '.Final' qualifier in version. So the artifacts are just versioned 4.1.2.
| Showing 1 to 5 of 66 blog entries |
|
|