JPA disable auto save - java

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?

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. [=

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.

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(){
...
}

Related

Are there performance issues when using both an EntityManager and Spring Repository in the same transacation?

I have a case where my Foo contains an object Bar, and if that Bar object does not already exist in the BarRepository, I want to persist it first and then continue to save the Foo object (don't need to do this when first creating the Foo object, but for some reason need to do this when updating it with a new Bar object).
I am wondering if it is either considered bad practice or any significant performance issues when combining both the an EntityManager (to update) and a Spring Repository (to check if something exists) together.
Here is my particular case:
#Service
#AllArgsConstructor
public class FooService {
private final FooRepository fooRepository;
private final BarRepository barRepository;
private final EntityManagerFactory entityManagerFactory;
// works fine
public Foo update(long id, Consumer<Foo> consumer) {
var foo = get(id);
updateFields(foo, consumer);
var entityManager = entityManagerFactory.createEntityManager();
var transaction = entityManager.getTransaction();
transaction.begin();
if (foo.getBar() != null) {
if (!barRepository.existsById(foo.getBar().getId())) {// option 1
entityManager.persist(foo.getBar());
}
}
var update = entityManager.merge(foo);
transaction.commit();
entityManager.close();
return update;
}
// doesnt work yet, but the idea is this
public Foo update(long id, Consumer<Foo> consumer) {
var foo = get(id);
updateFields(foo, consumer);
var entityManager = entityManagerFactory.createEntityManager();
var transaction = entityManager.getTransaction();
transaction.begin();
if (foo.getBar() != null) {
var barId = foo.getBar().getId();
if (entityManager // option 2
.createNativeQuery(
"SELECT b FROM bar b WHERE b.text = ?1 AND b.duration = ?2")
.setParameter(1, barId.getText()())
.setParameter(2, barId.getDuration())
.getSingleResult()
== null) {
entityManager.persist(foo.getBar());
}
}
var update = entityManager.merge(foo);
transaction.commit();
entityManager.close();
return update;
}
}
Out of these two methods, to me the first one is much cleaner to me because the checking of the the Foo entity is a small, one liner, compared to the other one, but I want to follow whatever the standard is.
Spring Data JPA is build on JPA and uses the EntityManager internally.
Spring Data never aimed at completely removing JPA from your code base. Its goal is to make the easy stuff trivial and comfortable while not getting in your way when you need to use it directly.
So using the EntityManager is completely fine and actually expected for many non trivial applications.
But code accessing the EntityManager lives on a different abstraction layer than the code using a repository.
I therefore recommend to move your EntityManager using code into custom methods of your repositories.

Hibernate - Rollback list of entities if one entity fails

im just working on a project to create, change user in my mysql database. Therefore i have UserService (REST) which creates a user and a GenericDAO class where i can persist users. In my DAO for each user i begin, persist and commit a transaction. Creating single users or find users works perfect.
Now i am facing with the problem to persist or update a list of users. Especially if one user can not be persisted (e.g. duplicates) the hole transaction should be rolled back. It doesnt work in my current setup.
My first idea is to outsource the commit in a separate method. With an loop over all users i only persist them. At the end of the loop i would call my method to commit everything. If a single or more users fails i can catch them with the rollback. Is that a good approach?
AbstractDAO (current)
public abstract class GenericDAO<T> implements IGenericDAO<T>{
#PersistenceContext
protected EntityManager em = null;
private CriteriaBuilder cb = null;
private Class<T> clazz;
public GenericDAO(Class<T> class1) {
this.clazz = class1;
this.em = EntityManagerUtil.getEntityManager();
this.em.getCriteriaBuilder();
}
public final void setClazz(Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
public T create(T entity) {
try {
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
return entity;
} catch (PersistenceException e) {
em.getTransaction().rollback();
return null;
}
}
public T find(int id) {
return em.find(this.clazz, id);
}
public List<T> findAll() {
return em.createQuery("from "+this.clazz.getName()).getResultList();
}
/** Save changes made to a persistent object. */
public void update(T entity) {
em.getTransaction().begin();
em.merge(entity);
em.getTransaction().commit();
}
/** Remove an object from persistent storage in the database */
public void delete(T entity) {
em.getTransaction().begin();
em.remove(entity);
em.getTransaction().commit();
}
Wouldn't the most convenient solution be to simply add methods like createAll()/updateAll()?
Adding separate public methods for starting and persisting the transaction like start() and commit() creates a whole bunch of problems because it means you suddenly introduce a stateful conversation between the Dao and its clients.
The Dao methods now need to be called in a certain order and, worse still, the state of the EntityManager transaction is retained. If you forget to commit() at the end of one service call using your Dao, a subsequent call is going to mistakenly assume a transaction was not yet started, and that call is going to fail 'for no apparent reason' (not to mention that the original call will appear completed when in reality the transaction was left hanging). This creates bugs that are hard to debug, and tricky to recover from.
EDIT As I already pointed out in the comment below this answer, getting programmatic transaction management right is tricky in a multi-layer application structure, and so, I would recommend to have a look at declarative transaction management.
However, if you insist on managing transactions yourself, I would probably introduce sth like a TransactionTemplate:
public class TransactionTemplate {
private EntityManager em; //populated in a constructor, for instance
public void executeInTransaction(Runnable action) {
try {
em.getTransaction().begin();
action.run();
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
} finally {
em.clear(); // since you're using extended persistence context, you might want this line
}
}
}
and use it in a service like so:
public class UserService {
private TransactionTemplate template;
private RoleDao roleDao;
private UserDao userDao; //make sure TransactionTemplate and all Daos use the same EntityManager - for a single transaction, at least
public void saveUsers(Collection<User> users, String roleName) {
template.executeInTransaction(() -> {
Role role = roleDao.findByName(roleName);
users.forEach(user -> {
user.addRole(role);
userDao.create(user);
});
// some other operations
});
}
}
(of course, using the above approach means only one layer - the service layer in this case - is aware of transactions, and so DAOs must always be called from inside a service)

hibernate/jpa complaining "flush during cascade"

My application is crashing with the error below:
org.hibernate.HibernateException: Flush during cascade is dangerous
I am not flushing unless hibernate is doing it on my behalf.
Specs:
webapp on tomcat
hibernate/jpa for persistence (application managed
entity manager)
This is the code of my util class to manage entity manager:
private static EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("returnit");
private static EntityManager entityManager;
public static EntityManager getEntityManager(){
return entityManager;
}
public static EntityManager initEntityManager(){
if (emFactory == null) {
emFactory = Persistence.createEntityManagerFactory( "returnit" );
}
entityManager = emFactory.createEntityManager();
return entityManager;
}
And this is the method that triggers the error:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response post(#HeaderParam(HttpHeaders.AUTHORIZATION) String authHeader, MasterCrossDock mcd) {
EntityManager em = Utils.initEntityManager();
em.getTransaction().begin();
MasterCrossDockDAO.save(mcd);
em.getTransaction().commit();
em.close();
return Response.ok(mcd.getId()).build();
}
public static void save(MasterCrossDock new_mcd) {
List<Receptacle> receptacles = new_mcd.getReceptacles();
List<Long> ids = new ArrayList<Long>();
for (Receptacle r: receptacles) {
ids.add(r.getId());
}
new_mcd.getReceptacles().clear();
EntityManager em = Utils.getEntityManager();
new_mcd.getCountryDestination())
em.createQuery("UPDATE receptacle r"
+ " SET r.masterCrossDock.id = :mcd_id"
+ " WHERE r.id IN :ids")
.setParameter("ids", ids)
.setParameter("mcd_id", new_mcd.getId())
.executeUpdate();
new_mcd.getEreturns());
}
Why am I getting the error above and how to fix it?
Entity manager is not thread safe. Using EntityManager in container managed transatcions is fine, but here you are managing both the EntityManager and the transaction yourself. Also the entity manager is static so you are effectivly re-using it over the different requests you may get from the controller. Incoming call would execute the update query which would invoke a flush.
I noticed that during your initEntityManager you are swapping the static instance of the entityManager with a new one. What about the old reference that may be in use by another thread ?
Do the following:
Delete entirly your method initEntityManager
Delete private static EntityManager entityManager;
Make you method Utils.getEntityManager(); to aways create a new EntityManager
Alternative solution should be to make Spring or your container if you use container manage your transactions. Make a service, annotate it with #Transaction attribute and make Spring/Container inject the EntutyManager in it, or just use spring-data repositories.
The flush operation is called by the EntityTransaction Hibernate implementation, which might be JdbcResourceLocalTransactionCoordinatorImpl in your case, on commit.
Inside SessionImpl, this is what throws the HibernateException.
private void doFlush() {
checkTransactionNeeded();
checkTransactionSynchStatus();
try {
if ( persistenceContext.getCascadeLevel() > 0 ) {
throw new HibernateException( "Flush during cascade is dangerous" );
}
...
Maybe, and I say maybe, some other thread got a hold on the Session object and is operating on your entities.

JPA, when to open and close entityManager

I've setup a spring MVC application for a web application and I'm using Hibernates implementation of JPA 2.1.
I've created my models and am able to interact with the database just fine.
I've also decided to use service classes which will manage returning the entities. What I've done is created a BaseService class, so all other service classes will expand on this, and they'll have access to common functions such as create(), delete(), update() and list().
My problem is I'm unsure as to when I should be creating the EntityManager and when I should be closing it?
Currently, in my controller I'm initiating the required services when the controller loads;
#Controller
#RequestMapping("/mycontroller")
public class TestController {
CarService carService = new CarService();
ShowroomService showroomService = new ShowroomService();
}
}
Here is the BaseService that each other service extends;
public class Service<Ety> {
EntityManager em = null;
public Class<Ety> entityClass;
public Service(Class<Ety> entityClass) {
this.entityClass = entityClass;
em = JPAUtil.getEntityManager();
}
public Ety get(int id) {
Ety object = null;
em.getTransaction().begin();
object = em.find(entityClass, id);
em.getTransaction().commit();
return object;
}
public List list() {
List<Ety> objects;
em.getTransaction().begin();
objects = em.createQuery("SELECT c FROM "+entityClass.getName()+" c").getResultList();
em.getTransaction().commit();
return objects;
}
public void save(Ety object) {
em.getTransaction().begin();
em.persist(object);
em.getTransaction().commit();
}
public void update(Ety object) {
em.getTransaction().begin();
em.merge(object);
em.getTransaction().commit();
}
public void delete(Ety object) {
em.getTransaction().begin();
em.remove(object);
em.getTransaction().commit();
}
}
Here's an example Service which expands the above;
public class CarService extends Service<Car> {
public CarService() {
super(Car.class);
}
}
As you can see, I'm creating an EntityManager when the service is created, but at the moment I'm not closing it anywhere.
I'm I creating the entity manager in the correct place? when should I close it.
I had considered putting the entity manager in a static property and creating it within a filter, and then closing it at the end of the application, however I do believe this wouldn't be thread safe and would cause issues?
Any advice would be appreciated.
your CarService should be a spring bean and the instance is created from spring. NOT from your code. The same with the EntityManager. You can use the entityManager with the #autowired annotation.
You open a new EntityManager for each transaction.
This EntityManager is like a Bag mapped to the database, but with zero entity managed inside when it's just opened.
When you work with it, this Bag will be filled with some entities and Hibernate will work to create the adequate requests.
You will close this Bag to save memory at the end of the transaction.
Of course there is some tricks to have many transactions for a given EntityManager, but you have the most general idea. As always it depends...
If you use a framework like Spring or JavaEE, it will open and close the EntityManager, as well starting and committing transactions for you. You have only your business work to write.

How to implement saving in a Repository with JPA/Hibernate

What I want is to implement the Repository pattern in a JPA/Hibernate application. I have a generic interface that describes the basic contract of my repositories:
public interface EntityRepository<Entity extends Object, EntityId> {
Entity add(Entity entity);
Entity byId(EntityId id);
void remove(Entity entity);
void removeById(EntityId id);
void save();
List<Entity> toList();
}
And here is an implementation of such an interface:
public class EntityRepositoryHibernate<Entity extends Object, EntityId>
implements Serializable,
EntityRepository<Entity, EntityId> {
private static final long serialVersionUID = 1L;
#Inject
protected EntityManager entityManager;
protected Class<Entity> entityClass;
public EntityRepositoryHibernate(Class<Entity> entityClass) {
this.entityClass = entityClass;
}
public EntityManager getEntityManager() {
return entityManager;
}
#Override
public Entity add(Entity entity) {
entityManager.persist(entity);
return entity;
}
#SuppressWarnings("unchecked")
#Override
public Entity byId(EntityId id) {
DetachedCriteria criteria = criteriaDAO.createDetachedCriteria(entityClass);
criteria.add(Restrictions.eq("id", id));
return (Entity)criteriaDAO.executeCriteriaUniqueResult(criteria);
}
#Override
public void remove(Entity entity) {
if(entity==null)
return;
entityManager.remove(entity);
}
#Override
public void removeById(EntityId id) {
remove(byId(id));
}
#Override
public List<Entity> toList() {
throw new UnsupportedOperationException("toList() not implemented in "+entityClass.getName());
}
#Override
public void save() {
entityManager.flush();
}
}
All methods are working fine, except save(), so this is the focus here.
As far as I understand, Hibernate is able to track all changes in any instance returned by a query (the byId() method). So, the idea of the save() method is to save any instances that where retrieved and changed, that's why the method does not receives any parameters, it is supposed to save everything that has to be saved (which means, any persistent instance that was retrived and somehow updated while the repository lives.
In a possible scenario, I could call byId() 10 times to retrieve 10 different instances and change only 4 of them. The idea is that by calling save() once, those 4 instances would be saved in the data server.
Problem is when I call flush() I receive an exception stating that there is no transaction active. Since I'm using a JTA persistence unit, it's illegal to open the transation programatically by calling entityManager.getTransaction().
Considering that, what to do to fix the code?
First of all, it seems that your are missunderstanding the purpose of EntityManager.flush method. It doesn't commit any changes managed by persistence context, just sends SQL instructuions to the database. I mean, for the same JTA transaction, when you retrieve and modify some entity instances the changes/SQL instructions are cached waiting to be sent to the database. If the underlying transaction is commited this changes are flushed to the database along with the commit instruction. If you invoke flush before transaction is commited, only flush the changes until the invokation point (well, some SQL instruction could have been flushed previously by reasons out of this matter) but not the commit instruction is sent.
How to fixed?
I suggest you to don't mix Repository Pattern with Transaction manipulation.
Looks like you are using Container Managed Transactions (javaee tutorial) so just erase the save method and let container to manage the transactions. This will change your focus, you now have to care about rolling back transactions (throwing exception or invoking setRollbackOnly) but you don't need to explicit commmit.

Categories

Resources