• About

N1nja Hacks

~ Random assortment of solutions, sneaky hacks and technical HOWTOs

N1nja Hacks

Category Archives: Hibernate

JPA callbacks with Hibernate’s SessionFactory and no EntityManager

07 Friday Oct 2016

Posted by valblant in Hibernate

≈ Leave a comment

I wanted to use JPA callback annotations, such as @PostLoad
and @PostUpdate, but realized that those JPA annotations do not work, unless Hibernate is configured to use a JPA EnityManager. My project uses Hibernate’s SessionFactory, so these annotations are not available to me out of the box.

So, how do we configure Hibernate to get the best of both worlds? Here’s how I did it in Hibernate 5. Hibernate 4 can use a very similar approach, but the code would be slightly different (just grab it from org.hibernate.jpa.event.spi.JpaIntegrator)

Luckily, Hibernate’s IntegratorServiceImpl uses the java.util.ServiceLoader API, so we can specify an additional list of org.hibernate.integrator.spi.Integrator implementations we want the SessionFactory to use.

All we need to do is specify a service provider for org.hibernate.integrator.spi.Integrator in:

META-INF/services/org.hibernate.integrator.spi.Integrator:

# This allows us to use JPA-style annotation on entities, such as @PostLoad
our.custom.JpaAnnotationsIntegrator

You will also need to ensure that ‘hibernate-entitymanager‘ jar of the appropriate version is on your classpath.

our.custom.JpaAnnotationsIntegrator (taken from org.hibernate.jpa.event.spi.JpaIntegrator):

package our.custom;

import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.internal.MetadataImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.jpa.event.internal.core.JpaPostDeleteEventListener;
import org.hibernate.jpa.event.internal.core.JpaPostInsertEventListener;
import org.hibernate.jpa.event.internal.core.JpaPostLoadEventListener;
import org.hibernate.jpa.event.internal.core.JpaPostUpdateEventListener;
import org.hibernate.jpa.event.internal.jpa.CallbackBuilderLegacyImpl;
import org.hibernate.jpa.event.internal.jpa.CallbackRegistryImpl;
import org.hibernate.jpa.event.spi.jpa.CallbackBuilder;
import org.hibernate.jpa.event.spi.jpa.ListenerFactory;
import org.hibernate.jpa.event.spi.jpa.ListenerFactoryBuilder;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;

/**
 * This integrator allows us to use JPA-style post op annotations on Hibernate entities.
 *

 * This integrator is loaded by <code>org.hibernate.integrator.internal.IntegratorServiceImpl</code> from
 * <code>META-INF/services/org.hibernate.integrator.spi.Integrator</code> file.
 *

 * <b>Note</b>: This code is lifted directly from <code>org.hibernate.jpa.event.spi.JpaIntegrator</code>
 *
 * @author Val Blant
 */
public class JpaAnnotationsIntegrator implements Integrator {
	private ListenerFactory jpaListenerFactory;
	private CallbackBuilder callbackBuilder;
	private CallbackRegistryImpl callbackRegistry;

	@Override
	public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
		final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );

		this.callbackRegistry = new CallbackRegistryImpl();

		// post op listeners
		eventListenerRegistry.prependListeners( EventType.POST_DELETE, new JpaPostDeleteEventListener(callbackRegistry) );
		eventListenerRegistry.prependListeners( EventType.POST_INSERT, new JpaPostInsertEventListener(callbackRegistry) );
		eventListenerRegistry.prependListeners( EventType.POST_LOAD, new JpaPostLoadEventListener(callbackRegistry) );
		eventListenerRegistry.prependListeners( EventType.POST_UPDATE, new JpaPostUpdateEventListener(callbackRegistry) );

		// handle JPA "entity listener classes"...
		final ReflectionManager reflectionManager = ( (MetadataImpl) metadata )
				.getMetadataBuildingOptions()
				.getReflectionManager();

		this.jpaListenerFactory = ListenerFactoryBuilder.buildListenerFactory( sessionFactory.getSessionFactoryOptions() );
		this.callbackBuilder = new CallbackBuilderLegacyImpl( jpaListenerFactory, reflectionManager );
		for ( PersistentClass persistentClass : metadata.getEntityBindings() ) {
			if ( persistentClass.getClassName() == null ) {
				// we can have non java class persisted by hibernate
				continue;
			}
			callbackBuilder.buildCallbacksForEntity( persistentClass.getClassName(), callbackRegistry );
		}
	}

	@Override
	public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
		if ( callbackRegistry != null ) {
			callbackRegistry.release();
		}
		if ( callbackBuilder != null ) {
			callbackBuilder.release();
		}
		if ( jpaListenerFactory != null ) {
			jpaListenerFactory.release();
		}
	}

}

Hibernate XML Mapping Fragment Re-use

19 Friday Sep 2014

Posted by valblant in Hibernate

≈ Leave a comment

Tags

Hibernate

Hibernate mapping files are a frequent source of code duplication. For example, let’s say that all your database tables contain the same set of audit columns. Why should you have to repeat that declaration in every single mapping file? Or maybe you have similarly structured tables with different names, which is also a good opportunity for reuse.

It is possible to reuse the same Hibernate XML mapping snippet from other mapping files by utilizing XML entities.

XML snippet in ca/gc/agr/common/jms/domain/portal/PortalEventMessage.xml:

<!-- This fragment is included from an another hbm -->

	<version name="lockSeqNum" type="int" column="LOCK_SEQ_NUM" />
	
	<property name="partyId" type="string" column="PARTY_ID" length="20" not-null="true" />
	<property name="fromAppNameEnglish" type="string" column="SOURCE_SYSTEM_NAME_ENG" length="100" not-null="true"  />        
	<property name="fromAppNameFrench" type="string" column="SOURCE_SYSTEM_NAME_FR" length="100" not-null="true" />
	
        ... etc ...
	
	<property name="createdDtm" type="timestamp" column="CREATED_DTM" />
	<property name="createdUserOid" type="long" column="CREATED_USER_OID" />
	<property name="updatedDtm" type="timestamp" column="UPDATED_DTM" />
	<property name="updatedUserOid" type="long" column="UPDATED_USER_OID" />

Hibernate mapping::

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" [
    <!ENTITY commonMapping SYSTEM "classpath://ca/gc/agr/common/jms/domain/portal/PortalEventMessage.xml">
    ]>

<hibernate-mapping>

    <class name="ca.gc.agr.common.jms.domain.portal.PortalEventMessage" table="PIN_PORTAL_EVENT" dynamic-update="true">

        <id name="oid" type="long" column="PORTAL_EVENT_OID" unsaved-value="0">
            <generator class="sequence">
                <param name="sequence">pin_portal_event_seq</param>
            </generator>
        </id>

		&commonMapping;

    </class>
    
</hibernate-mapping>

Make sure that the XML snippet is on the classpath and you are done.

Blog at WordPress.com.

  • Follow Following
    • N1nja Hacks
    • Already have a WordPress.com account? Log in now.
    • N1nja Hacks
    • Customize
    • Follow Following
    • Sign up
    • Log in
    • Report this content
    • View site in Reader
    • Manage subscriptions
    • Collapse this bar