2013-08-28 78 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的應用程序上下文中掃描該包,現在一切正常!