-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 25 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Interceptor and events replcement for Lifecycle?
PostPosted: Fri Feb 25, 2005 4:07 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
I've noticed that the Lifecycle interface is deprecated in Hibernate3.

We have all of our domain object implementing Lifecycle with different workflows in the onDelete method because of very complex and unusual requirements on "cascade deletes".

As I read the documentation for Hibernate3 it seems that I need to know in advance which specific Interceptor to use as I open a new Session, if I want it to handle my current domain object in a specific manor.
Or if I use the events it seems to be pretty "static" as well.

Before, with the deprecated Lifecycle interface I wouldn't have to know when I wanted to execute a specific onDelete with a different workflow, because it was tied to the domain object itself.

Bad or good design - in this particular matter I don't bother cause it's a very exotic requirement we're implementing that was possible with Hibernate2 and now it seems to be impossible with Hibernate 3?!

Ok, my question is:

Is there anyway in Hibernate3 to define Interceptor's per domain object which all have different workflows implemented for the onDelete(...) method -> including VETO signaling if we don't want to delete the object? And of course without me having to explicitly set it before we're managing a specific domain object. Just like the Lifecycle interface.... ;-)

Thanks in advance!

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 5:15 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
The fact that the listeners or interceptors are "static" does not mean that they can't decide dynamically what should be done. You have access to the entity as much as hibernate has - so why not just call something on it ?

Stil Interceptors cannot prevent specific object deletes. Throwing an exception during interceptor call will result in a rollback.

LifeCycle has been deprecated because it implies having persistence related code in your pojo's which we dont want to encourage. Instead we have made the event mechanism.

You should easily be able to provide a DeleteEventListener that checks with your POJO if it wants to be deleted or not.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 5:35 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
max wrote:
Stil Interceptors cannot prevent specific object deletes. Throwing an exception during interceptor call will result in a rollback.


Well, we don't like that - rollback... we'd just like to cancel deletion on the current POJO for instance.

max wrote:
LifeCycle has been deprecated because it implies having persistence related code in your pojo's which we dont want to encourage. Instead we have made the event mechanism.


I agree with your design choice in Hibernate3, that's not my issue.

max wrote:
You should easily be able to provide a DeleteEventListener that checks with your POJO if it wants to be deleted or not.


And it can be done even if all of my POJO's have different delete strategies (yeah, believe me most of them have)?

Could you provide with an example, please?

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 5:44 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
Something like:

Code:
public class MyPOJOControlledDeleteEventListener extends DefaultDeleteEventListener  {

   public void onDelete(DeleteEvent event) throws HibernateException {

    if (((CastToSomeCommonInterface)event.getObject()).doYouWantToBeDeleted()) {
    super.onDelete(event);
  } else {
     // object chose not to be deleted....
}
}
}


_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 6:00 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
max wrote:
Something like:

Code:
public class MyPOJOControlledDeleteEventListener extends DefaultDeleteEventListener  {

   public void onDelete(DeleteEvent event) throws HibernateException {

    if (((CastToSomeCommonInterface)event.getObject()).doYouWantToBeDeleted()) {

    // Add entity specific delete method
    ((CastToSomeCommonInterface)event.getObject()).doDelete();

    // Delegate to Hibernate delete
    super.onDelete(event);
  } else {
     // object chose not to be deleted....
}
}
}



Thanks! Good example...

So, this event listener is invoked whenever an entity is scheduled for a delete?

I'd still have to implement a delete method on the entity that releases connections to other objects according to our requirements. That should be ok 'cause we'd still don't have a dependency on an Hibernate interface.

Example:

Code:
public class MyPOJOControlledDeleteEventListener extends DefaultDeleteEventListener  {
  public void onDelete(DeleteEvent event) throws HibernateException {
    if (((CastToSomeCommonInterface)event.getObject()).doYouWantToBeDeleted()) {
      super.onDelete(event);
    } else {
      // object chose not to be deleted....
    }
  }
}


