implementing a dao class properly to manage transactions - java

I am working on a java web application that calls database backend through hibernate.I use servlets,jsp and tomcat for test/deployment.Most books on java-ee suggested using Dao classes for database calls.As per examples given in books(Hibernate Recipes by Gary Mak),I created a generic base class and a specific subclass as below.
class BaseDao{
private Class persistentClass;
public BaseDao(Class persistentClass) {
super();
this.persistentClass = persistentClass;
}
public Object findById(Long id) {
SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
Object object = null;
try {
object = (Object) session.get(persistentClass, id);
return object;
}
finally {
session.close();
}
}
#Override
public void saveOrUpdate(Object obj) {
SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.saveOrUpdate(obj);
tx.commit();
}catch(HibernateException e){
if (tx != null) {
tx.rollback();
}
throw e;
}finally {
session.close();
}
}
}
class SaleOrderDao extends BaseDao{
public SaleOrderDao() {
super(SaleOrder.class);
}
#Override
public SaleOrder findSaleOrderById(Long saleOrderId){
SaleOrder so = (SaleOrder)findById(saleOrderId);
return so;
}
#Override
public void saveOrUpdateSaleOrder(SaleOrder so){
saveOrUpdate( so);
}
}
While going through the posts in this forum ,I came across Ryan Stewart's advice that beginning and ending transactions in a dao method is not recommended..Sadly, my project does not use any web framework that supports transaction management..and I am limited to using jsp,servlets and a servlet container..
Is there some way I can rewrite my dao implementations sothat transactions can be managed properly..I couldn't find anything in this regard from those books I read..
Hope somebody helps me with suggestions
sincerely,
Jim

Normally transactions should not be handled in the DAO. They should be handled by the service layer. One service method may include multiple DAO calls that are all in the same transaction.
Spring (as well as other DI frameworks) allows you to do that by simply annotating your service methods with #Transactional. Without spring you can still do that manually in the service layer

Related

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)

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.

is it possible to use #transactional annotation with a transaction opened by hand (spring/hibernate)

I have a spring application which works with hibernate and annotations.
I open a session by hand which is done like in this How do I manually open a hibernate session? stack overflow answer.
Now i want to use a method of a service in the method that opens the session itself. This service however is annotated with the transactional statement. Is it possible to tell the method to use the transaction i just opened by hand?
e.g.
#Transactional("TransactionManager")
class Service1 {
public LazyObject someMethod();
}
class MetaService {
#Autowired
SessionFactory sf;
Service1 s1;
public someMethod() {
Session s = sf.openSession();
s.beginTransaction();
// tell this method to use s's transaction
// without annotating someMethod() with #transactional
LazyObject lo = s1.someMethod();
for ( LazyAtt la : lo.getLazyAtt() ) {
la.doSomething();
}
s.flush();
s.getTransaction().commit();
s.close();
}
}
For those wondering why I want to do it, check this question:
https://stackoverflow.com/questions/29363634/how-to-open-two-sessions-in-one-function-in-hibernate-spring

creating a generic DAO class for Hibernate

In my web application, there are quite a few entities, and most of them require CRUD operations. So I am thinking about writing a generic DAO that can handle CRUD for all of the entities. I've found a tutorial article from IBM, but don't quite understand the generic implementation using the generic type 'T', and 'PK'. The article is at this link
I wrote the following DAO by using Object type in all the methods, and they seem working just fine - all my entities are able to do CRUD with the following CommonDao class. Although it works for my needs, I'm looking for what is the best practice for implementing a generic DAO class for Hibernate.
public class CommonDao
{
private final static SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
public CommonDao() {}
#UnitOfWork
public List findAll(Object className)
{
List types = null;
Session session = sessionFactory.openSession();
Criteria criteria = session.createCriteria(className + ".class");
types = (List <Object>) criteria.list();
session.close();
return types;
}
#Transactional
public void saveObject(Object obj)
{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.saveOrUpdate(obj);
tx.commit();
session.close();
}
#Transactional
public void saveObjectWithManyEntities(Object obj, Set<Object> objects) /* for OneToMany relationships */
{
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.saveOrUpdate(obj);
for (Object o : objects)
{
session.save(o);
}
tx.commit();
session.close();
}
}
The best way to do it is to include Spring Data into your project. The JPA repositories offer basic CRUD, pagination, sorting and most of your queries could be built automatically from the methods naming convention.
Before Spring Data we'd resort to such Generic Daos but not anymore.

throwing meaningful exceptions from hibernate DAO implementation

In my web application(jsp+hibernate+hsqldb on tomcat) code, I am using couple of Dao implementations.The base class Dao implementation contains all the session open,close logic.A number of domain specific Dao classes extend this base class to provide specific find(),delete() methods
I wanted to give the user meaningful messages when an error occurs ,instead of a error500 message .
Since,the base class method uses a hibernate.Session class for get(),saveOrUpdate() methods ,they throw HibernateException.The domain specific subclasses need to catch this an wrap it in some Custom Exception and rethrow it.
I tried it this way..I don't know if this is the correct way to do it..I would welcome your opinion/suggestions
sincerely,
Jim
abstract class BaseDao{
private Class persistentClass;
public BaseDao(Class persistentClass) {
super();
this.persistentClass = persistentClass;
}
public Object findById(Long id) {
SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
Object object = null;
try {
object = (Object) session.get(persistentClass, id);
return object;
}
finally {
session.close();
}
}
#Override
public void saveOrUpdate(Object obj) {
SessionFactory factory = HibernateUtil.getSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.saveOrUpdate(obj);
tx.commit();
}catch(HibernateException e){
if (tx != null) {
tx.rollback();
}
throw e;
}finally {
session.close();
}
}
}
The domain specific dao is
class SaleOrderDao extends BaseDao{
public SaleOrderDao() {
super(SaleOrder.class);
}
#Override
public SaleOrder findSaleOrderById(Long saleOrderId){
SaleOrder so = (SaleOrder)findById(saleOrderId);
return so;
}
#Override
public void saveOrUpdateSaleOrder(SaleOrder so){
try{
saveOrUpdate( so);
}catch(HibernateException e){
String msg = "could not insert/update saleorder"+so.getSONumber();
throw new SaleOrderDaoException(msg+"/ "+e.getMessgae());
}
}
}
Are you sure that customer want to have meaningful message? I believe that meaningful error should appear just in case of business errors. For technical (read, unexpected) errors customer should see just generic error page, probably with error reference code, but no more that that.
Another problem with your code is you are going to include e.getMessage into error message. It is not good, because, potentially, that message can have some technical information, which may help to break into your system. But, saying that, logs have to have as much information as possible (within sensible limits, there shouldn't be passwords, card details) about the error.
So the basic rule - for technical errors show to customer as least as you can. Business errors are the other story, here you should be as clear as possible.

Categories

Resources