Domanda

I originariamente creato primavera con xapool, ma si scopre che è un progetto morto e sembra avere un sacco di problemi.

sono passato a c3p0, ma ora vengo a sapere che le annotazioni @Transactional in realtà non creano transazioni quando viene utilizzato con c3p0. Se faccio la seguente sarà inserire la riga in Foo anche attraverso un'eccezione è stato gettato all'interno del metodo:

@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");
    }

}

Questo è strano, perché se io commento l'annotazione @Transactional sarà effettivamente fallire e si lamentano di avere un set di transazioni di rollback solo:

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)

Quindi, chiaramente nota l'annotazione @Transactional. Ma, in realtà non impostato autocommit per via all'inizio del metodo.

Ecco come ho roba transazionale di installazione nella applicationContext.xml. È corretto? Se no, cosa si suppone che questo è?

<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"/>

Dopo un po 'di ricerca ho trovato un pool di connessioni chiamato Bitronix, ma la loro pagina di configurazione di primavera descrive cose su JMS che non ha nemmeno alcun senso. Che cosa JMS ha a che fare con la creazione di un pool di connessioni?

Così mi sono bloccato. Che cosa sono io in realtà dovrei fare? Non capisco il motivo per cui il pool di connessione deve supportare le transazioni. Tutto il supporto connessioni girando autocommit on e off, quindi non ho idea di quale sia il problema qui.

È stato utile?

Soluzione