This should work (?)

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 6:03 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
This was supposed to be MY SPECIFIC implementation.... I modified Max's quoted example... sorry!

Code:
public class MyPOJOControlledDeleteEventListener extends DefaultDeleteEventListener  {

   public void onDelete(DeleteEvent event) throws HibernateException {

    if (((CastToSomeCommonInterface)event.getObject()).doYouWantToBeDeleted()) {

    // Add entity specific delete method
    ((CastToSomeCommonInterface)event.getObject()).doDelete();

    // Delegate to Hibernate delete
    super.onDelete(event);
  } else {
     // object chose not to be deleted....
}
}
}



Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 6:06 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
Yes it should work.

If you look in DefaultDeleteEventListener you will see it is also here lifecycle delete is called.

Mind though that lifecycle delete is not called in all cases where this event is executed....look in the eventlistener to see if those cases matter for you...and adapt your event listener accordingly.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 6:09 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
max wrote:
If you look in DefaultDeleteEventListener you will see it is also here lifecycle delete is called.


Ok! I haven't browsed the source yet... but I will check it out!

max wrote:
Mind though that lifecycle delete is not called in all cases where this event is executed....look in the eventlistener to see if those cases matter for you...and adapt your event listener accordingly.


I'm sorry if I'm a complete idiot right about now... but what are you meaning?

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 6:35 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
Look at the source and you should be able to understand.

Look where LifeCycle.onDelete is called and see the execution path - there are som "if"'s on its way.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 25, 2005 6:37 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
max wrote:
Look at the source and you should be able to understand.

Look where LifeCycle.onDelete is called and see the execution path - there are som "if"'s on its way.


Ok, I will!

Thanks a lot Max! :-)

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 27, 2005 1:02 pm 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
Hello again!

Would anyone please give me some tips on how I perform the following. I'm trying to migrate some code from Hibernate2 using Lifecycle interface to Hibernate3 using a DeleteEventListener.

Consider a Order -> Product relationship where I'd like to manage the delete cascade using a DeleteEventListener.

Suppose that when I delete the order I'd like to delete one of two OrderLine objects leaving the not-deleted one de-referenced in the database.

Here's some working code illustrating my problem:

DomainObject's and lifecycle interfaces
Code:
package lifecycle;

public interface Deletable {
   
   public boolean isScheduledForDeletion();
   
   public void handleDelete();
}

Code:
package order;

import java.util.HashSet;
import java.util.Set;

import lifecycle.Deletable;

public class Order implements Deletable {

   private Long id;

   private String remarks;

   private Set<OrderLine> orderLines = new HashSet<OrderLine>();

   Order() {
   }

   public Long getId() {
      return this.id;
   }

   public String getRemarks() {
      return this.remarks;
   }

   public void setRemarks(final String someRemarks) {
      this.remarks = someRemarks;
   }

   public Set<OrderLine> getOrderLines() {
      return this.orderLines;
   }

   public void setOrderLines(final Set<OrderLine> someOrderLines) {
      this.orderLines = someOrderLines;
   }

   public void addOrderLine(final OrderLine aOrderLine) {
      final Set<OrderLine> orderLines = this.getOrderLines();
      orderLines.add(aOrderLine);
      aOrderLine.setOrder(this);
   }

   public void handleDelete() {
      if (this.isScheduledForDeletion()) {
         System.out.println("DO delete -> " + this);
      } else {
         System.out.println("DO NOT delete -> " + this);
      }
   }

   public String toString() {
      return this.getClass().getName() + ": id = " + this.getId();
   }

   public boolean equals(final Object o) {
      if (this == o) {
         return true;
      }
      if (o instanceof Order) {
         final Order otherOrder = (Order) o;
         return this.getId().equals(otherOrder.getId());
      }
      return false;
   }

