Help

Location: Austin, TX
Occupation: Software Developer
Archive
My Links
12. Sep 2013, 21:06 CET, by Steve Ebersole

Back to work after summer fun :)

This release has a lot of work, quite a bit in the form of pull requests. Thanks again!

A lot of the focus here has been on JPA 2.1 support. At this point:

  • AttributeConverter support should be complete; just about- there is still one minor issue which will be addressed in the next release
  • StoredProcedureQuery support should be complete; just about - there is still an issue here too which will be addressed in the next release
  • @ConstructorResult support is mostly done, though you currently can only use one ConstructorResult result per query which will be followed up by https://hibernate.atlassian.net/browse/HHH-8498
  • JPA schema generation support should be fully done at this point.
  • EntityGraph support is still not in place, though support for loading entities using LoadPlans (which is the internal fulfillment of entity graphs) is done and support for collections is under way.
  • Applications using JPA native (native SQL) query support should be aware of a potentially significant change to better align with the JPA spec. Essentially Hibernate will now flush the EntityManager before running the native query (as long as we are within a transaction). Note that this is a full flush, unlike HQL/JPQL or Criteria queries which perform a partial flush. In Hibernate native SQL query support, partial flush is achievable which is available to JPA native queries by unwrapping. For details see https://hibernate.atlassian.net/browse/HHH-8487.

Non-JPA-related changes to highlight include:

The full changelog can be found in Jira at https://hibernate.atlassian.net/browse/HHH/fixforversion/13652. Artifacts can be found in the usual places.

Also, along with this release came a new release (4.0.4.Final) of the hibernate-commons-annotations support library. For the 2 of you out there who care ;)

Anyway, we will now be back at a 4-week timebox. So the next release (Beta5, or CR if we get to that point by then) will be in 4 weeks from now.

Toodles.

29. May 2013, 21:20 CET, by Steve Ebersole

The Hibernate team is pleased to announce today's release of Hibernate 4.3.0.Beta3. Support for entity graphs is still a work in progress, but all other JPA 2.1 features should be fully operational. Specific JPA 2.1 features fully functional as of this release include:

For the full break down of changes, see the changelog.

10. Apr 2013, 17:24 CET, by Steve Ebersole

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:

  1. minimize the number of flushes that occur on a given persistence context (always a good idea anyway)
  2. 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.

03. Apr 2013, 21:52 CET, by Steve Ebersole

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() );
}
...
03. Apr 2013, 19:14 CET, by Steve Ebersole

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:

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.

Showing 6 to 10 of 73 blog entries