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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With