   public int hashCode() {
      final String hashCodeString = this.getId() + this.getRemarks();
      return hashCodeString.hashCode();
   }

   public boolean isScheduledForDeletion() {
      // Simulate Order always to be deleted
      return true;
   }

}

Code:
package order;

import lifecycle.Deletable;

public class OrderLine implements Deletable {

   private Long id;

   private String remarks;

   private Order order;

   OrderLine() {
   }

   public Long getId() {
      return this.id;
   }

   public String getRemarks() {
      return this.remarks;
   }

   public void setRemarks(final String someRemarks) {
      this.remarks = someRemarks;
   }

   private Order getOrder() {
      return this.order;
   }

   public void setOrder(final Order aOrder) {
      this.order = aOrder;
   }

   public void handleDelete() {
      if (this.isScheduledForDeletion()) {
         System.out.println("DO delete -> " + this);
         // Do nothing
      } else {
         System.out.println("DO NOT delete -> " + this);
         // Disconnect from order
         final Order order = this.getOrder();
         order.getOrderLines().remove(this);
         this.setOrder(null);
      }
   }

   public String toString() {
      return this.getClass().getName() + ": id = " + this.getId();
   }

   public boolean equals(final Object o) {
      if (this == o) {
         return true;
      }
      if (o instanceof OrderLine) {
         final OrderLine otherOrderLine = (OrderLine) o;
         return this.getId().equals(otherOrderLine.getId());
      }
      return false;
   }

   public int hashCode() {
      final String hashCodeString = this.getId() + this.getRemarks();
      return hashCodeString.hashCode();
   }

   public boolean isScheduledForDeletion() {
      // Simulate that OrderLine with id = 2 shouldn't be deleted
      final Long spareId = new Long(2);
      if (this.getId().equals(spareId)) {
         return false;
      }
      return true;
   }

}


Mapping documents
Code:
<?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="order.Order"
         table="ORDERS"
         lazy="false">
 
    <id name="id" column="ORDER_ID" access="field">
      <generator class="native"/>
    </id>
 
    <property name="remarks" column="ORDER_REMARKS"/>
   
    <set name="orderLines"
         table="ORDER_LINES"
         inverse="true"
         cascade="all"
         lazy="false"
         fetch="join">
      <key column="ORDER_ID"/>
      <one-to-many class="order.OrderLine"/>
    </set>
  </class>
</hibernate-mapping>

Code:
<?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="order.OrderLine"
         table="ORDER_LINES"
         lazy="false">
 
    <id name="id" column="ORDER_LINE_ID" access="field">
      <generator class="native"/>
    </id>
 
    <property name="remarks" column="ORDER_LINE_REMARKS"/>
   
    <many-to-one name="order"
                 column="ORDER_ID"
                 lazy="false"
                 fetch="join"/>
  </class>
</hibernate-mapping>


Hibernate Configuration
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
      "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
      "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
        <property name="hibernate.connection.url">jdbc:hsqldb:test</property>
        <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">create</property>
       
        <!-- How many objects around will be fetched -->
        <!-- If you set max_fetch_depth to 0 and load an
             account, only the account will be loaded.
             If you set it to 1 and query on an account,
             the account and its associated Product(s) will be
             loaded. If you set it to 3, then trades will be
             loaded as well on each product associated to the
             account. -->
        <property name="hibernate.max_fetch_depth">1</property>
       
        <mapping resource="order/Order.hbm.xml"/>
        <mapping resource="order/OrderLine.hbm.xml"/>
       
        <listener type="delete" class="lifecycle.ControlledDeleteEventListener"/>
    </session-factory>
</hibernate-configuration>


DeleteEventListener
Code:
package lifecycle;

import org.hibernate.HibernateException;
import org.hibernate.event.DeleteEvent;
import org.hibernate.event.def.DefaultDeleteEventListener;

public class ControlledDeleteEventListener extends DefaultDeleteEventListener {

   /**
    *
    */
   private static final long serialVersionUID = 1L;

