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
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
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);
}
}
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.
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);
}
}
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.