TGM
TGM

Reputation: 1679

JPA Entity not updating correctly

I'm using JPA2 with Eclipselink implementation on Glassfish 3.

I have a simple login/logout application.

These are the entity beans an their mappings:

@Entity
@Table(name = "user_account")
public class UserAccount implements Serializable{
    @Id
    private Integer id;
    private String email;
    private String password;
    private Integer role;

    @OneToOne(fetch=FetchType.EAGER)
    @PrimaryKeyJoinColumn
    private UserDetail userDetail;

    // get+set
}

@Entity
@Table(name = "user_detail")
public class UserDetail implements Serializable{
    private static final long serialVersionUID = 1L;
    @Id
    private Integer id;
    @Temporal(TemporalType.DATE)
    private Date birth;
    @Column(name="origin_city")
    private String originCity;
    @Column(name="residence_city")
    private String residenceCity;
    private String description;

    @OneToOne(mappedBy="userDetail")
    private UserAccount userAccount;
}

In a UserService class I have a simple CRUD implementation. All the methods work just fine but the problem is that sometimes the Entity Object saved in the Persistance Context / JPA Cache doesn't synchronize with the database information.

As a specific example, when I delete some user details (from the user_detail table) the database entry is deleted but when I read the information again, the returned entity still has the Details information. I'm guessing that this info was kept in the JPA cache and brought back without checking the database first.

Is this the correct behaviour? Is there a way to keep the Cache information synchronized with the database?

LE : The user Service contains the read(email, password) method which calls a helper method. This helper method contains the CriteriaAPI query which brings all the users with the desired email and password. The Criteria query is tested and works fine.

public UserAccount read(String email, String password){
    List<UserAccount> userList = getUserList(null, email, password, Constants.RESULT_SINGLE);
    return (userList.isEmpty() ? null : userList.get(0));
}

private List<UserAccount> getUserList(Integer id, String email, String password, String resultType){
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<UserAccount> shell = builder.createQuery(UserAccount.class);
    Root<UserAccount> entity = shell.from(UserAccount.class);
    shell.select(entity);
    shell.distinct(true);
    List<Predicate> predicateList = new ArrayList<Predicate>();
    if (id != null){
        ParameterExpression<Integer> param = builder.parameter(Integer.class, "id");
        predicateList.add(builder.equal(entity.get("id"), param));
    }
    if (email != null){
        ParameterExpression<String> param = builder.parameter(String.class, "email");
        predicateList.add(builder.equal(entity.get("email"), param));
    }
    if (password != null){
        ParameterExpression<String> param = builder.parameter(String.class, "password");
        predicateList.add(builder.equal(entity.get("password"), param));
    }

    if (predicateList.size() == 1){
        shell.where(predicateList.get(0));
    } else {
        Predicate[] p = new Predicate[predicateList.size()];
        p = predicateList.toArray(p);
        shell.where(builder.and(p));
    }   
    TypedQuery<UserAccount> selectQuery =  em.createQuery(shell);
    if (id != null) selectQuery.setParameter("id", id);
    if (email != null) selectQuery.setParameter("email", email);
    if (password != null) selectQuery.setParameter("password", password);

    return selectQuery.getResultList();
}

This is the delete method from the service:

public boolean deleteDetail(Integer userId) {
    boolean ok = true;
    try {
        UserDetail userDetails = em.find(UserDetail.class, userId);
        System.out.println("Trying to delete the detail. The address of the object is: " + userDetails);
        em.remove(userDetails);
        System.out.println("Object deleted. The address is: " + userDetails);
    } catch (Exception e) {
        e.printStackTrace();
        ok = false;
    }

    return ok;
}

Upvotes: 1

Views: 2398

Answers (1)

perissf
perissf

Reputation: 16273

Reading the comments I see that you are using application-managed transactions. As stated in the Java EE Tutorial,

Application-managed entity managers don’t automatically propagate the JTA transaction context. Such applications need to manually gain access to the JTA transaction manager and add transaction demarcation information when performing entity operations. The javax.transaction.UserTransaction interface defines methods to begin, commit, and roll back transactions. Inject an instance of UserTransaction by creating an instance variable annotated with @Resource...

From the code you have posted, you are not demarcating the transaction that removes the UserDetail record. Moreover, you have not shown the code that you are using for getting the EntityManager reference. You should stick to the same tutorial and use

em = emf.createEntityManager();

for getting an updated instance of the Entity Manager.

Otherwise, switch to Container-Managed transactions that make life much simpler in most of the situations.

Upvotes: 1

Related Questions