Reputation: 112
I seem unable to persist data in the db using the injected entitymgr in spring mvc. I have seen multiple similar questions (like EntityManager cannot use persist to save element to database) , but none of the answers seem to solve my issue. Here is my config:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- datasource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${driver}"
p:url="${url}"
p:username="contact" p:password="contact" />
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="com.rd.web"> <!-- scans for entities (model) -->
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQL5Dialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:component-scan base-package="com.rd.web" />
<bean id="contactService" class="com.rd.web.service.ContactServiceImpl"/>
</beans>
I have following code in (was in web controller but currently moved to service for testing):
TEST_CASE1 (using spring transaction):
@Transactional
public Contact setContact(Contact c){
if(c.getId() == null){
getEMgr().persist(c);
}else{
getEMgr().merge(c);
}
return c;
}
==> no error, the entity just isn't inserted, also no insert statement in log.
TEST_CASE2 (using spring transaction):
@Transactional
public Contact setContact(Contact c){
if(c.getId() == null){
getEMgr().persist(c);
}else{
getEMgr().merge(c);
}
getEMgr().flush();
return c;
}
==> Exception I got: no transaction is in progress
TEST_CASE3:
public Contact setContact(Contact c){
getEMgr().getTransaction().begin();
try{
if(c.getId() == null){
getEMgr().persist(c);
}else{
getEMgr().merge(c);
}
getEMgr().flush();
getEMgr().getTransaction().commit();
return c;
}catch(Throwable t){
getEMgr().getTransaction().setRollbackOnly();
}
return null;
}
==> throws error: java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
It should not be spring AOP issue since the operation is public and called from another component (in which the service is injected). Also the appcontext defines the transactions to be annotion driver . I don't really get why my transactions are not started.
When I use the same applicationcontext.xml and just trigger a testclass that loads the contactservice and creates a contact, the contact is saved correctly.
Also I have added the following filter in my web.xml, but to no avail:
<filter>
<filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Any hints would be appreciated. Cheers.
Added some extra information:
@Muel, the entitymgr is being injected using persistencecontext:
@Transactional
@Service("contactService") public class ContactServiceImpl implements IContactService {
// @Autowired // private IEntityMgrProvider eMgrPovider;
@PersistenceContext EntityManager eMgr;
@Transactional
public Contact getContactByID(long id) {
return getEMgr().find(Contact.class, id);
}
@Transactional
public List<Contact> getAllContacts() {
TypedQuery<Contact> qry = getEMgr().createNamedQuery("findAll", Contact.class);
return qry.getResultList();
}
@Transactional
public Contact setContact(Contact c){
if(c.getId() == null){
getEMgr().persist(c);
// getEMgr().flush();
}else{
getEMgr().merge(c);
}
return c;
}
@Transactional(readOnly=true)
public void deleteContact(long id){
getEMgr().remove(getEMgr().find(Contact.class, id));
}
private EntityManager getEMgr(){
// return eMgrPovider.getEMgr();
return eMgr;
}
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("appctx.xml");
IContactService contactService = ctx.getBean("contactService", IContactService.class);
Contact c= new Contact();
c.setBirthDate(new Date());
c.setFirstName("P1");
c.setLastName("P2");
ContactTelDetail tel = new ContactTelDetail();
tel.setContact(c);
tel.setTelNumber("056776650");
tel.setTelType("landline");
c = contactService.setContact(c);
System.out.println(c.getId());
}
}
I realize this getEmgr() method is not necessary but originally the eMgr came from somewhere else (where it was also injected, but nevermind that for now) BTW, when i run the main method, I can actually insert the Contact...
@user2264997 This is my servlet context:
<?xml version="1.0" encoding="UTF-8"?>
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
I have only added the last two filters for testing. I don't think it should be required (it seems the last filter is only for supporting lazy loading or something, but tried it anyway ...)
@Martin Frey
I'll have a look into that you could be on to something.
@mdeinum.wordpress.com
The service is injected in the webcontroller using @autowired. Service implementation and web.xml see above. config file for the dispatcher servlet (allthough it does not seem to have relevant information, maybe that could be the problem however ;) ):
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- use this dispatcher servlet for root -->
<!-- <default-servlet-handler/> -->
<resources location="/, classpath:/META-INF/web-resources/" mapping="/resources/**"/>
<interceptors>
<beans:bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
<beans:bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="lang"/>
</interceptors>
<beans:bean
class="org.springframework.ui.context.support.ResourceBundleThemeSource"
id="themeSource" />
<beans:bean class="org.springframework.web.servlet.theme.CookieThemeResolver"
id="themeResolver" p:cookieName="theme" p:defaultThemeName="standard" />
<beans:bean
class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
id="messageSource" p:basenames="WEB-INF/i18n/messages,WEB-INF/i18n/application"
p:fallbackToSystemLocale="false" />
<beans:bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
id="localeResolver" p:cookieName="locale"/>
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!-- <resources location="/resources/" mapping="/resources/**" /> -->
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<!-- now using tiles in stead ==> different view resolver -->
<!-- <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> -->
<!-- <beans:property name="prefix" value="/WEB-INF/views/" /> -->
<!-- <beans:property name="suffix" value=".jsp" /> -->
<!-- </beans:bean> -->
<context:component-scan base-package="com.rd.web" />
<!-- Add the following beans -->
<!-- Tiles Configuration -->
<beans:bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"
id="tilesViewResolver">
<beans:property name="viewClass"
value="org.springframework.web.servlet.view.tiles2.TilesView" />
</beans:bean>
<beans:bean
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer"
id="tilesConfigurer">
<beans:property name="definitions">
<beans:list>
<beans:value>/WEB-INF/layouts/layouts.xml</beans:value>
<!-- Scan views directory for Tiles configurations -->
<beans:value>/WEB-INF/views/**/views.xml</beans:value>
</beans:list>
</beans:property>
</beans:bean>
</beans:beans>
I'll try to configure the hibernate adapter and let you know how it goes...
Cheers
Upvotes: 0
Views: 2389
Reputation: 112
Allright, so there was indeed an issue with component scan being defined in the root application context and the dispathcer servlet application context. I moved the controller to a separate package and only scanned that package in the application context for the dispatcher servlet and now everything works fine!
Upvotes: 0
Reputation: 11663
You are duplicating your component scanning in both applicationContext.xml and servlet-context.xml.
<context:component-scan base-package="com.rd.web" />
When you do this, the controller will get injected with services picked up by the component scan in servlet-context.xml which does not have transactions.
Either explicitly specify controller packages for the base-package in servlet-context.xml, and non-controller packages for base-package in applicatioContext.xml.
Or use the exclude/include filter in the component-scan declaration.
applicationContext.xml
<context:component-scan ..>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
...
in servlet-context.xml
<context:component-scan ..>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
...
Upvotes: 2