Generic DAO, Spring, Hibernate - java

I want to understand how can i implement the generic methods like add, edit, delete and search on my database, i have already made the connection (hibernate) and works fine
I do have this method, that works
Class: GenericDAO
public <T> T save(final T o){
Session session=HibernateUtil.getSessionFactory().openSession();
Transaction trans=session.beginTransaction();
Object object = (T) session.save(o);
trans.commit();
return (T) object;
}
and in Main
GenericDAO gen = new GenericDAO();
gen.save(object);
also i have others methods that i dont know how to use them
Class: GenericDAO
public void delete(final Object object){
Session session=HibernateUtil.getSessionFactory().openSession();
Transaction trans=session.beginTransaction();
session.delete(object);
trans.commit();
}
/***/
public <T> T get(final Class<T> type, final int id){
Session session=HibernateUtil.getSessionFactory().openSession();
Transaction trans=session.beginTransaction();
Object object = (T) session.get(type, id);
trans.commit();
return (T) object;
}
public <T> List<T> getAll(final Class<T> type) {
Session session=HibernateUtil.getSessionFactory().openSession();
Transaction trans=session.beginTransaction();
final Criteria crit = session.createCriteria(type);
List<T> list = crit.list();
trans.commit();
return list;
}
Thank you

I think GenericDAO class is base class. It's not for using directly. Did you check this article ? I checked this article and created a sample project.
Don't repeat the DAO!
Example
GitHub - generic-dao-hibernate sample
For example, you might want to create an API to retrieve all employees list according to MySQL first step example.
Employees table schema is like following:
Base SQL
CREATE TABLE employees (
emp_no INT NOT NULL, -- UNSIGNED AUTO_INCREMENT??
birth_date DATE NOT NULL,
first_name VARCHAR(14) NOT NULL,
last_name VARCHAR(16) NOT NULL,
gender ENUM ('M','F') NOT NULL, -- Enumeration of either 'M' or 'F'
hire_date DATE NOT NULL,
PRIMARY KEY (emp_no) -- Index built automatically on primary-key column
-- INDEX (first_name)
-- INDEX (last_name)
);
O/R Mapping
Hibernate require you to configure mapping object-relation settings. After that, you will enjoy converting object-to-sql and sql-to-object.
Entity class based on SQL
#Entity, #Table, #Id, #Column, #GeneratedValue are from Hibernate
#Data, #NoArgsConstructor are from lombok, it reduces getter/setter code
#XmlRootElement, #XmlAccessorType are from jaxb, you might don't need to use it
#Entity
#Data
#NoArgsConstructor
#Table(name = "employees")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class Employees implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "emp_no", unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer empNo;
#Column(name = "birth_date")
private Date birthDate;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "gender")
#Enumerated(EnumType.STRING)
private Gender gender;
#Column(name = "hire_date")
private Date hireDate;
}
Resource Class for Frontend
You always need to write DAO(Data Access Object) for accessing the database. GenericDAO is a method to reduce boilerplate sources codes.
EmployeesResource class
CRUD operations on WEB API
#create, #read, #update or #delete
should be equivalent with
SQL
INSERT, SELECT, UPDATE and DELETE
You need to identify a record or records with key. In this case, id is sample primary key.
#Path("/employee")
public class EmployeesResource {
static Logger log = LoggerFactory.getLogger(EmployeesResource.class);
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Employees> index(#BeanParam Employees paramBean) {
EmployeesDao dao = (EmployeesDao) SpringApplicationContext.getBean("employeesDao");
List<Employees> result = dao.read();
System.out.println("Get all employees: size = " + result.size());
return result;
}
#GET
#Path("{id}")
#Produces(MediaType.APPLICATION_JSON)
public Employees show(#PathParam("id") Integer id) {
EmployeesDao dao = (EmployeesDao) SpringApplicationContext.getBean("employeesDao");
System.out.println("Get employees -> id = " + id);
return dao.read(id);
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Integer create(Employees obj) {
EmployeesDao dao = (EmployeesDao) SpringApplicationContext.getBean("employeesDao");
return dao.create(obj);
}
#PUT
#Path("{id}")
#Consumes(MediaType.APPLICATION_JSON)
public void update(Employees obj, #PathParam("id") String id) {
EmployeesDao dao = (EmployeesDao) SpringApplicationContext.getBean("employeesDao");
dao.update(obj);
}
#DELETE
#Path("{id}")
public void destroy(#PathParam("id") Integer id) throws Exception {
EmployeesDao dao = (EmployeesDao) SpringApplicationContext.getBean("EmployeesDao");
dao.delete(id);
}
}
GenericDao interface & implementation
Interface ( as is from ibm's post )
According to the post, we can declare dao interface. Then we should implement that interface's methods.
public interface GenericDao<T, PK extends Serializable> {
/** Persist the newInstance object into database */
PK create(T newInstance);
/**
* Retrieve an object that was previously persisted to the database using
* the indicated id as primary key
*/
T read(PK id);
List<T> read();
/** Save changes made to a persistent object. */
void update(T transientObject);
/** Remove an object from persistent storage in the database */
void delete(PK id) throws Exception;
void delete(T persistentObject) throws Exception;
}
Implementation
public class GenericDaoHibernateImpl<T, PK extends Serializable> implements GenericDao<T, PK> {
private Class<T> type;
#Autowired
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public GenericDaoHibernateImpl(Class<T> type) {
this.type = type;
}
// Not showing implementations of getSession() and setSessionFactory()
private Session getSession() {
Session session = sessionFactory.getCurrentSession();
return session;
}
#Transactional(readOnly = false, rollbackFor = RuntimeException.class)
public PK create(T o) {
return (PK) getSession().save(o);
}
#Transactional(readOnly = false, rollbackFor = RuntimeException.class)
public void update(T o) {
getSession().update(o);
}
#Transactional(readOnly = true)
public T read(PK id) {
return (T) getSession().get(type, id);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true)
public List<T> read() {
return (List<T>) getSession().createCriteria(type).list();
}
#Transactional(readOnly = false, rollbackFor = RuntimeException.class)
public void delete(PK id) {
T o = getSession().load(type, id);
getSession().delete(o);
}
#Transactional(readOnly = false, rollbackFor = RuntimeException.class)
public void delete(T o) {
getSession().delete(o);
}
If you use only simple CRUD operations in the project, you don't need to append any code for SQL operations. For example, you can create another simple SQL tables like divisions_table or personnel_table with using extends GenericDao<Division, Integer> or extends GenericDao<Personnel, Integer>.
EDIT
To instantiate real dao class related with each table, you need to configure applicationContext.xml and beans.
example
<bean id="employeesDao" parent="abstractDao">
<!-- You need to configure the interface for Dao -->
<property name="proxyInterfaces">
<value>jp.gr.java_conf.hangedman.dao.EmployeesDao</value>
</property>
<property name="target">
<bean parent="abstractDaoTarget">
<constructor-arg>
<value>jp.gr.java_conf.hangedman.models.Employees</value>
</constructor-arg>
</bean>
</property>
</bean>
P.S.
You need to remember this article was written a decade ago. And, you should think seriously about which O/R mapper is really good or not. I think O/R mapper is slightly declining now. Instead of Hibernate, you can find MyBatis , JOOQ

This is one way to implement a hibernate centric generic DAO. It provides basic CRUD operations along with simple search but can be extended to include other generic features.
IGenericDAO interface
public interface IGenericDAO<T extends Serializable> {
T findOne(long id);
List<T> findAll();
void create(T entity);
void update(T entity);
void delete(T entity);
void deleteById(long entityId);
public void setClazz(Class<T> clazzToSet);
}
AbstractTemplateDAO
import java.io.Serializable;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class AbstractHibernateDAO<T extends Serializable> implements IGenericDAO<T> {
private Class<T> clazz;
#Autowired
SessionFactory sessionFactory;
public final void setClazz(Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
#Override
public T findOne(long id) {
return (T) getCurrentSession().get(clazz, id);
}
#Override
public List<T> findAll() {
return getCurrentSession().createQuery("from " + clazz.getName(),clazz).getResultList();
}
#Override
public void create(T entity) {
getCurrentSession().persist(entity);
}
#Override
public void update(T entity) {
getCurrentSession().merge(entity);
}
#Override
public void delete(T entity) {
getCurrentSession().delete(entity);
}
#Override
public void deleteById(long entityId) {
T entity = findOne(entityId);
delete(entity);
}
protected final Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
GenericHiberateDAO
Note: the use of scope prototype here. The spring container creates a new instance of the dao on each request.
#Repository
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class GenericHibernateDAO<T extends Serializable> extends AbstractHibernateDAO<T>
implements IGenericDAO<T> {
//
}
Service class
Shows how to use autowire the generic dao in a service class and pass the model class a parameter. Also, do note that this implementation uses #Transactional annotation for spring transaction management.
#Service
public class TestService implements ITestService {
private IGenericDAO<TestModel> dao;
#Autowired
public void setDao(IGenericDAO<TestModel> daoToSet) {
dao = daoToSet;
dao.setClazz(TestModel.class);
}
#Override
#Transactional
public List<TestModel> findAll() {
return dao.findAll();
}
}
App Config
Shows how to set up spring for automatic transaction management using #EnableTransactionManagement
#Configuration
#ComponentScan("com.base-package")
#EnableTransactionManagement
public class AppConfig {
// add hibernate configuration
// add beans
}

Related

Why the entitymanager does not get autowired in spring boot?

I have an abstract repository class in which I used entitymanager and I want to initialize it with spring boot, but it gives an error that this object is null. What should I do? I used both #PersistenceContext on the entitymanager field and #EnableJpaRepositories on the main class, but the result is the same. What should I do?
#Getter
#NoArgsConstructor
#Transactional
#Repository
#Qualifier("personDAO")
public abstract class AbstractDAO<T> {
public EntityManager em;
#Transient
protected Class clazz;
public AbstractDAO(EntityManager em) {
this.em = em;
}
public void persist(T model) {
em.persist(model);
}
public void delete(T model) {
em.remove(model);
}
public boolean deleteById(int id) {
boolean result= em.createQuery("delete from "+clazz.getSimpleName()+ " o where o.id=" + id).executeUpdate() > 0;
return result;
}
public List<T> findAll() {
return em.createQuery("select o from "+clazz.getSimpleName()+" o").getResultList();}
public T findById(int id) {
return (T) em.find(clazz, id);
}
}
an example of its implementation:
public class SimpleDAOImpl extends SimpleDAO<Person> {
public SimpleDAOImpl(Class<?> clazz) {
this.clazz=Person.class;
}
}
exception:
Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:116)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.NullPointerException: Cannot invoke "jakarta.persistence.EntityManager.persist(Object)" because "this.em" is null
at org.isoft.repo.AbstractDAO.persist(AbstractDAO.java:29)
at org.isoft.App.main(App.java:35)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
... 2 more
If you want to inject some bean in abstract class for its implementation classes, you need to use Autowired annotation on the initialization method instead of a constructor like
#Getter
#NoArgsConstructor
#Transactional
#Repository
#Qualifier("personDAO")
public abstract class AbstractDAO<T> {
public EntityManager em;
#Transient
protected Class clazz;
#Autowired
public init(EntityManager em) {
this.em = em;
}
public void persist(T model) {
em.persist(model);
}
public void delete(T model) {
em.remove(model);
}
public boolean deleteById(int id) {
boolean result= em.createQuery("delete from "+clazz.getSimpleName()+ " o where o.id=" + id).executeUpdate() > 0;
return result;
}
public List<T> findAll() {
return em.createQuery("select o from "+clazz.getSimpleName()+" o").getResultList();}
public T findById(int id) {
return (T) em.find(clazz, id);
}
}
Otherwise, Spring won't inject the bean and it will be null for implementations of this abstraction
Also you can inject this bean in every implementation of this class

Custom Repository using Entity Manager in Spring Boot

I observed that the .save() method executes an extra SELECT query to check whether the corresponding record already exists when the corresponding ID is not a AUTO INCREMENT one.
I tried to implement a repository for this kind of situation that will be extended by many JpaRepository interfaces which will be used across different stateless services and I would like to know if my code is safe - race conditions wise - accross multiple requests as I am not that comfortable using the EntityManager yet.
User Entity :
public class User {
#Id
#Column(name = "username", nullable = false, length = 45)
private String username;
#Column(name = "password", nullable = false, length = 256)
private String password;
}
Solution 1 :
public interface SimpleRepository<T> {
void persist(T entity);
}
public class SimpleRepositoryImpl<T> implements SimpleRepository<T> {
#PersistenceContext
EntityManager entityManager;
#Transactional
#Override
public void persist(T entity) {
entityManager.persist(entity);
}
}
User Repository :
public interface UserRepository extends JpaRepository<User, String>, SimpleRepository<User> {}
User Service :
#Service
#AllArgsConstructor
public class UserService {
private final UserRepository userRepository;
public void createUser(User user) {
this.userRepository.persist(user);
}
}
The same implementation will be followed across many different JPA Repositories and Services in the application.
If the solution above is not safe how about this one?
#Service
public class PersistenceService {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public <T> void persist(T entity) {
entityManager.persist(entity);
}
}
Which will turn my UserService and every other Service that is in need of the same functionality to :
#Service
#AllArgsConstructor
public class UserService {
private final PersistenceService persistenceService;
public void createUser(User user) {
this.persistenceService.persist(user);
}
}

How can I implement the Repository pattern using Generic Types with Java Spring

I am trying to find a way I can implement the repository pattern using spring boot with Generic types. So far I looked into this article:
https://thoughts-on-java.org/implementing-the-repository-pattern-with-jpa-and-hibernate/
and tried implementing this solution using generic types based on the solution to this question:
Using generics and jpa EntityManager methods
I attempted to do so using JPA and Hibernate but for me, an error appears when I try returning the class of the entity on the specified type parameter.
the following is my User model using JPA and Hibernate:
package models;
import javax.persistence.*;
#Entity
public class User extends Model {
#Id
#Column(name = "id", updatable = false, nullable = false)
private String id;
public String username;
private String password;
public User(String username, String password) {
super();
this.username = username;
this.password = password;
}
public String getPassword() {
return password;
}
}
The following is my interface for basic CRUD operations:
package repositories;
import models.Model;
import java.util.UUID;
public interface IRepository<T> {
void add(T entity);
void delete(String id);
void update(T entity);
T get(String id);
boolean exists(String id);
}
I then created an abstract class for all repositories to avoid repeating myself for all Models.
package repositories;
import models.Model;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
public abstract class Repository<T> implements IRepository<T>{
private EntityManager em;
public Repository(EntityManager em){
this.em = em;
}
#Override
public void add(T entity) {
em.persist(entity);
}
#Override
public void delete(String id) {
T entity = get(id);
em.remove(entity);
}
#Override
public void update(T entity) {
em.merge(entity);
}
#Override
public T get(String id) {
return em.find(getEntityClass(), id);
}
public boolean exists(String id) {
return em.contains(get(id));
}
// link to an explanation can be found at:https://stackoverflow.com/questions/40635734/using-generics-and-jpa-entitymanager-methods
// when a class extends this class, all is needed is to fill out the method body of to return the class.
public abstract Class<T> getEntityClass();
}
the abstract class is there for me to return the class that belongs to T
and this is the specific repository for Users:
package repositories;
import models.Model;
import models.User;
import javax.persistence.EntityManager;
public class UserRepository<User> extends Repository<User> {
public UserRepository(EntityManager em) {
super(em);
}
#Override
public Class<User> getEntityClass() {
return null;
}
}
Ideally, for the getEntityClass method, I would like to return User.class, but I get an error on the IDE saying "Cannot select from type variable". I have looked at a few more questions online and another thing people tried was either put a parameter of type Class or have a member of type Class within the User repository. I tried both methods and it didn't work, any ideas?
class UserRepository<User> should just be class UserRepository. Otherwise, User is just like T, a generic type. Not the class User.
But you're reinventing the wheel. Learn and use Spring Data JPA, which brings generic repositories, and more.

Creating BaseDAO for Each Dao class

I created a spring application where i decided to add a BaseDAO to eliminate redundant create,
update,delete,findByid,and findAll methods for every dao. So i created a baseDao and every dao should extend this BaseDAO.
BaseDaoImpl
public class BaseDAOImpl implements BaseDAO{
SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public void create(ModelBase modelBase) {
Session session = this.sessionFactory.getCurrentSession();
session.persist(modelBase);
}
#Override
public void update(ModelBase modelBase) {
Session session = this.sessionFactory.getCurrentSession();
session.update(modelBase);
}
#Override
public Collection findAll(Class aClass) {
Session session = this.sessionFactory.getCurrentSession();
Collection modelCols = session.createQuery("from "+aClass.getSimpleName()).list();
return modelCols;
}
#Override
public ModelBase findById(Class aClass, Integer id) {
Session session = this.sessionFactory.getCurrentSession();
ModelBase modelBase = (ModelBase) session.load(aClass, new Integer(id));
return modelBase;
}
}
Then i extends this Dao to each DAO
EmployeeDAOImp
public class EmployeeDAOImpl extends BaseDAOImpl implements EmployeeDAO{
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
}
I created a BaseService like this. But when i try to access BaseDAO methods from EmployeeDAO it returns null pointer exception.
Why this happen. I dont want to use genericDAO from google. Because we should create DAOs
for each model. I want to eliminate this. So I follow this method.
Have you though about Spring Data project & Spring Data JPA in particular?
This would save you lots of time, since you would no longer need to write your DAO / Repositories from scratch, all you need to do is enable Spring Data JPA, and add needed interfaces. It should save you tons of time.
http://projects.spring.io/spring-data-jpa/
http://spring.io/guides/gs/accessing-data-jpa/ - Sample project
Your are overriding setSessionFactory from base class for no reason, its already available with extending class EmployeeDAOImpl , either remove it or try below:
public class EmployeeDAOImpl extends BaseDAOImpl implements EmployeeDAO{
//this reference should be from base class,
// the extending class ref is hiding base ref.
// private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
super.setSessionFactory(sf);
}
}
Something like the following should work (note the use of constructor rather than setter injection). In the BaseDAO:
public class BaseDAOImpl implements BaseDAO {
private final SessionFactory sessionFactory;
public BaseDAOImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
Then in the Employee DAO:
public class EmployeeDAOImpl extends BaseDAOImpl implements EmployeeDAO {
#Inject
public EmployeeDAOImpl (SessionFactory sessionFactory) {
super(sessionFactory);
}
}
You can create generic dao.
#Repository("genericDao")
public class GenericDaoImpl<T,PK extends Serializable> implements GenericDao<T, PK> {
protected Class<T> entityClass;
public T create(T t) {
this.entityManager.persist(t);
return t;
}
public T read(PK id,Class<T> c) {
return (T)this.entityManager.find(c, id);
}
public T update(T t) {
return this.entityManager.merge(t);
}
public void delete(T t) {
t = this.entityManager.merge(t);
this.entityManager.remove(t);
}
public List<T> getAll(Class<T> c){
return this.entityManager.createQuery("SELECT o FROM "+ c.getName() +" o").getResultList();
}
}
UPDATED
You can use as following, TimeRange is a pojo class in the following example. If you do not want a service layer. You can use timeRangeDao in controller.
#Service("timeRangeService")
public class TimeRangeServiceImpl implements TimeRangeService{
#Autowired
GenericDao<TimeRange,Long> timeRangeDao;
public List<TimeRange> getAllTimeRanges(){
return timeRangeDao.getAll(TimeRange.class);
}
#Transactional
public void createTimeRange(TimeRange c) {
timeRangeDao.create(c);
}
#Transactional
public void update(TimeRange p) {
timeRangeDao.update(p);
}
#Transactional
public TimeRange getTimeRange(long id) {
return timeRangeDao.read(id, TimeRange.class);
}
#Transactional
public void delete(long id) {
TimeRange timeRange = new TimeRange();
timeRange.setId(id);
timeRangeDao.delete(timeRange);
}
}

Hibernate Transaction Manager - Java configuration

I've been beating my head against a wall for awhile now trying to get this to work. I have created the following data access object:
public interface GenericDAO<T, ID extends Serializable> {
T findById(ID id);
List<T> findAll();
T save(T entity);
void update(T entity);
void delete(T entity);
}
public class GenericHibernateDAO<T, ID extends Serializable> implements GenericDAO<T, ID> {
private final Class<T> persistentClass;
private final SessionFactory sessionFactory;
public GenericHibernateDAO(final SessionFactory sessionFactory) {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
public Class<T> getPersistentClass() {
return persistentClass;
}
#Override
public T findById(final ID id) {
return (T) getSession().load(getPersistentClass(), id);
}
#Override #SuppressWarnings("unchecked")
public List<T> findAll() {
return findByCriteria();
}
protected List<T> findByCriteria(final Criterion... criterion) {
final Criteria crit = getSession().createCriteria(getPersistentClass());
for (final Criterion c : criterion) {
crit.add(c);
}
return crit.list();
}
#Override
public T save(final T entity) {
getSession().saveOrUpdate(entity);
return entity;
}
#Override
public void delete(final T entity) {
getSession().delete(entity);
}
#Override
public void update(final T entity) {
getSession().saveOrUpdate(entity);
}
}
#Repository
public class StockHibernateDAO extends GenericHibernateDAO<Stock, String> implements StockDAO {
#Inject
public StockHibernateDAO(final SessionFactory sessionFactory) {
super(sessionFactory);
}
}
I'm attempting to set this up with Java Configuration, so here is my configuration to setup my service layer:
#Configuration #Profile("hibernate")
#EnableCaching #EnableTransactionManagement
#ComponentScan("reference.dao.hibernate")
public class HibernateServiceConfig implements TransactionManagementConfigurer {
#Inject private StockDAO stockDao; //No extra methods, just the base stuff for now
#Bean(destroyMethod = "shutdown")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("classpath:schema.sql").build();
}
#Bean
public SessionFactory sessionFactory() {
return new LocalSessionFactoryBuilder(dataSource()).addAnnotatedClasses(Stock.class)
.setProperty("hibernate.show_sql", "true")
.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory")
.setProperty("hibernate.cache.use_query_cache", "true")
.setProperty("hibernate.cache.use_second_level_cache", "true")
.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect").buildSessionFactory();
}
#Override #Bean
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new HibernateTransactionManager(sessionFactory());
}
}
Here is the TradingService:
#Service
public class TradingServiceImpl implements TradingService {
#Inject private StockDAO stockDAO;
#Override #Transactional
#CachePut(value = "stockCache", key = "#stock.name")
public Stock addNewStock(final Stock stock) {
stockDAO.save(stock);
return stock;
}
#Override #Cacheable(value = "stockCache")
public Stock getStock(final String stockName) {
return stockDAO.findById(stockName);
}
#Override #CacheEvict(value = "stockCache", key = "#stock.name")
public void removeStock(final Stock stock) {
stockDAO.delete(stock);
}
#Override #CacheEvict(value = "stockCache", key = "#stock.name")
public void updateStock(final Stock stock) {
stockDAO.update(stock);
}
#Override
public List<Stock> getAll() {
return stockDAO.findAll();
}
}
The saving of a stock only seems to be completed if I add a session.flush() to the save method. The way I understand things, having the TransactionManager and the #Transactional around the service layer method should in fact cause that call to be made for me. What is this configuration missing?
Because you are injecting a Session
#Bean
public Session session() {
return sessionFactory().openSession();
}
Spring cannot add it's transactional behavior around it. Let Spring open the session and do it's business.
Instead of injecting a Session, inject a SessionFactory. In your DAO, keep a attribute for SessionFactory and use sessionFactory.getCurrentSession() to acquire a session.
When Spring sees the #Transactional, it will get the SessionFactory, call openSession(), begin a transaction on it, then call your method. When your method returns successfully, it will close that transaction.
You should also probably #Autowired the dao in your service class.

Categories