Tried to configure Spring for tests with hibernate and transactions. Getting bean from app context which is marked with #Transactional transaction isn't intercepted. What I could miss in configuration?
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<import resource="spring-dao.xml"/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="userService" class="com.test.service.UserServiceimpl">
<property name="userDao" ref="userDao"/>
</bean>
public interface UserService {
public abstract User loadUserById(long userId);
#Transactional
public abstract void doSomething();
}
public class UserServiceimpl implements UserService {
#Override
public void doSomething() {
User user = loadUserById(1);
user.fillUpMoney(999);
userDao.update(user);
throw new RuntimeException("Shpould be rollback");
}
Don't annotate the abstract method as transactional, annotate the concrete implementation.
Do not user BeanFactory ;)
http://forum.springsource.org/showthread.php?122292-Sprinng-doesnt-intercept-transaction
Related
I have following configuration in application context
<jee:jndi-lookup id="dataSource" jndi-name="MY_DS" />
<context:load-time-weaver/>
<bean id="transactionManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jtaDataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="persistenceUnitName" value="pu_TEST" />
</bean>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="database" value="ORACLE" />
<property name="showSql" value="true" />
</bean>
Now my DAO Class
#Repository
public class EmployeeDAO{
#PersistenceContext
private EntityManager em;
#Transactional
public void create(Employee entity) {
LOG.error("Enitity Manager:create" + em);
em.persist(entity);
// em.flush(); if i use flush it saves
}
}
Now when I save the entity it does not say give any error but no data is updated into db.
I do not wish to use flush as entitymanager is injected by spring and should perform flush at the end automatically which is not happening. correct my understanding.
Adding facade class may be issue is there, Does Propagation.REQUIRES_NEW has anything to do here?
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void process(){
Employee e = factory.getEmployee();
employeeDao.create(e);
}
On Debug after create method call it shows employee got primary key populated that mean db call has made but at the end it is not persisted.
Please try either of the 3 :
1.Solution 1
Please call below code
em.joinTransaction();
just before
em.persistEntity(entity);
2.Solution 2
make attribute readOnly=false in #Transactional
3.Solution 3
Try manually adding bean EmployeeDAO in spring xml file
or else you can try below:
#Transactional(propagation=Propagation.REQUIRED)
Issue: Cannot add Address object via User object inside a Spring Controller.
User and Address classes -> #Entity
User has a List<Address> with FetchType=LAZY
#Repository
public class UserDao{
#Autowired
private SessionFactory sessionFactory;
...
public User get(String username) {
Session session = sessionFactory.getCurrentSession();
return (User)session.get(User.class, username);
}
...
public void update(User user){
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(user);
}
...
}
#Service
#Transnational
public class UserService{
#AutoWired
private UserDao userDao;
...
#Transactional(readOnly = true)
public User get(String username) {
Session session = sessionFactory.getCurrentSession();
return (User)session.get(User.class, username);
}
public void update(User user){
userDao.update(user);
}
...
}
#Controller
public class UserController{
#AutoWired
private UserService userService;
....
public String update(){
User user = userService.get("user0001");
user.getAddressList.add(new Address("new street"));
return "update";
}
}
Spring.xml
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:jdbc.properties</value>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="packagesToScan" value="com.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
</bean>
Everything is working fine. But I cannot make changes to the user object inside a #Controller.
When user object is getting change in #Controller level, there is no such Hibernate session involves with the object. Somehow the object is out of the hibernate context.
Error happens at .add(new Address("new street")); statement in #Controller.
Why it is prohibited to change an object inside a Controller which is received via Hibernate session?
The way I followed is incorrect? If not what have I done wrong?
--Spring 4, Hibernate 4
User has a List<Address>. When you fetch the user from the database rather then a list, hibernate inserts a proxy that handles the fetching of the addresses.
This proxy needs to have a session to be able to do anything. When you try to add an address you are outside the scope of the transactional annotation, thus there is no session.
Best way to get you going would be to add a method annotated with #Transactional in the UserService that adds an address.
I am trying to implement the following: I need to add two different entities in same same transaction to database.
I have different DAO classes and Service classes for each entity.
public class InvoicesDAO {
#Autowired
protected SessionFactory sessionFactory;
public void save(Invoice object) {
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
session.persist(object);
}
}
public class RequestsDAO {
#Autowired
protected SessionFactory sessionFactory;
public void save(Request object) {
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
session.persist(object);
}
}
public class InvoicesService {
#Autowired
private InvoicesDAO invoicesDAO;
#Autowired
private RequestsDAO requestsDAO;
#Transactional
public void add(Invoice object) throws HibernateException {
invoicesDAO.save(object);
}
#Transactional
public void updateAndGenerate(Invoice object1, Request object2) throws HibernateException {
invoicesDAO.save(object1);
requestsDAO.save(object2);
}
}
The config:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/hibernate.properties" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${hibernate.connection.driver_class}" />
<property name="url" value="${hibernate.connection.url}" />
<property name="username" value="${hibernate.connection.username}" />
<property name="password" value="${hibernate.connection.password}" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.ejl.butler.object.data" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<context:annotation-config />
<context:component-scan base-package="com.service" />
<bean id="invoicesDao" class="com.dao.InvoicesDAO" />
<bean id="requestsDao" class="com.dao.RequestsDAO" />
Controller:
//***
/**
* Invoices access service
*/
#Autowired
private InvoicesService invoicesService;
// objects creation
invoicesService.updateAndGenerate(invoice, request);
//***
So when I am trying to call updateAndGenerate method and pass there invalid values for object2 - it fails without rolling back the object1. How can I fix it? Thank you
I dont think it is got to do with Proxies. You dont need a proxy object here. Generally you need a proxy object for instances such for a login service etc where you need a proxy object for the singleton bean definition. But, the only way it can not rollback is if your propogation level on the Transaction isnt correct.
If you use a Trasaction.REQUIRES_NEW then the dao.save wouldnt rollback and it wouldnt tie back to the outer transaction and hence wouldnt rollback.
Finally I figured out where the problem was so I will answer my own question...
According to Declarative transactions (#Transactional) doesn't work with #Repository in Spring and https://stackoverflow.com/a/3250959/705869 the order of the base-package items inside context:component-scan directive is very important. In additional, you should put only really necessary packages.
I had some duplicates inside this directive so the application context was initialized before database context. And that's why transactions were disabled inside services!
So check twice for base-package packages inside context:component-scan and remove unnecessary ones.
I am using <mvc:annotation-driven/> and I would like to configure RequestMappingHandlerMapping for disabling useTrailingSlashMatch. When I declare another RequestMappingHandlerMapping, I will end up 2 RequestMappingHandlerMapping. How can I configure RequestMappingHandlerMapping ?
As you have already noted, this is feasible in xml by removing mvc:annotation-driven and replacing with the entire xml equivalent:
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useTrailingSlashMatch" value="true"></property>
</bean>
Can you try with Java config to override RequestMappingHandlerMapping value
#Configuration
#ComponentScan(basePackages = "base.package.name")
public class WebAppConfig extends WebMvcConfigurationSupport {
#Override
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping rmh = super.requestMappingHandlerMapping();
rmh.setUseTrailingSlashMatch(false);
return rmh;
}
}
If you want a solution that doesn't involve duplicating functionality in Spring then you can override the DisplatcherServlet. in Servlet 3.0 container this might look like:
#WebServlet(name="spring-dispatcher", loadOnStartup=1, urlPatterns={"/"},
initParams={
#WebInitParam(name="contextConfigLocation",
value="/WEB-INF/spring/spring-dispatcher-servlet.xml")})
public class MyDispatcherServlet extends DispatcherServlet {
#Override
protected void initStrategies(ApplicationContext context) {
super.initStrategies(context);
for (RequestMappingInfoHandlerMapping handlerMapping
: BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, RequestMappingInfoHandlerMapping.class, true, false).values()) {
handlerMapping.setUseTrailingSlashMatch(false);
}
}
}
Add the following to your spring configuration file to toggle the useTrailingSlashMatch field.
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useTrailingSlashMatch" value="true">
</property>
</bean>
Hallo all:
I read the spring reference about this point.
I would choose to use the #PersistenceContext in my DAO to inject a shared transactional entity manager, but since I use the GenericDaoJpaImpl pattern over two entityManagerFactories that point to 2 different persistence units I cannot use it.
So right now in my application I have this configuration:
entityManagerFactoryies:
<bean id="entityManagerFactoryIban0" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/contratto-persistence-iban0.xml" />
</bean>
<bean id="entityManagerFactoryCont0" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/contratto-persistence-cont0.xml" />
</bean>
<bean abstract="true" id="abstractDaoJpaImplIban0" lazy-init="false">
<property name="entityManagerFactory" ref="entityManagerFactoryIban0" />
</bean>
<bean abstract="true" id="abstractDaoJpaImplCont0" lazy-init="false">
<property name="entityManagerFactory" ref="entityManagerFactoryCont0" />
</bean>
Then each of my DAOs is an instance of the GenericDaoImpl:
#Repository
public class GenericDaoJpaImpl<T, ID extends Serializable> implements GenericDao<T, ID> {
private Class<T> entityClass;
private EntityManagerFactory entityManagerFactory;
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
public GenericDaoJpaImpl() {
super();
}
public GenericDaoJpaImpl(Class<T> entityClass) {
super();
this.entityClass = entityClass;
}
/**
* #see it.alten.intesasanpaolo.contratto.dao.common.GenericDao#getItemByID(java.io.Serializable)
*/
#Override
public T getItemByID(ID id) {
EntityManager em = entityManagerFactory.createEntityManager();
return em.find(entityClass, id);
}
I construct my dao via spring in this way:
<bean id="eventMessageDao" parent="abstractDaoJpaImplCont0" class="it.alten.intesasanpaolo.contratto.dao.common.GenericDaoJpaImpl">
<constructor-arg>
<value>it.alten.intesasanpaolo.contratto.domain.event.OnlineEventMessage</value>
</constructor-arg>
</bean>
Now I would like to modify the GenericDaoJpaImpl as described in the spring documentation not to be associated to the entityManagerFactory from which I have to create every time the entityManager but directly to the entityManager.
I would like to define it in the context in a way I can inject it to the correct abstract dao to be extended from every dao.
<bean abstract="true" id="abstractDaoJpaImplIban0" lazy-init="false">
<property name="entityManagerFactory" ref="entityManagerFactoryIban0" />
</bean>
How can I achieve this?
kind regards
Massimo
You can use SharedEntityManagerBean to construct a transactional EntityManager from the EntityManagerFactory:
<bean id="entityManagerFactoryIban0"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>
<bean id="entityManagerIban0"
class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactoryIban0" />
</bean>
<bean abstract="true" id="abstractDaoJpaImplIban0" lazy-init="false">
<property name="entityManager" ref="entityManagerIban0" />
</bean>
You can provide the persistence unit name in the xml configuration, using the SharedEntityManagerBean, like below:
<bean id="testDao" class="com.test.persistence.dao.BaseDAO">
<property name="entityManager">
<bean class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="persistenceUnitName" value="persistence-test-unit" />
</bean>
</property>
</bean>
of course, you can have the SharedEntityManagerBean as a separate bean
Here, I m injecting entityManager into BaseDAO as you're doing using #PersistenceContext(unitName="...")