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)
Related
I have method where I'm trying to change some entity, also in this method I want to save transaction information.
When any exception occurred I want to roll back saving entity but still want to save transaction.
So how to make one repository for entity transactional but repository for transactions not?
There code from repository
#Override
#Transactional(noRollbackFor=NotEnoughAmountInAccountException.class)
<T extends Transaction> T save(T transaction);
but it doesn't help.
Saving transaction placed in final block.
UPDATE
I solved it by using AOP. I create Transaction object in aspect advice and save it here, out of JPA transaction.
Do it in a new #Transactional
#Transactional(propagation=Propagation.REQUIRES_NEW)
<T extends Transaction> T save(T transaction);
This will save your transaction element even if the other #Transactional gets rolled back
I solved it by using AOP. I create Transaction object in aspect advice and save it here, out of JPA transaction.
It's #Transactional method
#SaveTransaction
#Transactional
public synchronized Transaction move(#NonNull String accountName, #NonNull String destinationName, #NonNull BigDecimal amount, String comment) {
checkAmountIsPositive(amount);
Account donor = getAccount(accountName);
Account acceptor = getAccount(destinationName);
if (!isEnoughAmount(accountName, amount)) throw new NotEnoughAmountInAccountException();
BigDecimal newDonorAmount = donor.getAmount().add(amount.negate());
BigDecimal newAcceptorAmount = acceptor.getAmount().add(amount);
donor.setAmount(newDonorAmount);
acceptor.setAmount(newAcceptorAmount);
accountRepository.save(donor);
accountRepository.save(acceptor);
return null;
}
And it's aspect advice
#Around("#annotation(com.softjourn.coin.server.aop.annotations.SaveTransaction)")
public Transaction saveTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
Transaction transaction = prepareTransaction(joinPoint);
try {
joinPoint.proceed();
transaction.setStatus(TransactionStatus.SUCCESS);
return transaction;
} catch (Throwable e) {
transaction.setStatus(TransactionStatus.FAILED);
transaction.setError(e.getLocalizedMessage());
throw e;
} finally {
transactionRepository.save(transaction);
}
}
Also it's important to make order of this advice higher than order of #Transactional so this advice will be over transaction.
Set #Order(100) on aspect class.
By default it's order is smaller so it's under transaction.
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.
I got a method call from a stateless ejb which looks like the following
#Stateless
#Local(MyCrudService.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public class MyCrudServiceBean implements MyCrudService {
#EJB
private CrudService crudService;
#Override
public void writeLogEntry(StatementLog statementLog) {
try {
crudService.execute(statement.getSql());
} catch (Exception e) {
crudService.writeLogEntry(statementLog);
throw new MyApplicationException(e.getLocalizedMessage());
}
}
// ...
}
CrudSerivce:
#Stateless
#Local(CrudService.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
#Interceptors(GenericFrepDataBaseUserInterceptor.class)
public class CrudServiceBean implements CrudService {
public static final String PERSISTENCE_UNIT_NAME = "name";
private EntityManager entityManager;
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public void execute(String sqlString) {
Query query = entityManager.createNativeQuery(sqlString);
query.executeUpdate();
}
#Override
public void writeLogEntry(StatementLog statementLog) {
entityManager.persist(entity);
}
// ....
}
Statement is an entity which got an sql which is invalid (like 'invalid sql'). On execution I get the following error
javax.ejb.EJBTransactionRolledbackException: JBAS011469
If I debug this, I can see that this happens in the line with the logging.
I think the problem is, that because I am getting an exception the transaction gets rolled back. Because of that it is not possible to write into the db, because there is no open session anymore. Is this the case? What's best practice then? To manually open a session by myself seems quite ugly to me.
Your method log.writeErrorInDb needs to start its own transaction, so that it can still operate when the main transaction is rolled back. Yes, if your Hibernate session is already closed, then your log class would need to be able to open its own session. However it would probably be better to have a transaction boundary covering this entire block of code, and bind the Hibernate session to that, then set your log method to require a new transaction, to ensure it can operate once the first transaction is marked for rollback. i.e. two transactions, but one session
Based on your code, you should be able to annotate your log method:
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void writeLogEntry(StatementLog statementLog) {
entityManager.persist(entity);
}
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.
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(){
...
}