Reputation: 2897
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.
Upvotes: 6
Views: 1778
Reputation: 3387
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 :
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.
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).
@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.
Upvotes: 2
Reputation: 2897
According to Checkus, it seems to be a bug in GF4: https://java.net/jira/browse/GLASSFISH-20968
Upvotes: 0