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.x...
|
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>
|
View the original post :
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4143307#...
Reply to the post :
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&a...