简体   繁体   English

spring transactional cpool。 我用哪一个?

[英]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: 这是我的结果:

  • enhydra xapool is a terrible connection pool. enhydra xapool是一个糟糕的连接池。 I won't enumerate the problems it caused because it doesn't matter. 我不会列举它引起的问题,因为它无关紧要。 The latest version of that pool hasn't been updated since Dec 2006. It's a dead project. 自2006年12月以来,该池的最新版本尚未更新。这是一个死的项目。
  • I put c3p0 into my application context and got it working fairly easily. 我将c3p0放入我的应用程序上下文中并使其工作相当容易。 But, for some reason it just doesn't seem to support rollback even inside a single method. 但是,由于某些原因,即使在单个方法中,它似乎也不支持回滚。 If I mark a method as @Transactional then do an insert into a table and then throw a RuntimeException (one that's definitely not declared in the throws list of the method because there is no throws list on the method) it will still keep the insert into that table. 如果我将方法标记为@Transactional然后执行插入到表中然后抛出一个RuntimeException(一个肯定没有在方法的throws列表中声明的那个,因为方法上没有throws列表)它仍会保留插入那张桌子。 It will not roll back. 它不会回滚。
  • I was going to try Apache DBCP, but my searching turned up lots of complaints about it, so I didn't bother. 我打算尝试Apache DBCP,但我的搜索引起了很多关于它的抱怨,所以我没有打扰。
  • I tried Bitronix and had plenty of trouble getting it to work properly under Tomcat, but once I figured out the magic configuration it works beautifully. 我试过Bitronix并且在Tomcat下使它正常工作时遇到了很多麻烦,但是一旦我弄清楚它的神奇配置,它就能很好地工作。 What follows is all the things you need to do to set it up properly. 以下是正确设置所需的所有事项。
  • I dabbled briefly with the Atomkos connection pool. 我简单介绍了Atomkos连接池。 It looks like it should be good, but I got Bitronix working first, so I didn't try using it much. 它看起来应该是好的,但我让Bitronix首先工作,所以我没有尝试使用它。

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.htmlhttp:/ /docs.codehaus.org/display/BTM/FAQhttp://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问题:

  • You can't query tables which are remote synonyms in Oracle while inside a Spring @Transactional block while using Bitronix (you'll get an ORA-24777). 在使用Bitronix时,您无法在Spring @Transactional块内部查询Oracle中远程同义词的表(您将获得ORA-24777)。 Use materialized views or a separate EntityManager which directly points at the other DB instead. 使用物化视图或单独的EntityManager直接指向其他DB。
  • For some reason the btmConfig in the applicationContext.xml has problems setting config values. 由于某种原因,applicationContext.xml中的btmConfig在设置配置值时遇到问题。 Instead create a bitronix-default-config.properties file. 而是创建一个bitronix-default-config.properties文件。 The config values you can use are found at http://docs.codehaus.org/display/BTM/Configuration13 . 您可以使用的配置值位于http://docs.codehaus.org/display/BTM/Configuration13 Some other config info for that file is at http://docs.codehaus.org/display/BTM/JdbcConfiguration13 but I haven't used it. 该文件的其他一些配置信息位于http://docs.codehaus.org/display/BTM/JdbcConfiguration13,但我还没有使用它。
  • Bitronix uses some local files to store transactional stuff. Bitronix使用一些本地文件来存储事务性内容。 I don't know why, but I do know that if you have multiple webapps with local connection pools you will have problems because they will both try to access the same files. 我不知道为什么,但我知道如果你有多个带有本地连接池的webapp,你会遇到问题,因为他们都会尝试访问相同的文件。 To fix this specify different values for bitronix.tm.journal.disk.logPart1Filename and bitronix.tm.journal.disk.logPart2Filename in the bitronix-default-config.properties for each app. 要解决此问题,请在bitronix-default-config.properties中为每个应用指定bitronix.tm.journal.disk.logPart1Filename和bitronix.tm.journal.disk.logPart2Filename的不同值。
  • Bitronix javadocs are at http://www.bitronix.be/uploads/api/index.html . Bitronix javadocs位于http://www.bitronix.be/uploads/api/index.html

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM