2013-08-28 51 views
0

我似乎无法在spring mvc中使用注入的entitymgr在数据库中持久化数据。 我看到了多个类似的问题(如EntityManager cannot use persist to save element to database),但没有一个答案似乎解决了我的问题。 这里是我的配置:spring mvc + jpa + hibernate +事务问题

<?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> 

我有以下代码(在网络控制器,但目前转移到测试服务):

TEST_CASE1(使用Spring的事务):

@Transactional 
public Contact setContact(Contact c){  
     if(c.getId() == null){ 
      getEMgr().persist(c); 
     }else{ 
      getEMgr().merge(c); 
     } 
     return c; 
} 

==>没有错误,实体只是没有插入,在日志中也没有插入语句。

TEST_CASE2(使用弹簧交易):

@Transactional 
public Contact setContact(Contact c){  
     if(c.getId() == null){ 
      getEMgr().persist(c); 
     }else{ 
      getEMgr().merge(c); 
     } 
        getEMgr().flush(); 
     return c; 
} 

==>异常我:没有事务正在进行

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; 
} 

==>抛出错误: java.lang.IllegalStateException:不允许在共享EntityManager上创建事务 - 使用Spring事务或EJB CMT代替

它不应该是春天的AOP问题,因为操作是公开的,并从另一个组件(注入服务)中调用。 此外,appcontext将事务定义为annotion驱动程序。 我真的不明白为什么我的交易没有开始。

当我使用相同的applicationcontext.xml,只是触发加载contactservice并创建联系人的测试类时,联系人被正确保存。

而且我在web.xml中添加以下过滤器,但无济于事:

<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> 

任何提示,将不胜感激。干杯。

增加了一些额外的信息:

@Muel,该entitymgr被注射使用persistencecontext:

@Transactional 

@Service( “的ContactService”) 公共类ContactServiceImpl实现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()); 

} 
} 

我意识到这getEmgr()方法是没有必要的,但最初的eMgr来自其他地方(其中也有人注射,但没关系,就目前而言) BTW,当我运行的主要方法,我可以实际上插入联系人...

@ user2264997 这是我的servlet上下文:

<?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> 

我只是添加了最后两个过滤器进行测试,我不认为它是必需的(似乎最后一个过滤器只是用于支持延迟加载或某种东西,但是试过它......)

@Martin Frey

我会看看你可以做些什么。

@ mdeinum.wordpress.com

服务在webcontroller注射用@Autowired。 服务实现和web.xml见上面。对于调度的servlet(allthough似乎有相关信息,不,也许这可能是问题但是;) 配置文件):

<?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> 

我会尝试配置休眠适配器,让你知道如何它会...

干杯

+0

您可以粘贴“ContactServiceImpl”的完整代码吗?具体来说,我对'getEMgr()'实现感兴趣。 – Muel

+0

你能告诉我们你的servlet上下文xml吗? – ikumen

+0

我建议你通过设置以下记录器来调试hibernate事务和SQL:org.hibernate.transaction,org.hibernate.SQL。 (在hibernate 4.2上面使用org.hibernate.engine.transaction)。 – gerrytan

回答

2

要复制你的组件扫描两个applicationContext.xml中和servlet-context.xml的。

<context:component-scan base-package="com.rd.web" /> 

当你这样做的时候,控制器会注入servlet-context.xml中没有事务的组件扫描中拾取的服务。

要么在servlet-context.xml中显式指定base-package的控制器包,要么在applicatioContext.xml中显式指定base-package的非控制器包。

或者在组件扫描声明中使用exclude/include过滤器。

的applicationContext.xml

<context:component-scan ..> 
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> 
... 

在servlet的context.xml中

<context:component-scan ..> 
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> 
... 
+0

它并不真正使用include排除选项,但我认为你可以用双重加载的bean进行操作......我会研究它 – ruben056

0

奥莱特,所以确实有与组分扫描的问题在根应用上下文和dispathcer servlet应用被定义上下文。我将控制器移动到一个单独的包中,并且只在调度程序servlet的应用程序上下文中扫描该包,现在一切正常!