   public void onDelete(final DeleteEvent aDeleteEvent)
         throws HibernateException {
      final Object obj = aDeleteEvent.getObject();
      if (obj instanceof Deletable) {
         final Deletable deletableObject = (Deletable) obj;
         // Determine if we should delete the object or not
         if (deletableObject.isScheduledForDeletion()) {
            System.out.println("You're about to be wasted! ["
                  + deletableObject + "]");
            deletableObject.handleDelete();
            super.onDelete(aDeleteEvent);
         } else {
            System.out.println("You're spared! [" + deletableObject + "]");
            deletableObject.handleDelete();
         }
      }
   }

}


Client
Code:
package order;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class OrderClient {

   private static SessionFactory sessionFactory;

   static {
      sessionFactory = new Configuration().configure().buildSessionFactory();
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      final Order order = new Order();
      order.setRemarks("Order No.1");

      final OrderLine orderLine1 = new OrderLine();
      orderLine1.setRemarks("OrderLine No.1");
      order.addOrderLine(orderLine1);

      final OrderLine orderLine2 = new OrderLine();
      orderLine2.setRemarks("OrderLine No.2");
      order.addOrderLine(orderLine2);

      final Long orderId = OrderClient.saveOrder(order);

      final Order loadedOrder = OrderClient.loadOrder(orderId);
      System.out.println("Loaded order -> " + loadedOrder);

      OrderClient.deleteOrder(loadedOrder);

      OrderClient.queryAndPrint("from Order");
      OrderClient.queryAndPrint("from OrderLine");

      sessionFactory.close();

   }

   private static Long saveOrder(Order anOrder) {
      final Session session = sessionFactory.openSession();
      final Transaction transaction = session.beginTransaction();
      final Long orderId = (Long) session.save(anOrder);
      transaction.commit();
      session.close();
      return orderId;
   }

   private static Order loadOrder(final Long anOrderId) {
      final Session session = sessionFactory.openSession();
      final Transaction transaction = session.beginTransaction();
      final Order loadedOrder = (Order) session.get(Order.class, anOrderId);
      transaction.commit();
      session.close();
      return loadedOrder;
   }

   private static void deleteOrder(final Order anOrder) {
      final Session session = sessionFactory.openSession();
      final Transaction transaction = session.beginTransaction();
      session.delete(anOrder);
      transaction.commit();
      session.close();
   }

   private static void queryAndPrint(final String aHqlQuery) {
      System.out.println("-=# QUERY AND PRINT #=-");
      System.out.println("HQL = '" + aHqlQuery + "'\n");
      final Session session = sessionFactory.openSession();
      final Transaction transaction = session.beginTransaction();
      final Query query = session.createQuery(aHqlQuery);
      final List resultList = query.list();
      for (Object o : resultList) {
         System.out.println(o);
      }
      transaction.commit();
      session.close();
   }

}


Very similar code did work when using Hibernate2 Lifecycle interfaces.

I'm very thankful for any help!

Kind regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 3:51 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
And the stacktrace...

Code:
09:53:35,531  WARN Configurator:126 - No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath: jar:file:/C:/Andreas/test_area/tools/hibernate-3.0/lib/ehcache-1.1.jar!/ehcache-failsafe.xml
09:53:35,890  INFO SchemaExport:113 - Running hbm2ddl schema export
09:53:35,890  INFO SchemaExport:129 - exporting generated schema to database
09:53:35,906 DEBUG SchemaExport:143 - alter table ORDER_LINES drop constraint FK2C24AE8E6995D83A
09:53:35,906 DEBUG SchemaExport:143 - drop table ORDERS if exists
09:53:35,906 DEBUG SchemaExport:143 - drop table ORDER_LINES if exists
09:53:35,906 DEBUG SchemaExport:161 - create table ORDERS (
    ORDER_ID bigint generated by default as identity (start with 1),
    ORDER_REMARKS varchar(255),
    primary key (ORDER_ID)
)
09:53:35,906 DEBUG SchemaExport:161 - create table ORDER_LINES (
    ORDER_LINE_ID bigint generated by default as identity (start with 1),
    ORDER_LINE_REMARKS varchar(255),
    ORDER_ID bigint,
    primary key (ORDER_LINE_ID)
)
09:53:35,906 DEBUG SchemaExport:161 - alter table ORDER_LINES
    add constraint FK2C24AE8E6995D83A
    foreign key (ORDER_ID)
    references ORDERS
