Hibernate Transaction Manager - Java configuration - java

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.

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

FinAll in MongoDb using Spring return only one record

the problem is once i use method findAll using MongoOperation interface, it working fine with no exception but only return One Record, however the collection contain more than 1 record. i tried a different interface like mongotemp and also the same result, List size is return always 1, even when i try it from dao itself
Configuration Spring DATA with mongodb
#Configuration
public class MongodbConfig {
public #Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "inSpace");
}
public #Bean MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
DAO Class
#Repository
public class GenericDAOImpl<T> implements GenericDAO<T> {
#Autowired
protected MongoOperations mongoOper;
public <T> void save(T ct) {
mongoOper.save(ct);
}
public <T> void remove(T ct) {
mongoOper.remove(ct);
}
public T getBydId(String id, Class<T> clazz) {
return mongoOper.findById(id, clazz);
}
public List<T> getAll(Class<T> clazz) {
System.err.println(" ''''''' " + mongoOper.findAll(clazz).size());
return mongoOper.findAll(clazz);
}
}
Note: the upper two classs in a single project and used as dependency for the other project who contain the below classes
Service calling DAO
#Service("userService")
public class UserServiceImpl implements UserService {
#Autowired
private GenericDAO<Users> userDAO;
public Users create(Users user) {
userDAO.save(user);
return user;
}
public Users delete(String id) {
Users user = userDAO.getBydId(id, Users.class);
userDAO.remove(user);
return user;
}
public List<Users> findAll() {
System.out.println("inside User Service");
return userDAO.getAll(Users.class);
}
public Users findById(String id) {
return userDAO.getBydId(id , Users.class);
}
}
Controller Class
#RestController
#RequestMapping("/user")
public class UsersController {
#Autowired
UserService userService;
#RequestMapping(method = RequestMethod.GET, value = "/allUsers")
List<Users> getAllRegistedUsers() {
System.out.println("inside UserController");
return userService.findAll();
}
}
Configuration Class
#SpringBootApplication
#ComponentScan(basePackageClasses=
{UsersController.class,UserService.class,GenericDAO.class})
public class UsersConfiguration {
public static void main(String[] args) {
SpringApplication.run(UsersConfiguration.class, args);
}
}
Note: when i try to inject bean in Main methods and use the bean by ApplicationContext it's work fine and return all records in database !?

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

GenericDao, Class<T> is null

I'm implementing GenericDao. I have problem with 2 methods - getAll() and getById(Long id), entity class has null value. It looks like the class is not setted. How Can I solve this problem ?
#Repository
public class GenericDaoImpl<T> implements GenericDao<T> {
private Class<T> clazz;
#Autowired
SessionFactory sessionFactory;
public void setClazz(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
public T getById(final Long id) {
return (T) this.getCurrentSession().get(this.clazz, id);
}
public List<T> getAll() {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
this.clazz);
return criteria.list();
}
protected final Session getCurrentSession() {
return this.sessionFactory.getCurrentSession();
}
}
PersonDao
public interface PersonDao extends GenericDao<Person> { }
PersonDaoImpl
#Repository("PersonDAO")
public class PersonDaoImpl extends GenericDaoImpl<Person> implements PersonDao {}
Service:
#Service
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Transactional
public List<Person> getAll() {
return personDao.getAll();
}
#Transactional
public Person getById(Long id) {
return personDao.getById(id);
}
}
You must set the clazz property of PersonDao. This can be done by declaring a post initialization callback with the #PostConstruct annotation.
#Repository("PersonDAO")
public class PersonDaoImpl extends GenericDaoImpl<Person> implements PersonDao {
#PostConstruct
public void init(){
super.setClazz(Person.class);
}
}

Spring: Could not autowire. No beans of ... type found

I have problems with autowiring in Spring.
I have dao class for AccessLevel object like this:
Interface:
public interface AccessLevelDAO {
public AccessLevel select(Integer id);
}
It's implementation:
#Repository
public class AccessLevelDAOImpl implements AccessLevelDAO {
private SessionFactory sessionFactory;
#Autowired
public void AccessLevelDAO(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private Session currentSession() {
return sessionFactory.getCurrentSession();
}
#Override
#Transactional(propagation = Propagation.SUPPORTS,
isolation = Isolation.READ_UNCOMMITTED)
public AccessLevel select(Integer id) {
return (AccessLevel) currentSession().createCriteria(AccessLevel.class).add(Restrictions.idEq(id)).uniqueResult();
}
}
And service class which using this DAO class:
Interface:
public interface UserServices {
String getUserAccessLevel(String username);
AuthorizationResponseDTO authorize(String username, String password);
}
And implementation:
#Service
public class UserServicesImpl implements UserServices {
#Autowired private AccessLevelDAO accessLevelDAO;
#Autowired private UserDAO userDAO;
#Override
public String getUserAccessLevel(String username) {
User user = userDAO.select(username);
return accessLevelDAO.select(user.getAccessLevel()).getAccessLevel();
}
#Override
public AuthorizationResponseDTO authorize(String username, String password) {
return null;
}
}
When I'm trying to #Autowire accessLevelDAO and userDAO i get error message "Could not autowire. No beans of "AccessLevelDAO" type found".
Spring config includes component scan definition:
<context:annotation-config />
<context:component-scan base-package="com.core"/>

Categories