-->
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.  [ 3 posts ] 
Author Message
 Post subject: PreUpdate method of entity listener not always called
PostPosted: Thu Apr 20, 2006 11:32 am 
Beginner
Beginner

Joined: Mon May 16, 2005 6:06 am
Posts: 20
Hibernate version:
Hibernate 3.2CR1, Hibernate Annotations 3.1 beta 9, Hibernate Entity Manager 3.1 beta7

Name and version of the database you are using:
MySQL 5.0.20

Problem description
When you try to merge a non-modified entity the @PreUpdate method of the assigned entity listener is not called.

I would expect that the PreUpdate method is always called no matter if the entity was modified by the application code or not, since the PreUpdate method might alter the entity which then requires persisting the changes (e.g. auditing, setting lastModified fields, performing summary calculations based on other referenced items).

TestCase
Code:
package test;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;

import junit.framework.TestCase;

import org.hibernate.annotations.GenericGenerator;

public class EntityListenerTest extends TestCase
{
   @Entity
   @EntityListeners(OrderListener.class)
   static class Order
   {
      @Basic
      String customerName;

      @OneToMany(mappedBy = "order", cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
      @org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
      @org.hibernate.annotations.OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE)
      Set<OrderItem> items = new HashSet<OrderItem>();

      @Id
      @GeneratedValue(generator = "system-uuid")
      @GenericGenerator(name = "system-uuid", strategy = "uuid")
      String id;

      @Temporal
      Date lastModified;
   }

   @Entity
   static class OrderItem
   {
      @Basic
      int amount;

      @ManyToOne(optional = false, fetch = FetchType.LAZY)
      Order order;
      @Id
      @GeneratedValue(generator = "system-uuid")
      @GenericGenerator(name = "system-uuid", strategy = "uuid")
      String id;
   }

   public static class OrderListener
   {
      static boolean onPreUpdateCalled = false;

      @PreUpdate
      public void onPreUpdate(Order theOrder)
      {
         theOrder.lastModified = new Date();
         onPreUpdateCalled = true;
      }
   }

   private final static Logger LOG = Logger.getLogger(EntityListenerTest.class.getName());

   private final EntityManagerFactory emf = Persistence.createEntityManagerFactory("manager1");

   public Order createAnOrder()
   {
      // create and persist an order with one item
      Order theOrder = new Order();
      theOrder.customerName = "acme";
      OrderItem anItem = new OrderItem();
      anItem.order = theOrder;
      anItem.amount = 1;
      theOrder.items.add(anItem);
      persist(theOrder);

      return theOrder;
   }

   private void merge(Object e) throws RuntimeException
   {
      EntityManager em = emf.createEntityManager();
      EntityTransaction tx = null;
      try
      {
         tx = em.getTransaction();
         tx.begin();
         em.merge(e);
         tx.commit();
      }
      catch (RuntimeException ex)
      {
         if (tx != null && tx.isActive()) tx.rollback();
         throw ex;
      }
      finally
      {
         em.close();
      }
   }

   private void persist(Object e) throws RuntimeException
   {
      EntityManager em = emf.createEntityManager();
      EntityTransaction tx = null;
      try
      {
         tx = em.getTransaction();
         tx.begin();
         em.persist(e);
         tx.commit();
      }
      catch (RuntimeException ex)
      {
         if (tx != null && tx.isActive()) tx.rollback();
         throw ex;
      }
      finally
      {
         em.close();
      }
   }

   public void testMergeOrderUmodified()
   {
      Order theOrder = createAnOrder();

      OrderListener.onPreUpdateCalled = false;

      // merge the unmodified order
      merge(theOrder);

      if (OrderListener.onPreUpdateCalled == false)
      {
         LOG.warning("OrderListener.onPreUpdate was not called.");
         fail();
      }
      else
      {
         LOG.info("OrderListener.onPreUpdate was called.");
      }
   }

   public void testMergeOrderWithModifiedField()
   {
      Order theOrder = createAnOrder();

      OrderListener.onPreUpdateCalled = false;

      // modify the order and merge
      theOrder.customerName = "john doe";
      merge(theOrder);
      assertTrue(OrderListener.onPreUpdateCalled);
   }

   public void testMergeOrderWithModifiedItem()
   {
      Order theOrder = createAnOrder();

      OrderListener.onPreUpdateCalled = false;

      // modify an item and merge the order
      OrderItem theItem = theOrder.items.iterator().next();
      theItem.amount = 10;
      merge(theOrder);

      if (OrderListener.onPreUpdateCalled == false)
      {
         LOG.warning("OrderListener.onPreUpdate was not called.");
         fail();
      }
      else
      {
         LOG.info("OrderListener.onPreUpdate was called.");
      }
   }
}


Any feedback is appreciated.

Regards,
Sebastian


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 27, 2006 10:04 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
appart for the precalculation thing (which could be made on a regular getter), I don't understand why you want to change the update time or audit a non updated entity

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 27, 2006 11:26 am 
Beginner
Beginner

Joined: Mon May 16, 2005 6:06 am
Posts: 20
For my application I created a generic way to generate table views.

There exists a ViewEntry entity that can hold display information of any other entity type.

UI components can register "views" and specify which entity types the view should be built of, what display value the view entry should contain and which entities of a certain type should be part of that view (element filter).

Right now the ViewManager listens to the @PostUpdate method of the BaseEntity which all other entities extend. It then updates the corresponding ViewEntries of the registered views.

For the Order entity for example there exists a view called CurrentOrders that contains all orders of the last 30 days. the view entries contain some of the order basic fields but also summary information based on the order items, e.g. total order value.

If I now only change the price of some order items and then persist the order, the @PostUpdate is only called on the OrderItem entities. The result is that the summary fields of the order ViewEntries are not updated.

If the @PreUpdate method would be called on the order I could modify the last modified timestamp of the order which would lead to an update of the order and eventually to the call of @PostUpdate on the order entity.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

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.