0 Replies Latest reply on Apr 17, 2008 3:33 AM by Hans Meiser

    Modify data within PreUpdate will only merges partially

    Hans Meiser Newbie

      Hi,

      i have a problem with entity listeners using @PreUpdate to modify the data.
      There are two entity beans, the first contains a collection of the second entity bean => OneToMany-Mapping. Something like that:

      @Entity
      @Table(name = "TEST_FIRSTBEAN")
      public class FirstBean implements Serializable {
      
       private static final long serialVersionUID = 8150999009636714523L;
      
       private Integer id;
       private Integer state;
       private Set<SecondBean> secondBeans = new HashSet<SecondBean>(0);
      
       public FirstBean() {
       }
      
       @Id
       @GeneratedValue(strategy = GenerationType.AUTO)
       @Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
       public Integer getId() {
       return this.id;
       }
      
       public void setId(Integer id) {
       this.id = id;
       }
      
       @Column(name = "STATE")
       public Integer getState() {
       return this.state;
       }
      
       public void setState(Integer state) {
       this.state = state;
       }
      
       @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "firstBean")
       public Set<SecondBean> getSecondBeans() {
       return this.secondBeans;
       }
      
       public void setSecondBeans(Set<SecondBean> bBeans) {
       this.secondBeans = bBeans;
       }
      
      }
      


      and

      @Entity
      @Table(name = "TEST_SECONDBEAN")
      public class SecondBean implements Serializable {
       private static final long serialVersionUID = -5353954801248924436L;
      
       private Integer id;
       private Integer state;
       private FirstBean firstBean;
      
       public SecondBean() {
       }
      
       @Id
       @GeneratedValue(strategy = GenerationType.AUTO)
       @Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
       public Integer getId() {
       return this.id;
       }
      
       public void setId(Integer id) {
       this.id = id;
       }
      
       @Column(name = "STATE")
       public Integer getState() {
       return this.state;
       }
      
       public void setState(Integer state) {
       this.state = state;
       }
      
       @ManyToOne(fetch = FetchType.EAGER)
       @JoinColumn(name = "FIRSTBEAN_ID")
       public FirstBean getFirstBean() {
       return this.firstBean;
       }
      
       public void setFirstBean(FirstBean firstBean) {
       this.firstBean = firstBean;
       }
      
      }
      


      There is also a simple entity listener. In the PreUpdate-Method It will set the state of the second beans, if the state of the first bean was set. Something like this:
      public class FirstBeanEntityListener {
       private static final Logger LOGGER = Logger.getLogger(FirstBeanEntityListener.class);
      
       public FirstBeanEntityListener() {
       super();
       }
      
       @PreUpdate
       public void preUpdate(final FirstBean bean) throws NamingException {
       LOGGER.info("* FirstBean '"+bean.getName()+"' entity listener: @preUpdate called");
      
       if (bean != null && bean.getState() != null) {
       LOGGER.info(" FirstBean '"+bean.getName()+"': state set");
      
       for (SecondBean childBeans : bean.getSecondBeans()) {
       LOGGER.info(" set state for second bean: " + childBeans.getName());
       childBeans.setState(new Integer(1));
       }
       }
       }
      
      }
      


      The entity listener is bound to the entity bean using orm.xml:
      <?xml version="1.0" encoding="UTF-8"?>
      <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
       <entity class="FirstBean">
       <entity-listeners>
       <entity-listener class="FirstBeanEntityListener" />
       </entity-listeners>
       </entity>
      </entity-mappings>


      The problem is now: If i have one FirstBean with any number of second beans, and i set the state of the first bean on client side, merge it in a stateless session bean and look at the database after that, state of one SecondBean wasn't set, but all others are set. Assume a jUnit test like that:

      public class EntityBeanListenerTest {
      
       private TestServiceRemote testRemoteBean = null;
      
       @Before
       public void setUp() throws NamingException, RemoteCheckedException {
       //Perform lookup:
       ...
       this.testRemoteBean = (TestServiceRemote) ctx.lookup("TestService");
       }
      
       @After
       public void tearDown() throws Exception {
       this.testRemoteBean = null;
       }
      
       private FirstBean createSampleFirstBean() {
       final FirstBean firstBean;
       final SecondBean secondBean1;
       final SecondBean secondBean2;
       final SecondBean secondBean3;
      
       //Init first bean
       firstBean = new FirstBean();
      
       //Init second beans
       secondBean1 = new SecondBean();
       secondBean2 = new SecondBean();
       secondBean3 = new SecondBean();
      
       //Add second beans to first bean
       firstBean.getSecondBeans().add(secondBean1);
       firstBean.getSecondBeans().add(secondBean2);
       firstBean.getSecondBeans().add(secondBean3);
      
       secondBean1.setFirstBean(firstBean);
       secondBean2.setFirstBean(firstBean);
       secondBean3.setFirstBean(firstBean);
      
       return firstBean;
       }
      
       @Test
       public void testFirstBeanDeletion() throws RemoteCheckedException {
       FirstBean firstBean;
      
       //Reset database, will only clean table data
       this.testRemoteBean.resetDatabase();
      
       //Create data
       firstBean = this.createSampleFirstBean();
      
       //Save data and retrieve persisted data
       this.testRemoteBean.persistFirstBean(firstBean);
       firstBean = this.testRemoteBean.getFirstBean();
      
       firstBean.setState(new Integer(1));
       this.testRemoteBean.mergeFirstBean(firstBean);
       firstBean = this.testRemoteBean.getFirstBean();
      
       for (final SecondBean secondBean : firstBean.getSecondBeans()) {
       //This will fail on one bean, which one is indeterministic
       Assert.assertNotNull("Second beans have not been deleted", secondBean.getState());
       }
       }
      
      }


      I though the listeners should set all states of second beans, but for one second bean, the state wasn't set.
      If i perform a flush after merging the data in the same transaction in the session bean, the jUnit test will pass.

      Can anyone explain, why this happens? Am i doing something wrong? Must i always perform a flush?
      I am using JBoss AS 4.2.2 GA.

      Thanks in advance,

      Markus