09:53:35,921  INFO SchemaExport:173 - schema export complete
Hibernate: insert into ORDERS (ORDER_REMARKS, ORDER_ID) values (?, null)
Hibernate: call identity()
Hibernate: insert into ORDER_LINES (ORDER_LINE_REMARKS, ORDER_ID, ORDER_LINE_ID) values (?, ?, null)
Hibernate: call identity()
Hibernate: insert into ORDER_LINES (ORDER_LINE_REMARKS, ORDER_ID, ORDER_LINE_ID) values (?, ?, null)
Hibernate: call identity()
Hibernate: select order0_.ORDER_ID as ORDER1_1_, order0_.ORDER_REMARKS as ORDER2_0_1_, orderlines1_.ORDER_ID as ORDER3_3_, orderlines1_.ORDER_LINE_ID as ORDER1_3_, orderlines1_.ORDER_LINE_ID as ORDER1_0_, orderlines1_.ORDER_LINE_REMARKS as ORDER2_1_0_, orderlines1_.ORDER_ID as ORDER3_1_0_ from ORDERS order0_ left outer join ORDER_LINES orderlines1_ on order0_.ORDER_ID=orderlines1_.ORDER_ID where order0_.ORDER_ID=?
Loaded order -> order.Order: id = 1
You're about to be wasted! [order.Order: id = 1]
DO delete -> order.Order: id = 1
You're spared! [order.OrderLine: id = 2]
DO NOT delete -> order.OrderLine: id = 2Exception in thread "main" java.util.ConcurrentModificationException
   at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
   at java.util.HashMap$KeyIterator.next(Unknown Source)
   at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:366)
   at org.hibernate.engine.Cascades.cascadeCollection(Cascades.java:895)

   at org.hibernate.engine.Cascades.cascadeAssociation(Cascades.java:792)
   at org.hibernate.engine.Cascades.cascade(Cascades.java:720)
   at org.hibernate.engine.Cascades.cascade(Cascades.java:847)
   at org.hibernate.engine.Cascades.cascade(Cascades.java:819)
   at org.hibernate.event.def.DefaultDeleteEventListener.cascadeBeforeDelete(DefaultDeleteEventListener.java:248)
   at org.hibernate.event.def.DefaultDeleteEventListener.deleteEntity(DefaultDeleteEventListener.java:201)
   at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:109)
   at lifecycle.ControlledDeleteEventListener.onDelete(ControlledDeleteEventListener.java:24)
   at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:579)
   at order.OrderClient.deleteOrder(OrderClient.java:69)
   at order.OrderClient.main(OrderClient.java:39)


Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 4:01 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
dont call toString's that might initialize proxies while working from inside a session operation.


/max

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 4:17 am 
Beginner
Beginner

Joined: Fri Nov 19, 2004 6:41 am
Posts: 39
Location: Stockholm, Sweden
Well, I removed every call to:

Code:
System.out.println();


And the code still doesn't run correctly. Do you really think it have something to do with the proxies? Did you try to run the code by yourself?

Regards, Andreas


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 30, 2005 4:20 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
no i did not run it (this is free support you know ;)

But the exception is one that occurs when you modify the state of the session (eg. adding or removing entities from the underlying collections)

That often happens if users are initializing proxies when they shouldn't.

_________________
Max
Don't forget to rate


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 25 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.