I get this error when trying to invoke “persist” method to save entity model to database in my Spring MVC web application. Can’t really find any post or page in internet that can relate to this particular error. It seems like something’s wrong with EntityManagerFactory bean but i’m fairly new to Spring programming so for me it seems like everything is initialized fine and according to various tutorial articles in web.
dispatcher-servlet.xml
<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd"> <context:component-scan base-package="wymysl.Controllers" /> <jpa:repositories base-package="wymysl.repositories"/> <context:component-scan base-package="wymysl.beans" /> <context:component-scan base-package="wymysl.Validators" /> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> <bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/> <bean id="passwordValidator" class="wymysl.Validators.PasswordValidator"></bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" /> <property name="username" value="system" /> <property name="password" value="polskabieda1" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml" /> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" /> <property name="showSql" value="true" /> <property name="generateDdl" value="false" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.max_fetch_depth">3</prop> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.jdbc.batch_size">10</prop> </props> </property> </bean> <mvc:annotation-driven /> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename" value="classpath:messages" /> </bean> <bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> <mvc:resources mapping="/resources/**" location="/resources/" /> <mvc:resources mapping="/resources/*" location="/resources/css/" cache-period="31556926"/> </beans>
RegisterController.java
@Controller public class RegisterController { @PersistenceContext EntityManager entityManager; @Autowired PasswordValidator passwordValidator; @InitBinder private void initBinder(WebDataBinder binder) { binder.setValidator(passwordValidator); } @RequestMapping(value = "/addUser", method = RequestMethod.GET) public String register(Person person) { return "register"; } @RequestMapping(value = "/addUser", method = RequestMethod.POST) public String register(@ModelAttribute("person") @Valid @Validated Person person, BindingResult result) { if(result.hasErrors()) { return "register"; } else { entityManager.persist(person); return "index"; } }
Advertisement
Answer
I had the same problem and I annotated the method as @Transactional
and it worked.
UPDATE: checking the spring documentation it looks like by default the PersistenceContext is of type Transaction, so that’s why the method has to be transactional (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html):
The @PersistenceContext annotation has an optional attribute type, which defaults to PersistenceContextType.TRANSACTION. This default is what you need to receive a shared EntityManager proxy. The alternative, PersistenceContextType.EXTENDED, is a completely different affair: This results in a so-called extended EntityManager, which is not thread-safe and hence must not be used in a concurrently accessed component such as a Spring-managed singleton bean. Extended EntityManagers are only supposed to be used in stateful components that, for example, reside in a session, with the lifecycle of the EntityManager not tied to a current transaction but rather being completely up to the application.