2 Replies Latest reply on Aug 14, 2008 5:33 PM by benc

    Is there an EntityListener equivalent for when assocaitions

    benc

      I have a scenario where I need to know when an association between two entities is changed in the same fashion as @PostUpdate and @PostPersist in EntityListener. My original assumption was that EntityListener would do what I wanted; but, it seems to only be called for fields that are stored directly in the backing table of an entity... So when I change collections defined by @OneToMany or @ManyToMany with @JoinTable the EntityListener is not called.

      I can force the EntityListener to be called by setting another field in the Entity (labeled dirty in the attached code example) but I can't help but think there is a better solution than this.

      I've created as simple an ear as I could to demonstrate this behavior. EntityA contains a list of EntityB objects. A servlet has two methods it can call. The first creates an instance of EntityA with 3 instances of EntityB, persists them, then adds another instance of EntityB and persists them. The second method called by the servlet does the same thing except it also sets the dirty field on EntityA when it adds the fourth EntityB instance.

      Output from non-dirty servlet run

      15:39:30,077 INFO [STDOUT] Entity Listener Called for EntityA : 1
      15:39:30,077 INFO [STDOUT] Dirty : false
      15:39:30,077 INFO [STDOUT] EntityB list : [1, 2, 3]
      


      Output from dirty servlet run
      15:39:47,241 INFO [STDOUT] Entity Listener Called for EntityA : 2
      15:39:47,241 INFO [STDOUT] Dirty : false
      15:39:47,241 INFO [STDOUT] EntityB list : [5, 6, 7]
      15:39:47,255 INFO [STDOUT] Entity Listener Called for EntityA : 2
      15:39:47,255 INFO [STDOUT] Dirty : true
      15:39:47,256 INFO [STDOUT] EntityB list : [5, 6, 7, 8]
      


      Source Code:

      EntityA.java
      package test;
      
      import java.util.Collection;
      
      import javax.persistence.Entity;
      import javax.persistence.EntityListeners;
      import javax.persistence.GeneratedValue;
      import javax.persistence.Id;
      import javax.persistence.JoinColumn;
      import javax.persistence.JoinTable;
      import javax.persistence.OneToMany;
      
      @Entity
      @EntityListeners(AssociationTestEntityListener.class)
      public class EntityA
      {
       protected int id;
       protected boolean dirty;
       protected Collection<EntityB> b;
      
       @Id
       @GeneratedValue
       public int getId()
       {
       return id;
       }
       public void setId(int id)
       {
       this.id = id;
       }
      
       public boolean isDirty()
       {
       return dirty;
       }
       public void setDirty(boolean dirty)
       {
       this.dirty = dirty;
       }
      
      
       @OneToMany
       @JoinTable(
       name = "a_b",
       joinColumns = {@JoinColumn(name = "b_id")},
       inverseJoinColumns = @JoinColumn(name = "a_id")
       )
       public Collection<EntityB> getB()
       {
       return b;
       }
       public void setB(Collection<EntityB> b)
       {
       this.b = b;
       }
      }
      


      EntityB.java
      package test;
      
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.Id;
      
      @Entity
      public class EntityB
      {
       protected int id;
      
       @Id
       @GeneratedValue
       public int getId()
       {
       return id;
       }
       public void setId(int id)
       {
       this.id = id;
       }
      }
      


      AssociationTestEntityListener.java
      package test;
      
      import javax.persistence.PostPersist;
      import javax.persistence.PostUpdate;
      
      public class AssociationTestEntityListener
      {
       @PostPersist
       @PostUpdate
       public void postEvent(EntityA a)
       {
       StringBuffer bstr = new StringBuffer("EntityB list : [");
       for( EntityB b: a.getB())
       {
       bstr.append(b.getId());
       bstr.append(", ");
       }
       bstr.replace(bstr.lastIndexOf(", "), bstr.length(), "]");
      
       System.out.println();
       System.out.println("Entity Listener Called for EntityA : " + a.getId());
       System.out.println("Dirty : " + a.isDirty());
       System.out.println(bstr.toString());
       System.out.println();
       a.setDirty(false);
       }
      }
      


      TestListenerStatelessSession.java
      package test;
      
      import java.util.ArrayList;
      import java.util.Collection;
      
      import javax.ejb.Stateless;
      import javax.persistence.EntityManager;
      import javax.persistence.PersistenceContext;
      
      import org.jboss.annotation.ejb.LocalBinding;
      
      @Stateless (name="TestListener")
      @LocalBinding(jndiBinding="AT/TestListener")
      public class TestListenerStatelessSesssion implements TestListenerSession
      {
       @PersistenceContext(unitName="defaultPU")
       EntityManager em;
      
       /* (non-Javadoc)
       * @see test.TestListenerSession#doStuff()
       */
       public void testWithDirty()
       {
       Collection<EntityB> bList = new ArrayList<EntityB>(3);
       bList.add(getNewB());
       bList.add(getNewB());
       bList.add(getNewB());
      
       EntityA a = new EntityA();
       a.setB(bList);
       em.persist(a);
      
       a.setDirty(true);
       bList = a.getB();
       bList.add(getNewB());
       a.setB(bList);
       em.merge(a);
       }
      
       /* (non-Javadoc)
       * @see test.TestListenerSession#doStuff()
       */
       public void testWithoutDirty()
       {
       Collection<EntityB> bList = new ArrayList<EntityB>(3);
       bList.add(getNewB());
       bList.add(getNewB());
       bList.add(getNewB());
      
       EntityA a = new EntityA();
       a.setB(bList);
       em.persist(a);
      
       bList = a.getB();
       bList.add(getNewB());
       a.setB(bList);
       em.merge(a);
       }
      
       private EntityB getNewB()
       {
       EntityB rc = new EntityB();
       em.persist(rc);
       return rc;
       }
      }
      


      TestListenerSession.java
      package test;
      
      import javax.ejb.Local;
      
      
      @Local
      public interface TestListenerSession
      {
      
       public abstract void testWithDirty();
       public abstract void testWithoutDirty();
      
      }
      


      AT.jsp
      <%@ page import="test.*, javax.naming.*, java.text.*"%>
      
      <%!
       private TestListenerSession tls = null;
       public void jspInit ()
       {
       try
       {
       InitialContext ctx = new InitialContext();
       tls = (TestListenerSession) ctx.lookup("AT/TestListener");
       }
       catch (Exception e)
       {
       e.printStackTrace ();
       }
       }
      %>
      
      <%
       try
       {
       if (request.getParameter("TestWithoutDirty") != null)
       {
       tls.testWithoutDirty();
       }
       else if (request.getParameter("TestWithDirty") != null)
       {
       tls.testWithDirty();
       }
       }
       catch (Exception e)
       {
       e.printStackTrace ();
       }
       %>
      
      <html>
      <body>
      
      <p>Test Listener<br />
      <form action="AT.jsp" method="POST">
       <input type="submit" value="TestWithoutDirty" name="TestWithoutDirty">
       <input type="submit" value="TestWithDirty" name="TestWithDirty">
      </form>
      </p>
      
      </body>
      </html>
      


      Application.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <application
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
       http://java.sun.com/xml/ns/javaee/application_5.xsd"
       id="Application_ID"
       version="5">
       <module>
       <web>
       <web-uri>ROOT.war</web-uri>
       <context-root>/</context-root>
       </web>
       </module>
       <module>
       <ejb>AssociationTest.jar</ejb>
       </module>
      </application>