I know hibernate well, and fairly Spring core, I am able to use hibernate template and hibernate dao support class to integrate both but as from hibernate 3.1 we have contextual sessions i.e. we need not to use hibernate template and hibernate dao support but when i am trying to integrate with this concept and inject the sessionFactory in my dao class everything goes write but i am trying to insert data hibernate shows the insert query but the data is not saved to database please help me what can i do.
Here is my code
#Transactional
public class UserDAO {
SessionFactory sessionFactory;
public void save(User transientInstance) {
try {
sessionFactory.openSession().save(transientInstance);
} catch (RuntimeException re) {
throw re;
}
}
public static UserDAO getFromApplicationContext(ApplicationContext ctx) {
return (UserDAO) ctx.getBean("UserDAO");
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
This is my beans.xml
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>
<bean id="UserDAO" class="dao.UserDAO">
<property name="sessionFactory"><ref bean="sessionFactory" /></property>
</bean>
This is my main code
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
User user=new User("firstName", "lastName", "address", "phoneNo", "mobileNo", "email", "password", false, null);
SessionFactory factory=(SessionFactory)context.getBean("sessionFactory");
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
UserDAO ud=(UserDAO)context.getBean("UserDAO");
ud.save(user);
tx.commit();
session.close();
There are several problems with your code,
The DAO is already transactional, managed by Spring. You are starting another transaction in main.
You are opening session twice. Once in main and another time in dao.
When you are using contextual session, it is best practice to call sessionFactory.getCurrentSession()
Now if you have configured transaction manager with Spring properly, the following code will work, I just added some comments, see if it's helpful to you :)
#Transactional
public class UserDAO {
SessionFactory sessionFactory;
public void save(User transientInstance) {
//Removed try-catch. It was just catching RuntimeException only to re throw it
sessionFactory.openSession().save(transientInstance);
}
//I would get rid of this method.
public static UserDAO getFromApplicationContext(ApplicationContext ctx) {
return (UserDAO) ctx.getBean("UserDAO");
}
//I would get rid of this method or make it private
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
Main code
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//I would use builder instead of constructor here
User user=new User("firstName", "lastName", "address", "phoneNo", "mobileNo", "email", "password", false, null);
UserDAO ud=(UserDAO)context.getBean("UserDAO");
ud.save(user);
Related
I'm new to Spring and I did a login/register applicaton following a youtube tutorial but I want to add a new functionality that allows to delete a student. I used #Transactional on my delete method and modified accordingly the xml file but I get this error:
Message Request processing failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'platformTransactionManager' is expected to be of type 'org.springframework.transaction.PlatformTransactionManager' but was actually of type 'com.infotech.service.impl.StudentServiceImpl'
my Service class
#Service("studentService")
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentDAO studentDAO;
public void setStudentDAO(StudentDAO studentDAO) {
this.studentDAO = studentDAO;
}
public StudentDAO getStudentDAO() {
return studentDAO;
}
//other methods
#Override
public void delete(String email) {
getStudentDAO().delete(email);
}
}
my DAO class
#EnableTransactionManagement
#Repository("studentDAO")
public class StudentDAOImpl implements StudentDAO {
#Autowired
private HibernateTemplate hibernateTemplate;
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
#Autowired
private SessionFactory sessionFactory;
protected Session getSession() {
return (Session) sessionFactory.getCurrentSession();
}
//other methods
#Transactional("platformTransactionManager")
public void delete(String email) {
Student student = (Student) ((HibernateTemplate) getSession()).get(Student.class, email);
((HibernateTemplate) getSession()).delete(student);
}
}
In the dispatcher servlet I have defined InternalResourceViewResolver, dataSource, hibernateTemplate, sessionFactory beans and then I added another bean
<tx:annotation-driven transaction-manager="platformTransactionManager"/>
<bean id= "platformTransactionManager"class="com.infotech.service.impl.StudentServiceImpl">
</bean>
Finally, this is the controller
#Controller
public class MyController {
#Autowired
private StudentService studentService;
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
public StudentService getStudentService() {
return studentService;
}
//...RequestMappings...
#RequestMapping(value = "/delete/{email}", method = RequestMethod.GET)
public ModelAndView delete(#PathVariable("email") String email) {
studentService.delete(email);
return new ModelAndView("redirect:/view/home");
}
...
}
Now, how can I make my bean of PlatformTransactionManager type?
But most of all I think there's a simpler way to delete a field from my table, maybe without using #Transaction at all so can anyone help me understand why I get the error and explain me what is #Transactional and if I really should use it in this case?
Remember that I'm NEW to Spring, I still don't have many notions so sorry if I wrote something totally stupid :-)
Spring is looking for transaction manager - it requires a concrete implementation of the PlatformTransactionManager interface. It's being given your service implementation, which isn't a PlatformTransactionManager and not what it needs. If you're using JDBC, org.springframework.jdbc.datasource.DataSourceTransactionManager should work.
Try changing:
<bean id= "platformTransactionManager" class="com.infotech.service.impl.StudentServiceImpl">
To:
<bean id= "platformTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
I've been stuck for a week with this problem and there is no way to find a solution in my project using Spring tool suite.
My dispatcher scan packages and creates beans for the controller and service layer but it seems it can't reach the model layer.
This is my servlet-context.xml:
<beans:bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- <beans:property name="dataSource" ref="dataSource"/> -->
<beans:property name="configLocation" value="classpath:hibernate-annotation.cfg.xml" />
</beans:bean>
<tx:annotation-driven/>
<!-- <beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<beans:property name="sessionFactory" ref="sessionFactory" />
</beans:bean> -->
I have commented the transactionManager bean because it throws an expection.
The problem comes up when in my UserServiceImpl set UserDaoI as #Autowired
#Service
public class UserServiceImpl implements UserServiceI{
private static final Logger logger =
Logger.getLogger(UserServiceImpl.class);
#Autowired
UserDaoI userDao;
public boolean isRegisteredUser(User user){
logger.debug("Entrando en isRegisteredUser" + logger.getClass());
boolean isRegistered = false;
UserDao uDao = userDao.getUserByDni(user.getDni());
if(!(uDao.getEmail().equals(user.getEmail()))){
isRegistered = true;
}
return isRegistered;
}
I am clueless and desperate.
EDIT:
This is my UserDaoI class, but I think is not relevant.
public interface UserDaoI {
void addUser(UserDao userDao);
UserDao getUser(int id);
boolean updateUser(UserDao userDao);
boolean deleteUser(int id);
List<UserDao> getAllUsers();
UserDao getUserByDni(String dni);
}
This is my UserDaoImpl.java, the source of the problems.
#Repository
public class UserDaoImpl implements UserDaoI{
private static final Logger logger = LoggerFactory.getLogger(UserDaoImpl.class);
#Autowired
SessionFactory sessionFactory;
Session session;
#Override
public void addUser(UserDao userDao) {
session = sessionFactory.openSession();
session.beginTransaction();
session.save(userDao);
session.getTransaction().commit();
session.close();
}
#Override
public UserDao getUser(int id) {
//sessionFactory = HibernateUtil.getSessionFactory();
session = sessionFactory.openSession();
session.beginTransaction();
UserDao userDao = (UserDao) session.get(UserDao.class, id);
session.getTransaction().commit();
session.close();
return userDao;
}
#Override
public boolean updateUser(UserDao userDao) {
assert(userDao!=null);
//sessionFactory = HibernateUtil.getSessionFactory();
session = sessionFactory.openSession();
session.beginTransaction();
session.update(userDao);
session.getTransaction().commit();
session.close();
return false;
}
#Override
public boolean deleteUser(int id) {
//sessionFactory = HibernateUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
UserDao userDao = new UserDao();
userDao.setId(id);
if(getUser(id)!=null){
try{
session.beginTransaction();
session.delete(userDao);
session.getTransaction().commit();
return true;
}catch(Exception ex){
logger.error("No se ha podido borrar el usuario");
}finally{
session.close();
}
}
return false;
}
#Override
public List<UserDao> getAllUsers() {
//sessionFactory = HibernateUtil.getSessionFactory();
session = sessionFactory.openSession();
session.beginTransaction();
#SuppressWarnings("unchecked")ArrayList<UserDao> userDaoList = (ArrayList<UserDao>) session.createQuery("from user").list();
session.getTransaction().commit();
session.close();
return userDaoList;
}
#Override
public UserDao getUserByDni(String dni) {
session = sessionFactory.getCurrentSession();
session.beginTransaction();
UserDao userDao = (UserDao) session.createQuery("from user where dni = " + dni).uniqueResult();
session.getTransaction().commit();
session.close();
return userDao;
}
}
Why I do not need to autowire my Session?
This is my hibernate config
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/gen</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">fiw48asi</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<mapping class="com.library.app.dao.user.UserDao"/>
</session-factory>
Now the exception I get is
org.hibernate.hql.internal.ast.QuerySyntaxException: user is not mapped
But it is declared in hibernate-annotation.cfg.xml
Thanks a ton!
The problem is:
'userDaoImpl': Injection of autowired dependencies failed...: Could not autowire field:
because
No qualifying bean of type [org.hibernate.SessionFactory] found for dependency: expected at least 1 bean which qualifies as autowire candidate
In your model, you will need to define a userDaoImpl bean with an #Bean annotation and a Hibernate SessionFactory to allow Spring to create and inject it, e.g.,: add a method to your UserServiceImpl class like this:
UserServiceImpl.java
#Bean
public UserDaoI userDao() {
return new UserDaoImpl();
}
But I notice some mixing of types and models, however, which I think is at the root of your issues, so I'll explain how the service with DAO model works generally with a simpler model.
In a "service and data access object" model, you create a persistence mediator for each entity, conventionally with a DAO suffix and the entity is typically a simple plain ordinary Java object (POJO).
For example, to represent an in-memory user, you might model the entity as follows:
User.java
public class User {
private String name;
// more POJO properties as need..
public void setName(String name) {
this.name = name;
}
public String getName(String name) {
return this.name;
}
// more POJO setters and getters...
}
Now we need a mediator which can create, read, update, and delete these entities from our database. In a DAO pattern, that's the role of the entity's DAO:
UserDao.java
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class UserDao {
#Autowired
private SessionFactory sessionFactory;
public User getUser(long id) {
sessionFactory.getCurrentSession().get(User.class, id);
}
public void addUser(User user) {
sessionFactory.getCurrentSession().saveOrUpdate(user);
}
//...
}
We add a service layer on top of the DAO layer when services either span entities or add some (likely to change) business logic to the entity. For example, supposed we have a registration table which captures registration records in various states for each user:
UserService.java
public class UserService {
#Autowired
private UserDao userDao
#Autowired
private RegistrationDao registrationDao
public boolean isRegistered(long userId) {
// DAOs mediate access to tables
User user = userDao.getUser(userId);
Registration reg = registrationDao.getRegistration();
//
// business logic
//
return isUserRegistered(user, registration);
}
}
With Spring 4, though, I don't hand-code my DAO layers anymore; instead I use Spring Data JPA with the Hibernate JPA provider. Here's a very simple example of how to make this work. You're already using the Hibernate ORM, so you don't "lose" much (quite the opposite, really) by using Spring Data JPA with the Hibernate JPA provider.
I am trying to get access to the Hibernate session factory but am getting the following error at the line mentioned.
No CurrentSessionContext configured!
code
#Service
#Transactional
public class GenericSearchImpl implements GenericSearch {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Override
#SuppressWarnings("unchecked")
public <T> List<T> search(final Class<T> type, final String[] criteriaList, final int page, final int perPage) {
Session session = getSession();
...
}
public Session getSession() {
final HibernateEntityManagerFactory emFactory = (HibernateEntityManagerFactory) entityManagerFactory;
final SessionFactory sessionFactory = emFactory.getSessionFactory();
return sessionFactory.getCurrentSession(); //ERROR No CurrentSessionContext configured!
//This worked but I understand it to be BAD as spring should be managing open sessions.
// try {
// return sessionFactory.getCurrentSession();
// } catch (Exception e) {
// return sessionFactory.openSession();
// }
}
...
}
Any idea why?
In property file,
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
in configuration class
#Bean
public HibernateJpaSessionFactoryBean sessionFactory() {
return new HibernateJpaSessionFactoryBean();
}
Then you can autowire
#Autowired
private SessionFactory sessionFactory;
We do this as Spring boot doesn't auto configure hibernate sessinoFactory.
Update: As of Spring 4.3.12 and Hibernate 5.2, above Hibernate API solution is depreciated in favor of generic JPA API solution EntityManagerFactory.
Session session = entityManager.unwrap(Session.class);
Here is some detailed example doc with examples on EntityManagerFactory.
You can access SessionFactory with entityManagerFactory unwrap method instead of HibernateJpaSessionFactoryBean
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
HibernateJpaSessionFactoryBean is deprecated in Spring Boot 1.5.8
Looking at the below two classes and AOP configuration, I'm uncertain about whether this is right at all? I've configured a pointcut on the PartnerService, but only use the session in PartnerDao. Will this safely begin a new session (and transaction) which I can use in PartnerDao?
These are my classes
PartnerService.java:
public class PartnerService {
private PartnerDao dao;
public void setDao(PartnerDao dao) {
this.dao = dao;
}
PartnerDao getDao() {
return dao;
}
public List<Partner> getPartners() {
return getDao().getPartners();
}
public void createPartner(Partner partner) {
getDao().createPartner(partner);
}
}
PartnerDao.java
public class PartnerDao {
private HibernateTemplate template;
public void setSessionFactory(SessionFactory sessionFactory) {
this.template = new HibernateTemplate(sessionFactory);
}
HibernateTemplate getTemplate() {
return template;
}
public List<Partner> getPartners() {
return getTemplate().execute(new HibernateCallback<List<Partner>>() {
#Override
public List<Partner> doInHibernate(Session s) throws HibernateException {
Criteria c = s.createCriteria(Partner.class);
return c.list();
}
});
}
public void createPartner(Partner partner) {
getTemplate().save(partner);
}
}
Now, I'd like to use AOP to manage transactions. I've got the following AOP configuration in applicationContext.xml:
<aop:config>
<aop:pointcut id="serviceMethods" expression="within(com.company.pas.service.*)"/>
<aop:advisor advice-ref="companyTransactionAdvise" pointcut-ref="serviceMethods"/>
</aop:config>
If you look at my AOP configuration, I'm configuring a pointcut for the PartnerService (which is located in com.company.pas.service). However, I'm only dealing with the session in PartnerDao. The way I'm instantiating these classes is that I've got a ServiceFactory which returns a PartnerService with an autowired instance of PartnerDao.
Why not using Spring AOP Transactional Management? Using annotations it will be as easy as annotating your Service methods with #Transactional.
The heart of this question is: Is it possible to execute a Transaction from a method triggered by a Spring shutdown hook?
At the moment I have a HyperSqlDbServer class that implements SmartLifeCycle as found in this question:
In a spring bean is it possible to have a shutdown method which can use transactions?
I have a method in that class that is marked transactional that gets invoked as part of the stop method:
#Transactional
public void executeShutdown() {
hsqlDBShutdownService.executeShutdownQuery();
hsqlDBShutdownService.closeEntityManager();
}
The service used in that method is a bit of a hack that I had to do because I could not autowire in the EntityManager to this class:
#Service
public class HsqlDBShutdownService {
#PersistenceContext
private EntityManager entityManager;
#Autowired
private HyperSqlDbServer hyperSqlDbServer;
#Transactional
public void executeShutdownQuery() {
entityManager.createNativeQuery("SHUTDOWN").executeUpdate();
}
#Transactional
public void closeEntityManager() {
entityManager.close();
}
#PostConstruct
public void setHsqlDBShutdownService() {
hyperSqlDbServer.setShutdownService(this);
}
}
You may notice that all I'm really trying to accomplish is invoking the query "SHUTDOWN" before stopping the server. Without this, the hsqldb lock file sticks around on server restart, and the server throws an exception.
The code above produces the following exception:
javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:96)
...
So my original question stands, but if anyone has a thought on how I could execute this query another way I'll try that as well.
FYI, I've also tried the #PreDestroy annotation, but get the same TransactionRequiredException.
Edit: For completeness, I am using the JpaTransactionManager and the #Transactional annotations work throughout my project, except on shutdown...
Edit 2: Datasource and transaction manager configuration:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:persistence.properties")
public class PersistenceConfig implements TransactionManagementConfigurer {
private static final String PASSWORD_PROPERTY = "dataSource.password";
private static final String USERNAME_PROPERTY = "dataSource.username";
private static final String URL_PROPERTY = "dataSource.url";
private static final String DRIVER_CLASS_NAME_PROPERTY = "dataSource.driverClassName";
#Autowired
private Environment env;
#Bean
#DependsOn("hsqlDb")
public DataSource configureDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty(DRIVER_CLASS_NAME_PROPERTY));
dataSource.setUrl(env.getProperty(URL_PROPERTY));
dataSource.setUsername(env.getProperty(USERNAME_PROPERTY));
dataSource.setPassword(env.getProperty(PASSWORD_PROPERTY));
return dataSource;
}
#Bean
#DependsOn("hsqlDb")
public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("com.mycompany.model.db");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, env.getProperty(org.hibernate.cfg.Environment.DIALECT));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_AUTO));
jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, env.getProperty(org.hibernate.cfg.Environment.SHOW_SQL));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR));
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Override
#Bean()
#DependsOn("hsqlDb")
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new JpaTransactionManager();
}
}
I found a workaround for shutting down the HsqlDB database, but it involves avoiding the use of Spring's EntityManager and #Transactional as they apparently do not work during server shutdown. My modified HsqlDBShutdownService is below. The key change is that instead of using the EntityManager to invoke the query, I create a new jdbc connection manually, and invoke the query that way. This avoids the requirement for #Transactional:
#Service
public class HsqlDBShutdownService {
#Autowired
private ApplicationContext applicationContext;
#PersistenceContext
private EntityManager entityManager;
#Autowired
private HyperSqlDbServer hyperSqlDbServer;
public void executeShutdownQuery() {
Connection conn = null;
try {
JdbcTemplate jdbcTemplate = new JdbcTemplate(this.applicationContext.getBean(DataSource.class));
conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
conn.setAutoCommit(true);
jdbcTemplate.execute("SHUTDOWN");
} catch(Exception ex) {
ex.printStackTrace();
} finally {
try {
if(conn != null)
conn.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
#Transactional
public void closeEntityManager() {
entityManager.close();
}
#PostConstruct
public void setHsqlDBShutdownService() {
hyperSqlDbServer.setShutdownService(this);
}
}
The server can now restart successfully without leaving Hsqldb lock files around.