[英]spring transactional cpool. Which one do I use?
I originally set up spring with xapool, but it turns out that's a dead project and seems to have lots of problems. 我最初用xapool设置spring,但事实证明这是一个死的项目,似乎有很多问题。
I switched to c3p0, but now I learn that the @Transactional annotations don't actually create transactions when used with c3p0. 我切换到c3p0,但现在我知道@Transactional注释在与c3p0一起使用时实际上并不创建事务。 If I do the following it will insert the row into Foo even through an exception was thrown inside the method: 如果我执行以下操作,即使在方法内部抛出异常,它也会将行插入Foo:
@Service
public class FooTst
{
@PersistenceContext(unitName="accessControlDb") private EntityManager em;
@Transactional
public void insertFoo() {
em.createNativeQuery("INSERT INTO Foo (id) VALUES (:id)")
.setParameter("id", System.currentTimeMillis() % Integer.MAX_VALUE )
.executeUpdate();
throw new RuntimeException("Foo");
}
}
This is strange because if I comment out the @Transactional annotation it will actually fail and complain about having a transaction set to rollback only: 这很奇怪,因为如果我注释掉@Transactional注释,它实际上会失败并且抱怨将事务设置为仅回滚:
java.lang.IllegalStateException: Cannot get Transaction for setRollbackOnly
at org.objectweb.jotm.Current.setRollbackOnly(Current.java:568)
at org.hibernate.ejb.AbstractEntityManagerImpl.markAsRollback(AbstractEntityManagerImpl.java:421)
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:576)
at org.hibernate.ejb.QueryImpl.executeUpdate(QueryImpl.java:48)
at com.ipass.rbac.svc.FooTst.insertFoo(FooTst.java:21)
at com.ipass.rbac.svc.SingleTst.testHasPriv(SingleTst.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
So, clearly it notices the @Transactional annotation. 所以,很明显它注意到@Transactional注释。 But, it doesn't actually set autocommit to off at the start of the method. 但是,它实际上并没有在方法开始时将autocommit设置为off。
Here is how I have transactional stuff setup up in the applicationContext.xml. 以下是我在applicationContext.xml中设置事务的方法。 Is this correct? 这个对吗? If not, what is this supposed to be? 如果没有,这应该是什么?
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="jotm"/>
<property name="userTransaction" ref="jotm"/>
<property name="allowCustomIsolationLevels" value="true"/>
</bean>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="false"/>
After a bunch of searching I found a connection pool called Bitronix, but their spring setup page describes stuff about JMS which doesn't even make any sense. 经过一堆搜索,我发现了一个名为Bitronix的连接池,但是他们的弹簧设置页面描述了有关JMS的内容,这些内容甚至没有任何意义。 What does JMS have to do with setting up a connection pool? JMS与设置连接池有什么关系?
So I'm stuck. 所以我被卡住了。 What am I actually supposed to do? 我到底应该做什么? I don't understand why the connection pool needs to support transactions. 我不明白为什么连接池需要支持事务。 All connections support turning autocommit on and off, so I have no idea what the problem is here. 所有连接都支持打开和关闭自动提交,所以我不知道这里有什么问题。
It took a lot of searching and experimentation, but I finally got things working. 这需要大量的搜索和实验,但我终于开始工作了。 Here are my results: 这是我的结果:
The configuration below works in standalone unit tests and under Tomcat. 以下配置适用于独立单元测试和Tomcat。 That was the major problem I had. 那是我遇到的主要问题。 Most of the examples I found about how to set up Spring with Bitronix assume that I'm using JBoss or some other full container. 我发现的大多数关于如何使用Bitronix设置Spring的例子都假设我使用的是JBoss或其他一些完整的容器。
The first bit of configuration is the part that sets up the Bitronix transaction manager. 第一项配置是设置Bitronix事务管理器的部分。
<!-- Bitronix transaction manager -->
<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="disableJmx" value="true" />
</bean>
<bean id="btmManager" factory-method="getTransactionManager" class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig" destroy-method="shutdown"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="btmManager" />
<property name="userTransaction" ref="btmManager" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
The major difference between that code and the examples I found is the "disableJmx" property. 该代码与我找到的示例之间的主要区别是“disableJmx”属性。 It throws exceptions at runtime if you don't use JMX but leave it enabled. 如果您不使用JMX但是保持启用状态,它会在运行时抛出异常。
The next bit of configuration is the connection pool data source. 下一部分配置是连接池数据源。 Note that the connection pool classname is not the normal oracle class "oracle.jdbc.driver.OracleDriver". 请注意,连接池classname不是正常的oracle类“oracle.jdbc.driver.OracleDriver”。 It's an XA data source. 这是一个XA数据源。 I don't know what the equivalent class would be in other databases. 我不知道其他数据库中的等价类是什么。
<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
<property name="uniqueName" value="dataSource-BTM" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="4" />
<property name="testQuery" value="SELECT 1 FROM dual" />
<property name="driverProperties"><props>
<prop key="URL">${jdbc.url}</prop>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
</props></property>
<property name="className" value="oracle.jdbc.xa.client.OracleXADataSource" />
<property name="allowLocalTransactions" value="true" />
</bean>
Note also that the uniqueName needs to be different than any other data sources you have configured. 另请注意,uniqueName需要与您配置的任何其他数据源不同。
The testQuery of course needs to be specific to the database that you are using. 当然,testQuery需要特定于您正在使用的数据库。 The driver properties are specific to the database class that I'm using. 驱动程序属性特定于我正在使用的数据库类。 OracleXADataSource for some silly reason has different setter names for OracleDriver for the same value. 出于某些愚蠢的原因,OracleXADataSource对于相同的值具有不同的OracleDriver的setter名称。
The allowLocalTransactions had to be set to true for me. allowLocalTransactions必须为我设置为true。 I found recommendations NOT to set it to true online. 我发现建议不要在线设置为真。 But, that seems to be impossible. 但是,这似乎是不可能的。 It just won't work if it's set to false. 如果设置为false,它将无法工作。 I am not sufficiently knowledgeable about these things to know why that is. 我对这些事情知之甚少,不知道为什么会这样。
Lastly we need to configure the entity manager factory. 最后,我们需要配置实体管理器工厂。
<util:map id="jpa_property_map">
<entry key="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionManagerLookup"/>
<entry key="hibernate.current_session_context_class" value="jta"/>
</util:map>
<bean id="dataSource-emf" name="accessControlDb" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath*:META-INF/foo-persistence.xml" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect"/>
</bean>
</property>
<property name="jpaPropertyMap" ref="jpa_property_map"/>
<property name="jpaDialect"><bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/></property>
</bean>
Note the dataSource property refers to the id of the dataSource I declared. 请注意,dataSource属性引用我声明的dataSource的id。 The persistenceXmlLocation refers to a persistence xml file that exists in the classpath somewhere. persistenceXmlLocation指的是某个类路径中存在的持久性xml文件。 The classpath*: indicates it may be in any jar. 类路径*:表示它可能在任何jar中。 Without the * it won't find it if it's in a jar for some reason. 没有*它会因为某些原因而无法找到它。
I found util:map to be a handy way to put the jpaPropertyMap values in one place so that I don't need to repeat them when I use multiple entity manager factories in one application context. 我发现util:map是一种将jpaPropertyMap值放在一个地方的方便方法,这样当我在一个应用程序上下文中使用多个实体管理器工厂时,我就不需要重复它们了。
Note that the util:map above won't work unless you include the proper settings in the outer beans element. 请注意,除非在外部bean元素中包含适当的设置,否则上面的util:map将不起作用。 Here is the header of the xml file that I use: 这是我使用的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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
Lastly, in order for Bitronix (or apparently any cpool which supports two phase commit) to work with Oracle you need to run the following grants as user SYS. 最后,为了使Bitronix(或显然任何支持两阶段提交的cpool)与Oracle一起工作,您需要以用户SYS的身份运行以下授权。 (See http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/rtrb_dsaccess2.html and http://docs.codehaus.org/display/BTM/FAQ and http://docs.codehaus.org/display/BTM/JdbcXaSupportEvaluation#JdbcXaSupportEvaluation-Oracle ) (参见http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/rtrb_dsaccess2.html和http:/ /docs.codehaus.org/display/BTM/FAQ和http://docs.codehaus.org/display/BTM/JdbcXaSupportEvaluation#JdbcXaSupportEvaluation-Oracle )
grant select on pending_trans$ to <someUsername>;
grant select on dba_2pc_pending to <someUsername>;
grant select on dba_pending_transactions to <someUsername>;
grant execute on dbms_system to <someUsername>;
Those grants need to be run for any user that a connection pool is set up for regardless of whether you actually do any modifying of anything. 无论您是否实际修改任何内容,都需要为任何设置连接池的用户运行这些授权。 It apparently looks for those tables when a connection is established. 在建立连接时,它显然会查找这些表。
A few other misc issues: 其他一些misc问题:
That's pretty much it. 这就是它。 It's very fiddly to get it to work, but it's working now and I'm happy. 让它发挥作用非常繁琐,但它现在正在工作,我很高兴。 I hope that all this helps others who are going through the same troubles I did to get this all to work. 我希望所有这些能帮助那些经历同样麻烦的人,让我们全力以赴。
When I do connection pooling I tend to use the one provided by the app server I'm deploying on. 当我进行连接池时,我倾向于使用我正在部署的应用服务器提供的那个。 It's just a JNDI name to Spring at that point. 那时它只是Spring的一个JNDI名称。
Since I don't want to worry about an app server when I'm testing, I use a DriverManagerDataSource and its associated transaction manager when I'm unit testing. 因为我在测试时不想担心应用服务器,所以当我进行单元测试时,我会使用DriverManagerDataSource及其关联的事务管理器。 I'm not as concerned about pooling or performance when testing. 在测试时我并不关心汇集或性能。 I do want the tests to run efficiently, but pooling isn't a deal breaker in that case. 我确实希望测试能够高效运行,但在这种情况下,池化并不是一个交易破坏者。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.