Search code examples
jpajava-ee-6openjpaopenejb

Can lazy fetched field be modified before loaded?


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?


Solution

  • 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();