I'm trying to configure spring with hibernate, and I noticed one thing I can not get: when I tryied to get Session object from Container.
Bean.getBean(GenericDao.class).getCurrentSession(); I can only get closed session.
If I will do same with openSession() method - I would receive valid session.
So the question is next: why? I ve been researching in google, but did not find answer. Do anybody knows?
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.List;
#Transactional ( propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class } )
public abstract class GenericDao {
#Autowired
protected SessionFactory sessionFactory;
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
public Session openSession() {
return sessionFactory.openSession();
}
public void saveOrUpdate(final Serializable object) {
getCurrentSession().saveOrUpdate(object);
}
public void delete(final Serializable object) {
getCurrentSession().delete(object);
}
public void save(final Serializable object) {
getCurrentSession().save(object);
}
public void update(final Serializable object) {
getCurrentSession().update(object);
}
public void merge(final Serializable object) {
getCurrentSession().merge(object);
}
#SuppressWarnings ( "unchecked" )
public <T> List<T> list(final Class<T> clazz) {
return getCurrentSession().createCriteria(clazz).list();
}
#SuppressWarnings ( "unchecked" )
public <T> T get(final Class<T> clazz, final Serializable id) {
return (T) getCurrentSession().get(clazz, id);
}
}
#Repository
#Primary
class GenericDaoImpl extends GenericDao {
}
1) This is because, using getCurrentSession() method returns the session bound to the context. But for this to work, we need to configure it in hibernate configuration file as below.
<property name="hibernate.current_session_context_class">thread</property>
thread context means current thread. The default value is jta context means an already existing jta transaction.
Since this session object (fetched using getCurrentSession()) belongs to the hibernate context, we don’t need to close it. Once the SessionFactory is closed, this session object gets closed.
The getCurrentSession() should be used for single threaded environment.
2) openSession() method always opens a new session. We should close this session object once we are done with all the database operations.
We should open a new session for each request in multi-threaded environment.
For more details related to thread safety and Hibernate Session & their usage, please see this.
Hibernate uses a the notion of current context class to determine the scope of the Session to use with getCurrentSession(). In pure Hibernate this can be thread whereby the Session is bound to the thread , jta where the session is bound to a JTA UserTransaction
When used with Spring you dont need to specify the hibernate.current_session_context_class property as Spring uses its own called SpringSessionContext. This binds the session to the ongoing transaction using whatever PlatformTransactionManager you are using
To use SessionFactory.getCurrentSession() make sure your call is inside a #Transactional method otherwise it wont work and you have to open the session manually using openSession()
#Transactional takes responsibility for opening and closing sessionFactory, you can use getCurrentSession without openSession as:
#SuppressWarnings ( "unchecked" )
public <T> List<T> list(final Class<T> clazz) {
return getCurrentSession().createCriteria(clazz).list();
}
if you configured transaction with #Annotation or in .xml file as here:
<bean id="sessionFactory" class="..."</bean>
<tx:annotation-driven proxy-target-class="true" transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
and with #Transational and #Autowired as you do, it is ok
If you don't configure transaction, you can use openSession() only, and add closeSession when you want to finish session
Related
I am trying to understand why, after creating a new entity and persisting it whilst the application is running, when retrieving a list of these entities, the new entity should be retrieved from the database, but isn't?
For example:
I created a new entity (from the UI) and persisted it successfully like so:
#Repository
public class BaseDAOHibernate {
Session session;
public BaseDAOHibernate() {
session = HibernateUtils.getSessionFactory().openSession();
}
public void save(Object object) {
Transaction tx = session.beginTransaction();
session.save(object);
tx.commit();
}
...
I verified that the entity is persisted in the database.
Next, when I refresh the UI which lists these entities (for which I added a new one to the database) the new entity is not included and was not retrieved from the following:
#Repository
#SuppressWarnings("unchecked")
public class PasswordDAOHibernate extends BaseDAOHibernate implements PasswordDAO {
#Override
public Collection<Password> getPasswords() {
Query query = session.createQuery("select ...");
return query.list();
}
Here is the interface:
public interface PasswordDAO {
Password getPassword(Integer id);
Collection<Password> getPasswords();
Collection<Password> getPasswords(PasswordSearchParameters params);
Collection<PasswordType> getPasswordTypes();
}
...
Which is called from a controller:
#Controller
public class PasswordsController extends BaseControllerHelper {
#Autowired
private PasswordDAOHibernate passwordDAO;
#RequestMapping("/passwords.htm")
public void passwords(Map model,
HttpServletRequest request) {
Collection<Password> passwords = passwordDAO.getPasswords();
model.put("passwords", passwords);
}
Here is the configuration currently setup:
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.connection.url">jdbc:mysql://host:3306/server</property>
<property name="hibernate.connection.username">username</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<mapping class="com.crm.entity.User"></mapping>
<mapping class="com.crm.entity.Role"></mapping>
<mapping class="com.crm.entity.Property"></mapping>
<mapping class="com.crm.entity.Menu"></mapping>
<mapping class="com.crm.entity.Password"></mapping>
<mapping class="com.crm.entity.PasswordType"></mapping>
</session-factory>
</hibernate-configuration>
HibernateUtils.java
#Component
public class HibernateUtils {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
getSessionFactory().close();
}
}
When I restart the server application the new entity displays in the list.
Is there some kind of implicit caching going on that I need to clear? Note, I have not implemented any explicit caching at this stage.
The problem is your dao, which is flawed in a very dangerous way. A Session isn't thread safe and shouldn't be shared. It should be opened for each action (or at least the current session should be used).
Plain Hibernate Solution
You DAO should look something like this.
private final SessionFactory sessionFactory;
protected BaseDAOHibernate(SessionFactory sessionFactory) {
this.sessionFactory=sessionFactory;
}
protected Session getSession() {
return this.sessionFactory.getCurrentSession();
}
public void save(Object object) {
getCurrentSession().save(object);
}
Now you specific dao should reuse the getSession method.
#Repository
#Transactional
public class PasswordDAOHibernate extends BaseDao implements PasswordDao {
#Autowired
public PasswordDAOHibernate(SessionFactory sessionFactory) {
super(sessionFactory);
}
#Override
public Collection<Password> getPasswords() {
return getSession.query("select ...", Password.class).list();
}
When doing so you probably will run into issues with an error stating that no session can be found due to no transaction (or something along those lines).
To fix this use Spring Boot (and some manual configuration).
First move the hibernate.connection properties to your application.properties and remove them from hibernate.cfg.xml.
spring.datasource.url=jdbc:mysql://host:3306/server
spring.datasource.username=username
spring.datasource.password
Now Spring will create a Datasource for you. Next remove your HibernateUtils and configure the SessionFactory using Springs LocalSessionFactoryBean.
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setDataSource(dataSource);
return factory;
}
You will also need the appropriate transaction manager
#Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
Now due to everything being setup in Spring, the injection of the SessionFactory and the #Transactional you will get a managed Session which will be properly opened and closed for you.
Controller Fix
Your controller is also flawed as you should be injecting a PasswordDao and not the concrete type (which would now fail due the the creation of a proxy for the transactions).
#Controller
public class PasswordsController extends BaseControllerHelper {
#Autowired
private PasswordDAO passwordDAO;
JPA Solution
However while all of this will probably work I would strongly suggest to use JPA and the EntityManager instead of the Session and SessionFactory approach.
To do so remove the LocalSessionFactoryBean and the HibernateTransactionManager and add the remaining properties of the hibernate.cfg.xml to the application.properties.
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=true
Next to the spring.datasource properties that is all you need and you can delete the hibernate.cfg.xml file.
Now instead of using the SessionFactory use the EntityManager in your dao.
public abstract class BaseDao {
#PersistenceContext
protected EntityManager em;
public void save(Object o) {
em.persist(o);
}
}
And your specific dao.
#Repository
#Transactional
public PasswordJpaDao extends BaseDao implements PasswordDao {
#Override
public Collection<Password> getPasswords() {
return em.createQuery("select ...", Password.class).getResultList();
}
Spring Data JPA Solution
When using JPA you could even drop your generic dao approach and implementations and use Spring Data JPA instead. Your whole PasswordDao would then look like
public interface PasswordDao extends JpaRepository<Password, Long> {}
All the crud functionality (findAll, findOne, save etc.) is available out-of-the-box. Creating queries is pretty easy and saves you from writing the boilerplate code.
You can use #Cacheable(false) on your entity
or disable the shared cache altogether for all your entities by adding this to your persistence.xml
<shared-cache-mode>NONE</shared-cache-mode>
For Hibernate you can also use
session.setCacheMode(CacheMode.IGNORE);
see this link for more information about the different cache levels in JPA
First of all I am a beginner in spring.I have created a simple Spring service that has DAO injected and transaction is managed by HibernateTransactionManager of spring, like as below.(And transaction configuration is used using annotations )
#Service(value="daopowered")
public class UserServiceImplDao implements UserService
{
#Inject
private UserDao userDao;
#Override
#Transactional
public User autheticate( String userId, String password )
{
return userDao.findByIdAndPassword(userId, password);
}
My transaction configuration is following
<bean id="txMgr"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txMgr" />
Now the problem is when I call authenticate method first time using some controller then it works fine ( does DB operations successfully) but after calling it again second time hibernate session is closed exception is coming ? Please guide me what I am doing it wrong or how to handle this scenario ? Why wont spring opens a new transaction when I call this method second time ?
Exception Trace:
2013-05-22T21:04:18.041+0530 DEBUG [14208212-2] lerExceptionResolver Resolving exception from handler [com.akhi.store.controller.HomeController#5d9d277e]: org.springframework.orm.hibernate3.HibernateSystemException: Session is closed!; nested exception is org.hibernate.SessionException: Session is closed!
2013-05-22T21:04:18.044+0530 DEBUG [14208212-2] tusExceptionResolver Resolving exception from handler [com.akhi.store.controller.HomeController#5d9d277e]: org.springframework.orm.hibernate3.HibernateSystemException: Session is closed!; nested exception is org.hibernate.SessionException: Session is closed!
2013-05-22T21:04:18.044+0530 DEBUG [14208212-2] lerExceptionResolver Resolving exception from handler [com.akhi.store.controller.HomeController#5d9d277e]: org.springframework.orm.hibernate3.HibernateSystemException: Session is closed!; nested exception is org.hibernate.SessionException: Session is closed!
2013-05-22T21:04:18.046+0530 DEBUG [14208212-2] et.DispatcherServlet Could not complete request
org.springframework.orm.hibernate3.HibernateSystemException: Session is closed!; nested exception is org.hibernate.SessionException: Session is closed!
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:658)
at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.convertHibernateAccessException(AbstractSessionFactoryBean.java:245)
at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.translateExceptionIfPossible(AbstractSessionFactoryBean.java:224)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy28.findByIdAndPassword(Unknown Source)
EDIT: The DAO code
#Repository
public class UserDaoImpl extends GenericHibernateDAO<User, Long>
implements
UserDao
{
#Override
public User findByIdAndPassword( String id, String password )
{
Criteria crit = getSession().createCriteria(User.class).add(Restrictions.eq("userId",
id)).add(Restrictions.eq("password",
password)).setMaxResults(1);
List<?> list = crit.list();
if (list.size() > 0)
return (User) list.get(0);
else
return null;
}
and getSession() implementation is
protected Session getSession() {
if (session == null) {
session = sessionFactory.getCurrentSession();
}
return session;
}
Also the abstract DAO class has sessionfactory injected
public abstract class GenericHibernateDAO<T, ID extends Serializable>
implements GenericDAO<T, Long> {
private Class<T> persistentClass;
protected Session session;
#Autowired
private SessionFactory sessionFactory;
Your ObjectDao need a SessionFactory and the annotation Transaction. Something like this :
#Component
public class userDao{
#AutoWired
private SessionFactory sessionFactory;
#Transactional
public User findByIdAndPassword(String id , String password){
....
}
{getters and setters}
}
Dont do that :
protected Session getSession() {
if (session == null) {
session = sessionFactory.getCurrentSession();
}
return session;
}
just return the current session , the Transactional annotation is responsible for opening and closing sessions , like this :
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
I am new to Spring and I am working on CAS. I need to query a database for user authentication but I am using a servlet as a controller. Therefore I need to know if there is any way to set up a SimpleJdbcTemplate in that servlet and use it to query the data base. If there is how to configure the web.xml file or any other file.
Thank you already.
It is not a good Idea to inject or access JdbcTemplate directly into a Servlet or a Controller.
You can have a DAO layer in between and inject your JdbcTemplate in you DAO would be a better approach.
In order to use a JdbcTemplate you need to have a DataSource defined somewhere in your configuration (Spring context either through xml or Annotations).
If you have a UserDao, then your spring configuration would be as follows
<bean class="com.xxx.dao.UserDAOImpl" id="userDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
and here you need to difine your "dataSource" there are multiple ways to configure it, You may get better help from google.
And now, your UserDaoImpl looks a like
public class UserDAOImpl implements UserDAO {
private JdbcTemplate jdbcTemplate;
//setter and getter for jdbcTemplate
public List<Map<String, Object>> getUsers() {
String query = "select * from user";
return jdbcTemplate.queryForList(query, new HashMap<String, String>());
}
}
In your Servlet, you need to get the reference of this Dao using a ServiceLocator
in servlet class
...
public UserDAO getUserDao() {
return ServiceLocator.getBean(UserDAO.class);
}
...
Again there are multiple ways to design the ServiceLocator, Here is the simple implementation.
public class ServiceLocator implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* #return Returns the applicationContext.
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static <T> T getBean(Class<T> requiredType) throws BeansException {
return getApplicationContext().getBean(requiredType);
}
/**
* #param applicationContext The applicationContext to set.
*/
public void setApplicationContext(ApplicationContext applicationContext) {
ServiceLocator.applicationContext = applicationContext;
}
}
Finally, all these pieces are independent, You need to read upon individually, You will get much and precise help on google or Spring forums.
I used this annotation successfully for a Dao class. And rollback works for tests.
But now I need to rollback real code, not just tests.
There are special annotations for use in tests. But which annotations are for non-test code?
It is a big question for me. I spent a day for that already. The official documentation did not meet my needs.
class MyClass { // this does not make rollback! And record appears in DB.
EmployeeDaoInterface employeeDao;
public MyClass() {
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "HibernateDaoBeans.xml" });
employeeDao = (IEmployeeDao) context.getBean("employeeDao");
}
#Transactional(rollbackFor={Exception.class})
public void doInsert( Employee newEmp ) throws Exception {
employeeDao.insertEmployee(newEmp);
throw new RuntimeException();
}
}
employeeDao is
#Transactional
public class EmployeeDao implements IEmployeeDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public void insertEmployee(Employee emp) {
sessionFactory.getCurrentSession().save(emp);
}
}
And here is a test for which the annotations work well:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/HibernateDaoBeans.xml" })
#TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
#Transactional
public class EmployeeDaoTest {
#Autowired
EmployeeDaoInterface empDao;
#Test
public void insert_record() {
...
assertTrue(empDao.insertEmployee(newEmp));
}
HibernateDaoBeans.xml
...
<bean id="employeeDao" class="Hibernate.EmployeeDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
...
**YES, I rolled back the transaction. I just added BEAN for the service... and then annotation #Transactional begin to work :-) **
<bean id="service" class="main.MyClass">
<property name="employeeDao" ref="employeeDao" />
</bean>
Thanks all, Russia will not forget you!
Just throw any RuntimeException from a method marked as #Transactional.
By default all RuntimeExceptions rollback transaction whereas checked exceptions don't. This is an EJB legacy. You can configure this by using rollbackFor() and noRollbackFor() annotation parameters:
#Transactional(rollbackFor=Exception.class)
This will rollback transaction after throwing any exception.
or programatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
You can throw an unchecked exception from the method which you wish to roll back. This will be detected by spring and your transaction will be marked as rollback only.
I'm assuming you're using Spring here. And I assume the annotations you refer to in your tests are the spring test based annotations.
The recommended way to indicate to the Spring Framework's transaction infrastructure that a transaction's work is to be rolled back is to throw an Exception from code that is currently executing in the context of a transaction.
and note that:
please note that the Spring Framework's transaction infrastructure code will, by default, only mark a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException.
For me rollbackFor was not enough, so I had to put this and it works as expected:
#Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
I hope it helps :-)
Is there ever a case in a standard webapp where one would pass an EntityManager or Session as a parameter to a DAO call, i.e. findPersonByName(String name, Session session)? Or should the the opening and closing of the session be abstracted in the implementation?
A better approach would be to initialize or otherwise inject the DAO with the SessionFactory. Then you can do things like this:
public abstract class AbstractHibernateDao<T extends Object>
implements AbstractDao<T> {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
public void save(T t) { getSession().save(t); }
public void update(T t) { getSession().update(t); }
...
}
without having to pass Sessions all over the place.
No, it should never be used, but a Service layer might. Imagine you have two different methods (in possibly different DAO's) that need to be encapsulated in the same transaction (commit/rollback), than you might want/have to use the same connection object.