I've been reading about the sun blueprint GenericDAO implementation and Gavin King's take on this for use with Hibernate. It seems he doesn't mention anything about transaction handling:
public abstract class GenericHibernateDAO<T, ID extends Serializable> {
protected Session getSession() {
return HibernateUtil.getSessionFactory().getCurrentSession();
}
public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}
}
I'm puzzled as to where I should put the start/end of the transaction. Currently they are inside the DAOs that extend this GenericHibernateDAO
public class FooHibernateDAO extends GenericHibernateDAO<Foo, Long> {
public Foo saveFoo(Foo foo) {
getSession().beginTransaction();
makePersistent(foo);
getSession().getTransaction().commit();
}
}
Should the transaction handling be managed by the caller of the DAO in the application tier?
Generally the best practice is to manage transactions in service layer not in DAO layer. Each DAO method generally handles one specific operation and a service method aggregates them in one transaction.
Transactions should be managed in the application tier. Say for example you had an AccountDAO:
public class AccountDAO {
public void DebitAccount( int accountId, int dollars ) {
}
public void CreditAccount( int accountId, int dollars ) {
}
}
If I wanted to transfer money between accounts, I would call DebitAccount on one account and CreditAccount on another. I would want these calls to happen in the same transaction. The DAO can't possibly know that, but the application tier would.
If transactions were managed at the DAO tier, you would need to create another TransferMoney method on the DAO to do it in one transaction. This would eventually bloat your DAO tier and, for complex operations, bring in business logic that probably shouldn't be there. And it gets even messier if you have an operation that requires multiple DAOs to be involved in a single transaction.
Related
I am using JPA/hibernate, Spring and JSF.
so my application is organized as following:
I have my entities,
My Dao Interface and implementation for each entity where I define basic methods: findById, add, update, remove ...
and then I have my service layer which just use DAO interfaces and where ther is basically the same methods as in my DAO.
My problem is that in my backing bean, I have a method Add_hospital(), which add a hospital and also services in that hospital, so my method looks like
add_hospital(){
add-hospital();
add-services();
add-Hospital-schedule();
}
so this method is a transaction and I want that if some issue happen, the transaction rollback, but I know that the rollback need to be managed in my DAO, will I need to define my method Add_hospital() in my managed bean, and it's in this stage where I have this combination of inserts.
Please how to solve this problem?
Transactions should be managed on the service layer, not data access.
Example from spring:
#Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
What is the difference between using #Transactional in dao layer and in service layer?
#Transactional
class UserDAO{}
or
#Transactional
class UserService{}
We can put this annotation in both the layers. So what is the difference?
if you add #Transactional annotation at Service layer and you are performing multiple operations on database, then all these operations will be in a single transaction and with that you can make sure that both the operations are commited, if any of them failed then roll back.
For example: There is a case you want to create an Employee and your business rule says that for every Employee you create, you need to create User in your database. In such case we would use Transactional annotation at service layer
#Service
public class EmployeeService {
....
#Transactional
public int createEmployee(Employee emp) {
//create Employee
employeeDao.createEmployee(emp);
User user = new User();
// some fileds of employee are used to create a User
user.setEmployeeId(emp.getEmployeeId());
....
userDao.createUser(user);
...
}
}
There is no difference at all. But best practice is use it in Service layer. Cause sometimes you need to make transaction through more than one entity. So if you declare transactions in your dao manually. And you call method from service with two methods from your daos you will have two transactions in one call. And that is something what you don't want.
How do I achieve a complete transaction where a service class requires more than one data access object. Assuming I have the following structure.
Currently, if my dao2 failed, the dao1 still being committed to the database which I do not want it to happen. However, I need my DAO to be reusable.
public class mainService(){
dao1.store(obj1);
dao2.store(obj2);
}
And my dao is written in this way.
Dao 1
private EntityManager entityManager;
#Transactional
public void store(Object obj1){
entityManager.persist(obj1);
}
Dao 2
private EntityManager entityManager;
#Transactional
public void store(Object obj2){
entityManager.persist(obj2);
}
Please help.
Make this happen in one transaction.
#Transactional
public void mainService(..){
dao1.store(obj1);
dao2.store(obj2);
}
Only one transaction will be created, and if dao2 fails, dao1 won't be committed as well. And remove #Transactional from store method. Making DB level methods transactional it's not a good idea. This level might be two low in some cases.
The obvious answer is to use:
private EntityManager entityManager;
#Transactional
public void store(Object obj1, Object obj2){
entityManager.persist(obj1);
entityManager.persist(obj2);
}
What's happened is that you have become hung up on using DAOs. The DAO is dead! If you look at the EntityManager API you will notice that it actually looks very much like a DAO type of interface. So just use it directly where ever you would normally consider using a DAO.
In your case, you should mark mainService() with #Transactional too, For transaction propagation rule is PROPAGATION_REQUIRED by default , and then both dao1.store() and dao2.store() with be under the same transaction.It's OK to keep dao1.store() and dao2.store() #Transactional, they will realize that there is already a transaction and then join the transaction.
My Spring+Hibernate configuration files are small and super tight. I use auto scanning to find my model entities/daos.
I don't want to have to write a DAO + DAOImpl for EVERY Entity in my hierarchy.
Some may qualify to have their own, like if they have complex relationships with other entities and require more than basic CRUD functionality. But for the rest...
Is there any way to circumvent the defacto standard?
Say, something like a generic DAO, ex:
http://www.ibm.com/developerworks/java/library/j-genericdao/index.html
Then I can do something like
GenericDao dao = appContext.getBean("genericDao");
dao.save(car);
dao.save(lease);
Is this possible with annotations? I don't want to have to configure anything in xml. If I cannot do above, is it still possible to have one GenericDaoImpl.java with something like:
#Repository("carDao")
#Repository("leaseDao")
class GenericDaoImpl extends CustomHibernateDaoSupport implements GenericDao {
...
}
and then
GenericDao dao = appContext.getBean("carDao");
dao.save(car);
dao = appContext.getBean("leaseDao"); //carDao is garbage coll.
dao.save(lease);
Is this practical at all?
Using generics, you might try something like this:
#Repository
#Transactional
public class GenericDAOImpl<T> implements GenericDAO<T> {
#Autowired
private SessionFactory factory;
public void persist(T entity) {
Session session = factory.getCurrentSession();
session.persist(entity);
}
#SuppressWarnings("unchecked")
public T merge(T entity) {
Session session = factory.getCurrentSession();
return (T) session.merge(entity);
}
public void saveOrUpdate(T entity) {
Session session = factory.getCurrentSession();
session.saveOrUpdate(entity);
}
public void delete(T entity) {
Session session = factory.getCurrentSession();
session.delete(entity);
}
}
The content may be different, but the general idea is applicable.
You should be able to then autowire the DAO in your controller and service classes by using
#Autowired
private GenericDAO<Car> carDao;
You can combine Spring/Hibernate with JPA, which provides the EntityManager for a large amount of basic persistence tasks:
#Service
public class CarService {
#PersistenceContext
private EntityManager em;
public void saveCarAndLease(Car car, Lease lease) {
em.persist(car);
em.persist(lease);
}
}
It will also handle transactions and simple queries without needing to write a DAO. For the more complex operations, you can still write a DAO and fall back to Hibernate's SessionFactory (although JPA is an option here too).
Some tutorials suggest you should still write the DAO for abstraction of the JPA plumbing. However, I have personally found this unnecessary (JPA has a very small integration footprint), and in fact this is also the way Spring Roo deals with the data layer behind the scenes.
Have you tried to use Spring Data. I mean to say Spring JPA where you can use repositories.
You can eliminate writing all stuffs for each entity.
im in trouble with implemenetation of a service layer, i think i did not understand this concept very well.
In a DAO implementation i can write all CRUD logic for a specific technology and entity (for example hibernate and User table), and in a service layer we use a DAO for all data operation for the entity in DAO (like getUser, loginUser, etc..) is this ok?
If this is ok i have a simple question, can i handle database connection (or in case of hibernate, session and transaction) within service layer, DAO implementation or neither?
Example, i have a simple GUI with one Button(load all User), and a table will contains all User. Pressing the Button will load the table with all users.
I have a HibernateDAO for User entity (UserHibernateDAO) containing all CRUD operation and a service layer, UserService, for some specific data operation with user.
ServiceLayer:
public class UserService extends AbstractServiceLayer{
private AbstractDAO dao;
public UserService(AbstractDAO dao){
this.dao=dao;
}
public List<User> loadAllUsers(){
return dao.findAll();
}
}
In actionperformed of Button:
private void buttonActionPerformed(ActionEvent evt) {
Transaction transaction=HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
List<User> users=userService.loadAllUsers();
loadTableWithUsers(users);
transaction.commit();
}
Is this implementation ok?
Session and transaction handle is in the right position or i have to put it into service layer? ..or perhaps into dao?
EDIT1:
If i have an interface UserDAO and a UserHibernateDAO that implements UserDAO, service layer has no reason to exists, isn't true?
Becouse i can have all method to manage an "USER" inside my UserDAO and UserHibernateDAO implements all this methods for hibernate technology... then i could have a UserJdbcDAO, UserMysqlDAO etc... mmm...
EDIT2:
private void buttonActionPerformed(ActionEvent evt) {
myBusinessMethod();
}
private void myBusinessMethod(){
Transaction transaction=HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
List<User> users=userService.loadAllUsers();
loadTableWithUsers(users);
//some other useful operation before close session
transaction.commit();
}
im not sure, a business method is a method like this?
Thanks all.
You are handling the transaction inside your actionPerformed() method. Its clearly defeating the purpose of DAO/Service layer
Your UserService is accepting AbstractDAO, which means some other code may pass wrong DAO implementation to your UserService that will screw things up
Now, few suggestions.
You can look into GenericDAO concept for this. That might suit your need
Most of the time we ain't need all these layers like Service, DAO and BusinessDelegate. So, question yourself are any of these really answering some of your questions. If not, get rid of them. YAGNI
Get rid of DAO completely, and treat your Hibernate API as your DAO. Handle database transaction in your business methods. You may like to read this question
[Edited]
After your edit my 3rd suggestion doesn't carry much weight. By the way, you name your DAOs as follows; UserJdbcDAO, UserMysqlDAO etc. Your 2nd name is not making much sense, as we use ORMs just to avoid DB vendor specific DAOs/queries. It might start making some sense if your UserMysqlDAO extends UserJdbcDAO.