Out team is currently programming a JavaEE webapplication for use on a Tomcat appserver.
We want to handle persistence using Hibernate (5.0.1). To access the database entities, we use EntityManagers (not from JPA, they were implemented by us, see below) which provide methods to list, create and delete rows in the associated tables. The model classes use Hibernate Annotations for the mapping.
We also have a static class PersistenceController which initializes Hibernate's SessionFactory and provides a static method to get a newly opened session.
Of course we want to be able to use unit tests to test the functionality of our classes, so the PersistenceController is a little thorn in our eyes.
Someone else recommended me to move everything from the PersistenceController into the EntityManager base class. He wasn't sure if this would have any side consequences though.
So I thought "let's ask the hive mind". What would be the best practice in this case?
(If more code is needed, I'm happy to provide it)
PersistenceController
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class PersistenceController {
private static final SessionFactory sessionFactory;
static {
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
try {
sessionFactory = new MetadataSources(registry).buildMetadata()
.buildSessionFactory();
} catch (Exception e) {
// The registry would be destroyed by the SessionFactory, but we had
// trouble building the SessionFactory
// so destroy it manually.
StandardServiceRegistryBuilder.destroy(registry);
throw e;
}
}
public static Session openSession() {
return sessionFactory.openSession();
}
}
EntityManager
import java.util.List;
public abstract class EntityManager<T extends PersistenceEntity> {
public abstract List<T> listAll();
public abstract void save(T entity);
public abstract void delete(T entity);
}
ProductManager
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.Transaction;
import etepat.model.product.Product;
public class ProductManager extends EntityManager<Product> {
public ProductManager() {
super();
}
#Override
public List<Product> listAll() {
try (Session session = PersistenceController.openSession()) {
Transaction transaction = null;
try {
transaction = session.beginTransaction();
#SuppressWarnings("unchecked")
List<Product> returned = session.createCriteria(Product.class)
.list();
transaction.commit();
return returned;
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
}
}
return new ArrayList<Product>();
}
#Override
public void save(Product entity) {
// TODO Auto-generated method stub
}
#Override
public void delete(Product entity) {
// TODO Auto-generated method stub
}
}
The idea of your EntityManager(better call BaseDao or GenericDao) is good but needs some improvements.
First, base CRUD methods don't have to be abstract. They can simply persist/load/delete/list on generic type T. That way you don't have to write these methods on each subclass. See this generic dao approach https://github.com/athanasiosem/Hibernate-Generic-Dao-Java/blob/master/src/main/java/com/demien/hibgeneric/dao/GenericDAOImpl.java
Second is, you're managing transactions manually, is there a good reason to do so?
With container managed transactions(using annotations) you don't need to, and they greatly simplify your code by eliminating the boilerplate try{...}catch{//rollback}.
Basically, with a GenericDao<T> and container managed transactions you don't need this code at all, your classes sublass the GenericDao<ConcreteType> with concrete types and they're ready to do CRUD on database without a single line of code.
Related
I have a list of repository beans existing in my Spring Boot application and I need to link each CrudRepository into corresponding entity class so I end with a Map<Class, CrudRepository> object.
Example repository:
public interface ReviewRepository extends CrudRepository<Review, ReviewId>
And I need help with the following method linking repository with a proper entity class:
#Service
public class MyService {
final Map<Class, CrudRepository> repositoryMap;
public MyService(List<Class> entityClasses, List<CrudRepository> existingRepositories) {
this.repositoryMap = logicGoesHere(entityClasses, existingRepositories); // <== HERE
}
This works. Might be the [0] should be replaced by actual lookup (in my case just by accident zero was a correct index)
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import sun.reflect.generics.repository.ClassRepository;
import static java.util.function.Function.*;
import static java.util.stream.Collectors.*;
#Configuration
class Config {
#Bean
public Map<Class, JpaRepository> classToRepo( List<JpaRepository> repos) {
return repos.stream().collect(toMap(this::extractEntityClass, identity()));
}
private Class extractEntityClass(JpaRepository repository) {
try {
Method getGenericInfo = Class.class.getDeclaredMethod("getGenericInfo");
getGenericInfo.setAccessible(true);
ClassRepository cr = (ClassRepository) getGenericInfo.invoke(repository.getClass().getInterfaces()[0]);
Type t = ((ParameterizedTypeImpl) cr.getSuperInterfaces()[0]).getActualTypeArguments()[0];
return (Class) t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
And you can inject it using #Qualifier("classToRepo")
The hibernate documentation says:
With CMT, transaction demarcation is declared in session bean deployment descriptors, rather than performed in a programmatic manner.
but I can't find any complete example on how to do this.
This is what I have in mind my code should look like:
#Stateless
public class Dao{
#Inject // or some other annotation
private SessionFactory factory;
public void doDaoStuff(){
Object obj = factory.getCurrentSession().get(Entity.class, "Id");
// do something with obj
return;
}
}
It is free from all the boilerplate that hibernate has as transaction should be started, committed and roll backed by container.
So, Is it possible to do this? Although the documentation says that required declarations should be specified in bean deployment descriptors, doing it with annotations would be great.
In JavaEE environments Hibernate can use the CMT (Container Managed Transaction) strategy which will bind hibernate transactions with the underlying JTA transaction eliminating the need to manually begin, commit and rollback transactions. Example here.
However, there are problems:
This doesn't work in all Java EE Containers. Newer versions of Websphere are not supported and quoting from the source code of hibernate - WebSphere, however, is not a sane JEE/JTA container...
This restricts to one session one transaction idiom. Thus during the invocation of a EJB business method, there can be only one JTA or Hibernate transaction.
Luckily, using CDI, and some custom interceptors this can be solved and a lot of Hibernate boilerplate can be removed. I wrote a sample on github.
This approach creates a wrapper for Hibernate SessionFactory and provides most commonly used APIs. Using CDI, #RequestScoped Sessions are opened and closed automatically. Transactions are managed using an Interceptor. #RequestScoped ensures one Session per request so sessions aren't shared among multiple requests.
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
#RequestScoped
public class MySessionFactory implements SessionFactoryTemplate{
#Inject
private SessionFactory sessionFactory;// Inject after creating the singleton instance
private Session currentSession;
public Session openSession(){
return sessionFactory.openSession();
}
public Session getCurrentSession(){
if(currentSession == null){
currentSession = sessionFactory.openSession();
}
return currentSession;
}
public StatelessSession openStatelessSession() {
return sessionFactory.openStatelessSession();
}
#PreDestroy
private void closeSession(){
if(currentSession!=null && currentSession.isOpen()) {
currentSession.close();
}
}
}
This implementation is then injected in database layer and used to obtain sessions.
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.ares.cdi.hibernate.interceptors.Transactional;
public class Dao {
#Inject
private MySessionFactory sf;
public void get(int id){
sf.getCurrentSession().get(clazz,id);
}
#Transactional
public void add(Object entity){
sf.getCurrentSesion().add(entity);
}
}
Transactions are managed by TranscationManager interceptor and declared by #Transactional annotation.
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.resource.transaction.spi.TransactionStatus;
#Interceptor
#Transactional
public class TransactionManager {
#Inject
private MySessionFactory sessionFactory;
#AroundInvoke
public Object handleTransaction(InvocationContext context) throws Exception{
Session session = sessionFactory.getCurrentSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
return context.proceed();
}
catch(Exception e){
tx.rollback();
throw e;
}
finally{
if(tx.getStatus().equals(TransactionStatus.ACTIVE)){
try{
tx.commit();
}
catch(Exception e){
tx.rollback();
throw e;
}
}
}
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Transactional {
}
EJB methods are transactional by default.
You can tweak their behavior using the TransactionAttribute annotation.
You can read more about CMT here:
https://docs.oracle.com/javaee/7/tutorial/transactions003.htm#BNCIJ
https://docs.oracle.com/javaee/7/tutorial/transactions.htm#BNCIH
Your Typical example would be like--
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
#TransactionManagement(TransactionManagementType.CONTAINER)
#Stateless(..)
public class YourBean{
#TransactionAttribute(TransactionAttributeType.REQUIRED) // if in case you wanted to use 'existing' transaction
public void DoStuff(){
}
}
And in your server configurations you require below tag under <enterprise-beans>
<transaction-type>Container</transaction-type>
I am following this tutorial to create my first Spring Hibernate JSF app, and in this tutorial http://marco-ng.blogspot.com/2014/02/primefaces-jsf2-spring-security-spring.html?showComment=1440293840519#c5483896447188701172 the developer used a UserDAO and CustomerDAO which are one for getting login name and the other to manager customers. For my sample, I'm using one class User merging the two used functions (User will login, and then he will manager totality of Users).
My Question is, can I simply merge those two DAO in one UserDAO :
User DAO :
package spring.dao;
import spring.model.User;
public interface UserDAO {
public User getUser(String login);
}
Custommer DAO :
package spring.dao;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import spring.dao.CustomerDAO.ComponentScan;
import spring.model.Customer;
import java.util.List;
#Repository
public class CustomerDAO {
public #interface ComponentScan {
}
#Autowired
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public void addCustomer(Customer customer) {
getSessionFactory().getCurrentSession().save(customer);
}
public void deleteCustomer(Customer customer) {
getSessionFactory().getCurrentSession().delete(customer);
}
public void updateCustomer(Customer customer) {
getSessionFactory().getCurrentSession().update(customer);
}
public Customer getCustomerById(int id) {
List list = getSessionFactory().getCurrentSession().createQuery("from Customer where id=?").setParameter(0, id).list();
return (Customer)list.get(0);
}
public List<Customer> getCustomers() {
List list = getSessionFactory().getCurrentSession().createQuery("from Customer").list();
return list;
}
}
The goal of all this is about using Management functions as long as Login from same class. And as I'm following a tuto to learn, I can't know the exceptions or things that are impossible to do before asking about them.
Thank you
NO both DAO should remain sepparated.
You can (and should for what you explain) create a single service for all the management, in this service ManagementBS for example you must inject DAO's with #Autowired annotation.
What you can do is a BasicDAO using reflection for basic operation like save, getById, delete... shared between all classes.
The DAO can't be merged because login needs an interface which is has no superclass but an interface. This means both DAO remains but both inherit data from model UserDAO.
Some time ago I started to learn java EE. I swiftly moved to spring(mvc)+hibernate. As I was learning about databases and integration with spring+hibernate I came up with an idea.
As far as I noticed(and understand) there's a common approach to build an objects structure including configuration files, entities, dao interface and dao implementation(as we're talking just about dbs, not controllers and other applications' layers). I decided to write a generic java class and call it BasicDao. It's a template which takes entity as a type.
This is actually working and I think it's much better than interfaces and implementations, because you need only one class for all entities(if you wanted to write separated implementations for each entity you might end up with a big amount of files).
I also wrote some template functions there so the class is very flexible(no exceptions with types passed to the db). Here's the code
package local.bb.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
#Repository(value = "basicDao")
#Transactional(propagation = Propagation.REQUIRED,readOnly = true)
public class BasicDao<ENTITY> {
private Class<ENTITY> data;
private SessionFactory sessionFactory;
public BasicDao() {
this.data = null;
}
#Transactional
public void addRecord(ENTITY t) {
this.sessionFactory.getCurrentSession().save(t);
}
#Transactional
public void removeRecord(ENTITY t) {
this.sessionFactory.getCurrentSession().delete(t);
}
#Transactional
public List<ENTITY> getAllRecords() {
return (List<ENTITY>)this.sessionFactory.getCurrentSession().createCriteria(this.data).list();
}
#Transactional
public <TYPE> ENTITY getRecordByParam(String param, TYPE value) {
return (ENTITY)this.sessionFactory.getCurrentSession().createCriteria(this.data).add(Restrictions.eq(param, value)).uniqueResult();
}
#Transactional
public <TYPE> List<ENTITY> getRecordsByParam(String param, TYPE value) {
return (List<ENTITY>)this.sessionFactory.getCurrentSession().createCriteria(this.data).add(Restrictions.like(param, value)).list();
}
// GETTERS / SETTERS
public SessionFactory getSessionFactory() {
return sessionFactory;
}
#Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Class<ENTITY> getData() {
return data;
}
public void setData(Class<ENTITY> data) {
this.data = data;
}
}
The question, finally, is: is it a good approach actually? Because I've never seen such code anywhere(speaking about tutorials on the Internet and books).
Spring likes interfaces since a couple of important mechanism are based on it, e.g. AOP, interceptors. So, if you decide to go without them you have to accept certain limits to Spring functionality. What's more, it will be harder to write Test-Mocks for other classes that depend on your DAOs.
If you want to save code, I suggest you lose the implementation rather than the interface. With Spring JPA you can annotate a DAO interface with as set of annotations, i.e. #Query, #Procedure, #Modifying etc to define how the data is accessed. If you then enable JPA repositories in your application context, Spring will supply the DAO implementation for you.
More information can be found here.
Yes you can write generic DAO in hibernate and if it's useful and helps you to reduce substantial amount of code and makes your codebase clean then you should go for it. I have used generic DAO but only for simple CRUD operations. Here is a good read on generic persistence layer.
I am not being able to persist entity in JPA, although findAll works
here.
Here is the JpaDAO
package aop.web.teacher.dao;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.apache.log4j.Logger;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public abstract class JpaDAO extends JpaDaoSupport {
protected Class entityClass;
private static Logger log = Logger.getLogger(JpaDAO.class);
#SuppressWarnings("unchecked")
public JpaDAO() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass()
.getGenericSuperclass();
this.entityClass = (Class) genericSuperclass
.getActualTypeArguments()[1];
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void persist(E entity) {
getJpaTemplate().persist(entity);
}
#Transactional
public void remove(E entity) {
getJpaTemplate().remove(entity);
}
#Transactional
public E merge(E entity) {
return getJpaTemplate().merge(entity);
}
#Transactional
public void refresh(E entity) {
getJpaTemplate().refresh(entity);
}
#Transactional
public E findById(K id) {
return getJpaTemplate().find(entityClass, id);
}
#Transactional
public E flush(E entity) {
getJpaTemplate().flush();
return entity;
}
#SuppressWarnings("unchecked")
#Transactional
public List findAll() {
Object res = getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
Query q = em.createQuery("SELECT h FROM "
+ entityClass.getName() + " h");
return q.getResultList();
}
});
return (List) res;
}
#SuppressWarnings("unchecked")
#Transactional
public Integer removeAll() {
return (Integer) getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
Query q = em.createQuery("DELETE FROM " + entityClass.getName()
+ " h");
return q.executeUpdate();
}
});
}
}
Here is the TestDao class
package aop.web.teacher.dao;
import java.util.Date;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import aop.web.teacher.rmodels.Teachermaster;
#Service
#Repository
public class TestDaoImpl extends JpaDAO implements TestDao {
#Autowired
EntityManagerFactory entityManagerFactory;
#PersistenceContext
private EntityManager em;
#PostConstruct
public void init() {
super.setEntityManagerFactory(entityManagerFactory);
}
public int saveTeacher() {
List teacherList = findAll();
Teachermaster m1 = teacherList.get(0);
logger.info("Found " + m1.getId() + " and " + m1.getRace());
m1.setRace(m1.getRace() + "::" + System.currentTimeMillis());
logger.info("New " + m1.getId() + " and " + m1.getRace());
persist(m1);
return 0;
}
}
Here is the spring context xml
http://pastebin.com/pKqzW9h1
Here the findAll works
but when we make a change to the attribute of the Teachermaster
then persist or merge does not seem to save the entity...
If we flush it we get exception
javax.persistence.TransactionRequiredException: no transaction is in progress
Please advise
Spring uses proxy-based AOP, therefore aspects (including transactional aspect) are not applied when methods are called from the same class.
Generally speaking
#Transactional annotations should be usually placed on service methods rather than on DAO methods
Your saveTeacher() looks like a service method, it would be better to place it in the separate service class and annotate as #Transactional
You don't need persist() in saveTeacher() - changes made to persistent objects should be saved automatically
Beware of dynamic proxy vs target class proxy distinction (regarding to TestDao) - see the links below
See also:
7.6.1 Understanding AOP proxies
10.5.1 Understanding the Spring Framework's declarative transaction implementation
You are calling a local method when invoking persist() from your test class. This way the proxy that would create transactions is not invoked so your call to persist() has no transaction.
The way to do this properly is to have the test class not extend the object under test but have it injected. This way the proxy will be triggered and the transaction will be created.
By the way, I must add that I find your class design a little bit beculiar. May I suggest creating a structure like the following?
DAO interface:
public interface FooDao {
void persist(Foo foo);
// ...
}
DAO implementation:
public class FooDaoImpl implements FooDao {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void persist(Foo foo) {
entityManager.persist(foo);
}
}
Test class:
#RunWith(SpringJunit4ClassRunner.class)
#ContextConfiguration(...)
public class FooDaoTest {
#Autowired
private FooDao fooDao;
#Test
public void testPersist() {
// do some testing
}
}
You can, if you wish, extract most of the logic in the DAO implementations into a generic superclass.