我的应用程序要求非常简单:使用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