JPA disable auto save
Asked Answered
T

3

8

i use JPA eclipse Toplink & EJB

everytime i receive entity from database then change some data inside of it,

database(MYSQL) automaticly change too without i commit or do something

Example:

Player p = em.findById(1);
p.setName(newName);
// i do nothing , but database automaticly change

i already read related question here, but i still can't figure it out

i think i must detach entity but i don't know how to do this, because

i have class GenericDAO generated from netbeans

GenericDAO (generated from netbeans)

public abstract class GenericDAO<T> {
    private Class<T> entityClass;

    public GenericDAO(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    protected abstract EntityManager getEntityManager();

    public void insert(T entity) {
        getEntityManager().persist(entity);
    }

    public void edit(T entity) {
        getEntityManager().merge(entity);
    }

    public void delete(T entity) {
        getEntityManager().remove(getEntityManager().merge(entity));
    }

    public T findById(Object id) {
        return getEntityManager().find(entityClass, id);
    }

    public List<T> findAll() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<T> findRange(int[] range) {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        q.setMaxResults(range[1] - range[0] + 1);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }

    public int count() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(rt));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

}

DaoPlayer

@Local
public interface DaoPlayer{
    void insert(Player player);

    void edit(Player player);

    void delete(Player player);

    Player findById(Object id);

    List<Player> findAll();

    List<Player> findRange(int[] range);

    int count();

ImplPlayer

@Stateless
public class ImplPlayer extends GenericDAO<Player> implements DaoPlayer{
    @PersistenceContext(unitName = "MonsterPuzzle")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    public ImplPlayer() {
        super(Player.class);
    }

}

My question is

  • how to disable JPA automaticly save to database?
  • ( Because i think i need to detach )how to detach the entity from GenericDAO?
Thereof answered 23/9, 2014 at 0:23 Comment(0)
T
2

You will need to look into how you are managing transactions in your application. Consider one of your methods:

Player findById(Object id){}

If you are using Container Managed Transactions in a Java EE environment, you just need to mark the transaction as not supported in the DAO.

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
Player findById(Object id) {}

This will make sure that the transaction is not open when the Dao passed the entity back to a different layer. Any changes you make to the entity will not be synchronized with the database.

Twylatwyman answered 23/9, 2014 at 0:36 Comment(2)
where should i place the annotation sir? in interface in implement class or in GenericDao ? i already test in interface is not working, but i will check againThereof
In the implementation class.Twylatwyman
L
3

Understand this: "every update in an attached entity will be reflected in the database".

When an entity is attached? To put it in a simple way, every entity that pass through the EntityManager with the methods: persist, merge, refresh, delete (maybe I am forgetting some any other here) or in the query (JPQL or find) methods inside a transaction.

Look at the sample below:

entityManager.getTransaction().begin();
Dog dog = entityManager.find(Dog.class, 1);
dog.setGoodBoyLevel(99);
entityManager.getTransaction().commit();

Since dog passed by the find method inside a transaction it can be considered managed.

If you are using EJB you must know that the default transaction is REQUIRED. The REQUIRED works like: If there is a transaction opened, let us use it. If there is no transaction opened, let us open it. (It is a nice guy)

Since your method is without any indicator to suspend the transaction every update in the entity will be reflected in the database. What could you do? Use the NOT_SUPPORTED transaction way:

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void yourMethod(){
    // to awesome things
}

Since you are using the NOT_SUPPORTED transaction option your entity will be detached. What does detached means? The JPA will not analyse the updates in the entity. If you want just read some info, that is ok. But the code below will raise exception:

entityManager.getTransaction().begin();
Dog dog = new Dog();
Owner owner = getDetachedOwner(ownerId);
dog.setOwner(owner);
entityManager.persist(dog);
entityManager.getTransaction().commit();

The problem here is that the owner is detached, so the JPA do not knows it and would raise an exception.

Be careful with the NOT_SUPPORTED option. [=

Luckily answered 23/9, 2014 at 2:3 Comment(2)
Is there no general way to disable this behaviour of auto-save? I don't want to miss the transaction context but just want to explicit save a changeHargrove
Well, you should use Bean Manager Transaction. You will control the transaction manually.Luckily
T
2

You will need to look into how you are managing transactions in your application. Consider one of your methods:

Player findById(Object id){}

If you are using Container Managed Transactions in a Java EE environment, you just need to mark the transaction as not supported in the DAO.

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
Player findById(Object id) {}

This will make sure that the transaction is not open when the Dao passed the entity back to a different layer. Any changes you make to the entity will not be synchronized with the database.

Twylatwyman answered 23/9, 2014 at 0:36 Comment(2)
where should i place the annotation sir? in interface in implement class or in GenericDao ? i already test in interface is not working, but i will check againThereof
In the implementation class.Twylatwyman
H
-1

2021 Solution,

Put it top of your class

@Transactional(propagation = Propagation.NOT_SUPPORTED)
 public class YourClass{
   ...
} 

or you can use, only on your method,

@Transactional(propagation = Propagation.NOT_SUPPORTED)
 public void yourMethod(){
   ...
} 
Hyperplasia answered 12/1, 2021 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.