I'm using embedded OpenEJB in one unit test. The test does not work. When I was debugging I had found out, that the lazy fetched field behaved peculiarly.
Is it really possible? If the field has been already loaded, all goes in usual way:
//field == "something from db"
field = "ahoj";
//field == "ahoj"
But if the field has not been loaded:
//field == null
field = "ahoj";
//field == null
In the call stack I saw, that there was some method of some overlying layer on the top, most likely the entity managing one. I tried Google, but no answer found.
So my question is: Is there some rule, that not fetched field of managed entity cannot be assigned? And if there is similar rule, how to change the value of field without fetching it from database?
The answer is of course yes. But what was happening at yesterday?
The problem was in the persistence context and might be also in the method name. The field was not updated in its setter. The persistence context is bound with transaction. Thus if you need update and save several fields, you must do all operations within one transaction. You must start the transaction, call setters on the entity and call an update method on the session bean managing access to entities.
@Basic(fetch=FetchType.LAZY)
String field;
@Override
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public String getFiled() {
return field;
}
@Override
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public void setText(String text) {
//other code
this.field = text;
//other code
}
The getter may or not be invoked within a transaction. But the setter must be invoked within an existing transaction. The same transaction attribute applies to the update method of session bean. In addition we need a method reattach()
to ensure, that we're working with the object in proper persistence context.
@Stateless
class AccessBean implements Accessor {
@PersistenceContext(unitName = "MyUnit")
private EntityManager manager;
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public TheEntity update(TheEntity e) {
e = manager.merge(e);
manager.flush();
return e;
}
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public <T> T reattach(T object) {
return manager.merge(object);
}
}
All must be done within one transaction.
Context context = new InitialContext(your properties);
UserTransaction trans = (UserTransaction)context.lookup("java:comp/UserTransaction");
//AccessBean ab already injected;
//TheEntity e already exists
trans.begin(); //this implicates a new persistent context
e = da.reattach(e); //critical line
e.setText("New text");
e = da.updateEvent(e);
trans.commit();
TestCase.assertEquals("New text", e.getField()); //yes!
The critical line is the seemingly needless one, but it isn't. If you comment this line out, you will work with an entity detached or attached to another persistent context. Then you will see the funny behavior, which was matter of my question.
Note: We need also get the data within an explicit transaction up to now.
trans.begin();
e = da.reattach(e);
String s = e.getField();
trans.commit();