Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate Interceptors with Annotations

Tags:

java

hibernate

I'm migrating an app from hibernate xml configuration to annotations, I'm not sure how to adapt my BusinessObjectInterceptor class to the new annotations based format.

We are changing our HibernateUtil class from creating SessionFactory

            InitialContext ctx      = new InitialContext();
        sessionFactory  = (SessionFactory)ctx.lookup("java:/hibernate/SessionFactory");

to creating EntityManagerFactory

            entityManagerFactory = Persistence.createEntityManagerFactory("primary");

We are changing our HibernateUtil class from using sessionFactory.openSession() to creating a Session from an EntityManager

            //s = sessionFactory.openSession(new BusinessObjectInterceptor());
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        s = entityManager.unwrap(Session.class);

The problem is I'm not sure how to inject a BusinessObjectInterceptor into a new Hibernate Session, or the proper way to annotate my classes so that they can use the Interceptor

I'm trying to set the Interceptor as a property in persistence.xml. I'm not sure if this is correct

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
    http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="primary"><jta-data-source>java:jboss/datasources/MySqlDS</jta-data-source>

  <properties>
     <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
     <property name="hibernate.ejb.interceptor.session_scoped" value="com.mycompany.common.persistence.BusinessObjectInterceptor"/>
           </properties>

Our classes were previously configured via hbm.xml files

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.mycompany.liveexpert.common.businessobjects.ServerSettings" table="server_settings">
            <id name="serverSettingsID" type="integer" column="server_settings_id"> 
                    <generator class="identity" />
            </id>
            <version  name="updateCounter" column="update_counter"/>
            <property name="changedDate" type="timestamp" column="changed_date"/>
            <property name="changedBy" type="string" column="changed_by"/>
            <property name="createdDate" type="timestamp" column="created_date"/>
            <property name="createdBy" type="string" column="created_by"/>
            <property name="status" type="string" column="status"/>

            <property name="emailServer" type="string" column="email_server"   />
            <property name="emailFromAddress" type="string" column="email_from_address"   />
            <property name="emailUser" type="string" column="email_user"   />
            <property name="emailPassword" type="string" column="email_password"   />

</class>

the annotated classes look like

@Entity
@Table(name="server_settings")
public class ServerSettings  extends BusinessObject
{
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer serverSettingsID;
    @Column(name = "email_server")
    private String emailServer;
    @Column(name = "email_from_address")
private String emailFromAddress;
    @Column(name = "email_user")
private String emailUser;
    @Column(name = "email_password")
private String emailPassword;

We have a BusinessObjectInterceptor class and a BusinessObject class. I will post them below for reference. I think what needs to happen is that BusinessObject class needs to be annotated, I'm not sure how that's done since BusinessObject doesn't map to columns in a specific table, but rather columns that are common to all tables in our database. I'll paste these two classes below. Any advice on how to set up my Interceptor with annotations would be appreciated. Thanks.

BusinessObjectInterceptor

public class BusinessObjectInterceptor extends EmptyInterceptor
{
private int updates;
private int creates;

private static final String defaultDesignation = "system";

private String getUserDesignation()
{
    UserContextI theContext = PersistenceContext.getUserContext();
    if (theContext == null) return defaultDesignation;
    String uid = theContext.getUserDesignation();
    if (uid == null) return defaultDesignation;
    return uid;
}
public boolean onFlushDirty(Object entity,
                            Serializable id,
                            Object[] currentState,
                            Object[] previousState,
                            String[] propertyNames,
                            Type[] types)
{
    boolean theReturn = false;
    if (entity instanceof BusinessObject)
    {
        updates++;
        for (int i=0; i<propertyNames.length; i++)
        {
            if ("changedDate".equals(propertyNames[i]))
            {
                currentState[i] = new Date();
                theReturn = true;
            }
            if ("changedBy".equals(propertyNames[i]))
            {
                currentState[i] = getUserDesignation();
                theReturn = true;
            }
        }
    }
    return theReturn;
}
public boolean onSave(Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types)
{
    boolean theReturn = false;
    if (entity instanceof BusinessObject)
    {
        creates++;
        for (int i=0; i<propertyNames.length; i++)
        {
            if ("createdDate".equals(propertyNames[i]))
            {
                state[i] = new Date();
                theReturn = true;
            }
            if ("createdBy".equals(propertyNames[i]))
            {
                state[i] = getUserDesignation();
                theReturn = true;
            }
            if ("changedDate".equals(propertyNames[i]))
            {
                state[i] = new Date();
                theReturn = true;
            }
            if ("changedBy".equals(propertyNames[i]))
            {
                state[i] = getUserDesignation();
                theReturn = true;
            }
        }
    }
    return theReturn;
}
public void preFlush(Iterator entities)
{
    updates = 0;
    creates = 0;
}

BusinessObject

    public abstract class BusinessObject
{
private String status;
private String createdBy;
private Date   createdDate;
private String changedBy;
private Date   changedDate;
private int    updateCounter;

/**
 * Generic save method to be used for persisting a business object.
 * 
 * @return a copy of this business object in its saved state.
 * 
 * @throws Exception
 */
public BusinessObject save() throws Exception 
{
    Session        hsession = null;
    Transaction    tx = null;
    BusinessObject theObject = null;

    validate(); // throws ValidationException

    try {
        hsession = HibernateUtil.currentSession();
        tx = hsession.beginTransaction();

        if (getStatus() == null || getStatus().length() < 1)
        {
            setStatus("OK");
        }

        //theObject = (BusinessObject) hsession.saveOrUpdateCopy(this);
        theObject = (BusinessObject) hsession.merge(this);
        if (tx != null && tx.isActive() && !tx.wasCommitted())
            tx.commit();
    } catch (Exception e){
        try
        {
        if (tx!=null) tx.rollback();
        } catch (Exception e3)
        {}
        try
        {
        hsession.close();
        } catch (Exception e2)
        {}
        throw e;
    } finally
    {
        HibernateUtil.closeSession();
    }
    return theObject;
}
like image 752
user619804 Avatar asked Aug 23 '11 23:08

user619804


1 Answers

In JPA you can use @PrePersist and @PreUpdate annotation to annotate a method in your model (@Entity class) that will be called before persisting (INSERT) or updating (UPDATE).

You can create an entity listener, and add @EntityListeners annotation to your models, like:

public class BusinessListener {
    @PrePersist
    public void businessUpdate(BusinessObject obj) {
        obj.setCreatedDate(new Date());
        ....
    }
    @PreUpdate
    public void businessUpdate(BusinessObject obj) {
        obj.setChangedDate(new Date());
        ....
    }
}

@EntityListeners(class=...BusinessListener)
public class BusinessObject {
}

Or you could put methods in a base entity class and extend all your entities from it, like:

public class BusinessObject {
    @PrePersist
    public void businessUpdate() {
        createdDate = new Date();
        ....
    }
    @PreUpdate
    public void businessUpdate() {
        changedDate = new Date();
        ....
    }
}
like image 145
Zoran Regvart Avatar answered Sep 20 '22 23:09

Zoran Regvart