我的应用程序要求非常简单:使用Spring3.1和JPA2(休眠)开发几个基于SOAP的Web服务,并将其托管在JBoss AS 7.1上。
因此,我开始创建一个多模块maven项目,其中一个jar模块包含使用Spring&JPA的服务实现,另一个war模块将该服务公开为基于SOAP的Web服务。 但是关键部分是服务需要针对某些服务方法与多个数据库进行对话。
我知道从Spring获得的JPA2集成支持,但没有persistence.xml和很酷的packagesToScan属性,这使工作变得更轻松。 我配置了2个数据源,2个LocalContainerEntityManagerFactoryBeans,注册了2个JpaTransactionManagers并启用了基于注释的事务管理支持。
<tx:annotation-driven transaction-manager='txnManager1'/><tx:annotation-driven transaction-manager='txnManager2'/><bean class='org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor'/><bean class='org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor'/><!-- This will throw error because it found multiple EntityManagerFactory beans --><bean id='txnManager1' class='org.springframework.orm.jpa.JpaTransactionManager'p:entityManagerFactory-ref='emf1'/><bean id='txnManager2' class='org.springframework.orm.jpa.JpaTransactionManager'p:entityManagerFactory-ref='emf2'/> <bean id='emf1' class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'><property name='persistenceUnitName' value='Sivalabs1PU'></property> <property name='dataSource' ref='dataSource1'></property><property name='jpaVendorAdapter'><bean id='jpaAdapter' class='org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter'p:showSql='${hibernate.show_sql}'/></property><property name='jpaProperties'><props><prop key='hibernate.dialect'>${hibernate.dialect}</prop><prop key='hibernate.hbm2ddl.auto'>${hibernate.hbm2ddl.auto}</prop></props></property><property name='packagesToScan' value='com.sivalabs.springdemo.entities'></property><property name='loadTimeWeaver'><bean class='org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver'/></property></bean> <bean id='emf2' class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'><property name='persistenceUnitName' value='Sivalabs2PU'></property><property name='dataSource' ref='dataSource2'></property><property name='jpaVendorAdapter'><bean id='jpaAdapter' class='org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter'p:showSql='${hibernate.show_sql}'/></property><property name='jpaProperties'><props><prop key='hibernate.dialect'>${hibernate.dialect}</prop><prop key='hibernate.hbm2ddl.auto'>${hibernate.hbm2ddl.auto}</prop></props></property><property name='packagesToScan' value='com.sivalabs.springdemo.entities'></property><property name='loadTimeWeaver'><bean class='org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver'/></property></bean> <bean id='dataSource1' class='org.apache.commons.dbcp.BasicDataSource'><property name='driverClassName' value='${node1.jdbc.driverClassName}'></property><property name='url' value='${node1.jdbc.url}'></property><property name='username' value='${node1.jdbc.username}'></property><property name='password' value='${node1.jdbc.password}'></property></bean><bean id='dataSource2' class='org.apache.commons.dbcp.BasicDataSource'><property name='driverClassName' value='${node2.jdbc.driverClassName}'></property><property name='url' value='${node2.jdbc.url}'></property><property name='username' value='${node2.jdbc.username}'></property><property name='password' value='${node2.jdbc.password}'></property></bean>
此后,我意识到将Entitymanager与正确的PersistenceUnit绑定,我需要将persistenceUnitName赋予LocalContainerEntityManagerFactoryBean。
<bean class='org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor'><property name='persistenceUnits' ><map><entry key='unit1' value='Sivalabs1PU'/><entry key='unit2' value='Sivalabs2PU'/></map></property></bean><bean id='emf1' class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'><property name='persistenceUnitName' value='Sivalabs1PU'></property><property name='dataSource' ref='dataSource1'></property>........ </bean> <bean id='emf2' class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'><property name='persistenceUnitName' value='Sivalabs2PU'></property><property name='dataSource' ref='dataSource2'></property>........ </bean>
然后在我的Service Bean中,将EntityManager和事务管理器粘合在一起,如下所示:
@Service
public class AdminUserService implements UserService
{@PersistenceContext(unitName='Sivalabs1PU')private EntityManager sivalabs1EM;@PersistenceContext(unitName='Sivalabs2PU')private EntityManager sivalabs2EM;@Override@Transactional('txnManager1')public List<User> getAllUsersFromSivalabs1DB() {return sivalabs1EM.createQuery('from User', User.class).getResultList();}@Override@Transactional('txnManager2')public List<User> getAllUsersFromSivalabs2DB() {return sivalabs2EM.createQuery('from User', User.class).getResultList();}}
现在,通过此设置,我得到了异常提示“找不到名称为'Sivalabs1PU'的持久性单元”。 然后,在进行一些谷歌搜索之后,我创建了META-INF / persistence.xml文件,如下所示:
<persistence><persistence-unit name='Sivalabs1PU' transaction-type='RESOURCE_LOCAL'> </persistence-unit><persistence-unit name='Sivalabs2PU' transaction-type='RESOURCE_LOCAL'> </persistence-unit></persistence>
现在,持久性单元名称错误得到解决,并出现其他异常,说明“未从[用户]映射用户”。 User类使用@Entity注释,并且位于“ com.sivalabs.springdemo.entities ”包中,我将其配置为“ packagesToScan ”属性。 我不明白为什么'packagesToScan'属性不起作用,如果没有persistence.xml,它会很好地工作。 所以暂时我在persistence.xml文件中配置了实体类。
<persistence><persistence-unit name='Sivalabs1PU' transaction-type='RESOURCE_LOCAL'> <class>com.sivalabs.springdemo.entities.User</class> </persistence-unit><persistence-unit name='Sivalabs2PU' transaction-type='RESOURCE_LOCAL'> <class>com.sivalabs.springdemo.entities.User</class></persistence-unit></persistence>
最终,当我运行调用AdminUserService方法的JUnit测试时,一切看起来都很好并且工作正常。 然后,我在JBoss AS 7.1服务器上部署了war文件,然后再次出现了很多错误。 JBoss抱怨“当未设置“ hibernate.dialect”时,Connection不能为空”。 '[PersistenceUnit:Sivalabs1PU]无法建立EntityManagerFactory。
经过几分钟的思考,我了解到JBoss服务器正在尝试执行“配置约定”规则应做的事情。 JBoss试图创建EntityManagerFactory,因为它在类路径中找到了META-INF / persistence.xml。 但是由于它不包含jdbc连接细节,因此引发了Error。
再次谷歌搜索之后,我发现我们可以将persistence.xml重命名为其他名称(spring-persistence.xml),并使用Spring将此新名称连接起来,如下所示:
<bean id='emf1' class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'><property name='persistenceUnitName' value='Sivalabs1PU'></property><property name='persistenceXmlLocation' value='classpath:META-INF/spring-persistence.xml'/><property name='dataSource' ref='dataSource1'></property>........ </bean> <bean id='emf2' class='org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean'><property name='persistenceUnitName' value='Sivalabs2PU'></property><property name='persistenceXmlLocation' value='classpath:META-INF/spring-persistence.xml'/><property name='dataSource' ref='dataSource2'></property>........ </bean>
最终,我成功地使该应用程序在JBoss AS 7.1上运行(仍然不知道还有多少其他漏洞)。
但是在这里我不了解Spring的一些概念:
1.当我尝试提供persistenceUnitName时,为什么Spring正在检查该名称是否存在于persistence.xml中? 无论如何,persistence.xml不含任何单元名称!
2.为什么在与persistence.xml一起使用时packagesToScan机制失败? 是弹簧虫吗?
一切似乎都正常,只是缺少一件事,在我与Spring和Tomcat一起工作时通常会露出微笑:-(
我非常喜欢Spring框架,自2006年以来一直在使用它,在编写Spring代码的同时我非常喜欢。 这并不意味着我不喜欢CDI,EJB3,JAX-RS :-)
无论如何,通过以上所有练习,我感觉就像Spring3 + JPA2 + JavaEE6AppServer = Confusion Over Configuration ,这只是我(一个普通的Java开发人员)的意见。
再说一次:Spring很棒,JavaEE6很棒,最新的JavaEE6 Application服务器也很棒:-)。
参考: Spring3 + JPA2 + JavaEE6AppServer =来自我们的JCG合作伙伴 Siva Reddy的“配置混乱”,来自My Experiments on Technology博客。
翻译自: https://www.javacodegeeks.com/2012/10/spring3-jpa2-java-ee6-app-server.html