Modify data within PreUpdate will only merges partially
masterozz666 Apr 17, 2008 3:33 AMHi,
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