Question

I thought that an entity found by em.find was automatically managed by em, even out a transaction, but this class below seems to show the contrary. Was I wrong or what is the mistake in that class?

@Stateful
@TransactionAttribute(NOT_SUPPORTED)
public class CustomerGateway {

  @PersistenceContext(unitName = "customersPU", type = EXTENDED)
  private EntityManager em;
  private Customer customer;

  public Customer find(Long id) {
    // customer is not managed!
    this.customer = em.find(Customer.class, id);
    // Print false!
    System.out.println("Method find: " + em.contains(customer));
    // Print false too (2 is the id of an entity)!
    System.out.println("Method find: " + em.contains(em.find(Customer.class, 2L));
    // A workaround
    customer = em.merge(customer);
    // Print true.
    System.out.println("Method find after merge: " + em.contains(customer));
    return this.customer;
  }

EDIT 1: code of the entity

@Entity
@NamedQuery(name = "Customer.all", query = "select c from Customer c")
public class Customer implements Serializable {
  private static final long serialVersionUID = 1L;
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  private String name;

  public Customer() {
  }

  public Customer(String name) {
    this.name = name;
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  @Override
  public int hashCode() {
    int hash = 0;
    hash += (id != null ? id.hashCode() : 0);
    return hash;
  }

  @Override
  public boolean equals(Object object) {
    // TODO: Warning - this method won't work in the case the id fields are not set
    if (!(object instanceof Customer)) {
      return false;
    }
    Customer other = (Customer) object;
    if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
      return false;
    }
    return true;
  }

  @Override
  public String toString() {
    return "entity.Customer[ id=" + id + " ]";
  }

}

Code of the stateful EJB:

@Stateful
@TransactionAttribute(NOT_SUPPORTED)
public class CustomerGateway {

  @PersistenceContext(type = PersistenceContextType.EXTENDED)
  private EntityManager em;

  private Customer customer;

  public Customer getCustomer() {
    return customer;
  }

  public void create(Customer customer) {
    em.persist(customer);
    this.customer = customer;
  }

  public Customer find(Long id) {
    this.customer = em.find(Customer.class, id);
    System.out.println("customer managed ? " + em.contains(this.customer));
    // Workaround :
//    this.customer = em.merge(customer);
    return customer;
  }

  public void remove(Long id) {
    Customer cust = em.getReference(Customer.class, id);
    em.remove(cust);
  }

  @TransactionAttribute(REQUIRES_NEW)
  public void save() {
  }

  public List<Customer> findAllCustomers() {
    Query query = em.createNamedQuery("Customer.all");
    return query.getResultList();
  }

  @Remove
  public void close() {
  }

}

I work with NetBeans 7.4, GlassFish 4.0, EJB 3.2, Java DB.

Was it helpful?

Solution 2

According to Checkus, it seems to be a bug in GF4: https://java.net/jira/browse/GLASSFISH-20968

OTHER TIPS

All that you have experienced is according to the spec. The persistence context remains (and the entities keeps attached) while the transaction exists. So, in a extended persistence context and a NOT_SUPPORTED transaction the objects retrieved by calling find method are dettached. -Also, if your Customer object has lazy relationships and you try to access them, then, it is highly probable that you will get a runtime exception.-

Now, why the merge method is just ok?. Well, first remember that merge returns a managed entity and is attaching the customer to the persistence context.

Second, you have an EXTENDED persistence context, so, it wont go to update the database until you call the @Remove annotated method. When this call arrives, you will probably get a TransactionRequiredException.

EDIT 1 --------------------------------------------------------------------------------

According to your comments:

  • find is not required to be in a transaction, although, if you want managed object there must be one.

  • The paragraph is about EM the life cycle (3.3 section), in this case, tries to explain that at the end of a method for a transaction-scoped bean, the entities will be detached, but, in the case of extended EM the entities will remains attached.

  • There are 2 insightful paragraphs :

  1. When an EM with an extended persistence context is used, the persist, remove, merge and refresh operations can be called regardless of whether a transaction is active. The effects of these operations will be committed to the database when the extended persistence context is enlisted in a transaction and the transaction commits.

  2. The persistence context is closed by the container when the @Remove method of the stateful session bean completes (or the stateful session bean instance is otherwise destroyed).

  • Looks like the method that you initially omit in the question with @TransactionAttribute(REQUIRES_NEW) is the place where the merge occurs successfully. That's why you have no exception.

EDIT 2 --------------------------------------------------------------------------------

After some testing, GF4 has a bug and has been reported > https://java.net/jira/browse/GLASSFISH-20968

EDIT 3 ---------------------------------------------------------------------------------

20/May/2014 : The bug has been marked as : Must Fix for Glassfish 4.0.1.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top