Ci sono voluti un sacco di ricerca e di sperimentazione, ma alla fine ho ottenuto le cose di lavoro. Qui sono i miei risultati:

  • enhydra xapool è un pool di connessioni terribile. Non voglio elencare i problemi che hanno causato, perché non importa. L'ultima versione di quella piscina non è stato aggiornato dal dicembre 2006. E 'un progetto morto.
  • ho messo nella mia c3p0 contesto dell'applicazione e ottenuto che funziona abbastanza facilmente. Ma, per qualche motivo non sembra supportare il rollback anche all'interno di un singolo metodo. Se ho marchio un metodo come @Transactional poi fare un inserimento in una tabella e poi gettare un RuntimeException (uno che non è assolutamente dichiarati nella lista del metodo tiri perché non c'è getta lista sul metodo) sarà ancora mantenere l'inserto nella quel tavolo. Non sarà il rollback.
  • Stavo per provare Apache DBCP, ma la mia ricerca alzato un sacco di lamentele su di esso, quindi non mi ha dato fastidio.
  • Ho provato Bitronix e aveva un sacco di problemi a farla funzionare correttamente sotto Tomcat, ma una volta che ho capito la configurazione magica funziona splendidamente. Quello che segue è tutte le cose che dovete fare per configurarlo correttamente.
  • I dilettava brevemente con il pool di connessioni Atomkos. Sembra che dovrebbe essere buona, ma ho avuto Bitronix lavorando prima, quindi non ho provato ad usarlo molto.

La configurazione seguente funziona in test di unità autonome e sotto Tomcat. Questo era il problema principale che ho avuto. La maggior parte degli esempi che ho trovato su come impostare primavera con Bitronix supporre che sto usando JBoss o qualche altro contenitore pieno.

Il primo bit di configurazione è la parte che imposta il manager Bitronix transazione.

<!-- 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" />

La principale differenza tra il codice e gli esempi che ho trovato è la proprietà "disableJmx". Esso genera eccezioni a runtime se non si utilizza JMX, ma di lasciarlo abilitato.

La prossima po 'di configurazione è la fonte di dati pool di connessioni. Si noti che il nome classe pool di connessioni non è il normale classe di Oracle "oracle.jdbc.driver.OracleDriver". Si tratta di una fonte di dati XA. Non so ciò che la classe equivalente sarebbe in altri database.

<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>

Si noti inoltre che l'UniqueName ha bisogno di essere diverso da tutte le altre fonti di dati configurate.

Il TestQuery ovviamente deve essere specifico per il database che si sta utilizzando. Le proprietà del driver sono specifici per la classe di database che sto usando. OracleXADataSource per qualche motivo stupido ha nomi diversi setter per OracleDriver per lo stesso valore.

I allowLocalTransactions dovevano essere impostata su true per me. Ho trovato raccomandazioni NON impostare a true on-line. Ma, che sembra essere impossibile. Semplicemente non funziona se è impostata su false. Io non sono sufficientemente informati circa queste cose per sapere perché.

Infine abbiamo bisogno di configurare la fabbrica di un'entità responsabile.

<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>

Si noti la proprietà DataSource si riferisce alla id del dataSource ho dichiarato. Il persistenceXmlLocation fa riferimento a un file XML di persistenza che esiste nel classpath da qualche parte. Classpath *: indica che può essere in qualsiasi vaso. Senza l'* non troverà se è in un barattolo per qualche motivo.

Ho trovato util:. Mappa per essere un modo pratico per mettere i valori jpaPropertyMap in un unico luogo in modo che non ho bisogno di ripeterli quando uso più fabbriche entità Manager in uno contesto applicativo

Si noti che l'util: mappa qui sopra non funziona a meno che non si includono le impostazioni corrette nell'elemento fagioli esterno. Ecco l'intestazione del file XML che uso:

<?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">

Infine, in ordine per Bitronix (o apparentemente qualsiasi Cpool che supporta due phase commit) per lavorare con Oracle è necessario eseguire le seguenti borse di studio come utente SYS. (Vedere http: //publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/rtrb_dsaccess2.html e http://docs.codehaus.org/display/BTM/FAQ e 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>;

Queste sovvenzioni devono essere eseguiti per ogni utente che un pool di connessioni è impostato per indipendentemente dal fatto che effettivamente fare qualsiasi modifica di nulla. Sembra a quanto pare per quelle tabelle quando viene stabilita una connessione.

Alcuni altri problemi Varie:

  • Non è possibile interrogare le tabelle che sono sinonimi remoti in Oracle, mentre all'interno di un blocco @Transactional primavera durante l'utilizzo Bitronix (si otterrà un ORA-24777). Usa viste materializzate o un EntityManager separato che punta direttamente all'altra DB invece.
  • Per qualche motivo l'btmConfig nel applicationContext.xml ha problemi di impostazione dei valori di configurazione. Invece di creare un file bitronix-default-config.properties. I valori di configurazione che è possibile utilizzare sono disponibili all'indirizzo http://docs.codehaus.org/display/BTM / Configuration13 . Qualche altro informazioni di configurazione per quel file è a http://docs.codehaus.org/display/BTM / JdbcConfiguration13 ma io non l'ho usato.
  • Bitronix utilizza alcuni file locali per memorizzare roba transazionale. Non so perché, ma so che se si dispone di più webapps con pool di connessioni locali si avranno problemi perché saranno entrambi cercare di accedere agli stessi file. Per risolvere questo problema specificare valori diversi per bitronix.tm.journal.disk.logPart1Filename e bitronix.tm.journal.disk.logPart2Filename nelle bitronix-default-config.properties per ogni app.
  • javadocs Bitronix sono a http://www.bitronix.be/uploads/api /index.html .

Questo è praticamente. E 'molto poco pratici per farlo funzionare, ma è ora di lavoro e sono felice. Spero che tutto questo aiuta gli altri che stanno attraversando gli stessi problemi ho fatto per ottenere tutto questo per funzionare.

Altri suggerimenti

Quando faccio il pool di connessioni Io tendo ad usare quello fornito dal server app sto distribuzione on. E 'solo un nome JNDI alla primavera in quel punto.

Dal momento che non si vuole preoccupare di un application server quando sto testando, io uso un DriverManagerDataSource e il suo gestore delle transazioni associate quando sono test di unità. Io non sono così preoccupato per il pool o le prestazioni durante il test. Io voglio i test da eseguire in modo efficiente, ma messa in comune non è una garanzia di insuccesso in quel caso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top