Hibernate + Transação em 2 bancos diferentes. XA transaction?

E ai pessoal, blz?

Tenho a seguinte situação:

Trabalho com um sistema onde cada cliente tem um schema no banco de dados e temos um global.

Tudo estava funcionando mto bem até que cheguei a seguinte situação:

Preciso que: dentro de um método, primeiro eu faça uma consulta no banco A, dps uma inserção no banco B.

Estou tendo um problema :
org.jboss.util.NestedSQLException: Transaction is not active

Fazendo alguns testes notei que isso ocorre porque ele cria uma nova transação e isso essa transação para conectar no banco A, mas não cria outra para o banco B.

Assim sendo, depois de fazer minha consulta no banco A, não consigo fazer mais nada no banco B.

Eu poderia:

  1. Cria a transação manualmente(péssimo)
  2. Colocar a parte que consulta o banco A em outro método que cria uma nova transação para isso(não tão ruim, mas ainda ruim)

Andei lendo um pouco sobre XA e acho que seria o caso de usar aqui, alguém sabe me dizer se ela se encaixaria nesse caso?

Esse tipo de cenário será bem comum em meu projeto, ter q trabalhar com mais de um banco e fazer com que tudo seja uma única transação.

Alguém tem algum exemplo de como configurar o hibernate para trabalhar assim ? Algum link, qq coisa ajuda, dei uma olhada na net mas sequer sei se XA se encaixa nesse meu cenário.

Grato,

O JTA que vai te ajudar bastante nisso

XA é pra fazer esse tipo de operacao de two-phase commit (2PC)

Dá uma olhada em http://www.infoq.com/minibooks/JTDS , tem um capitulo só de XA lá

Abraços

To baixando o PDF.

Qndo eu tiver uma solução posto aqui.

Valeu a força

Pedro Sena

Oi,

Não é muito complicado não, como o ffranceschi falou, vc precisa usar JTA e seus datasources tem que ser XA.

Segue um exemplo do arquivo datasource que faço deploy no jboss:

<datasources>
        <xa-datasource>
                <jndi-name>localhost</jndi-name>
                <track-connection-by-tx/>
                <isSameRM-override-value>false</isSameRM-override-value>
                <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
                <xa-datasource-property name="URL">jdbc:mysql://localhost/teste</xa-datasource-property>
                <xa-datasource-property name="User">XXXXX</xa-datasource-property>
                <xa-datasource-property name="Password">YYYYY</xa-datasource-property>
                <min-pool-size>5</min-pool-size>
            <max-pool-size>100</max-pool-size>
            <blocking-timeout-millis>5000</blocking-timeout-millis>
            <idle-timeout-minutes>15</idle-timeout-minutes>
                <metadata>
                        <type-mapping>mySQL</type-mapping>
                </metadata>
    </xa-datasource>
    <xa-datasource>
          ..............
    </xa-datasource>
</datasources>

Depois disso é só usar a transação do servidor de aplicação. Se estiver usando jboss, da uma lida nisso: http://docs.jboss.org/jbossas/jboss4guide/r5/html/ch4.chapt.html

Se vc estiver usando o Spring, ele pode gerenciar suas transações, segue um exemplo configuração para obter o entityManager:

<?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:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd"
	default-autowire="byName">
	
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="NOME_DO_UNIT_NAME" />
		<property name="jpaProperties">
			<props>
				<prop key="hibernate.transaction.factory_class">org.hibernate.ejb.transaction.JoinableCMTTransactionFactory </prop>
				<prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</prop>
			</props>
		</property>
	</bean>
	<bean id="transactionManager"
		class="org.springframework.transaction.jta.JtaTransactionManager"
		autowire="no">
		<property name="transactionManagerName" value="java:/TransactionManager" />
		<property name="userTransactionName" value="UserTransaction" />
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager" />
	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>

Você pode configurar vários entity manager (igual o configurado no exemplo) cada um acessando um persistent unit diferente e depois usar as anotações @Transactional e @PersistenceContext (nessa ultima indicando o unit-name) que o spring já faz tudo para vc.
Ah, no seu persistence.xml o transaction-type tem que ser “JTA”.

Aqui nos temos casos também de duas aplicações em servidores separados, acessando bases diferentes e se comunicando através de EJB, tudo transacional. Caso precise disso e estiver usando jboss, aqui explica como faz
http://www.jboss.org/community/docs/DOC-13179

Espero ter ajudado.

Opa luBS,

Obrigado pelas dicas, tendo algum exemplo assim fica mais fácil :slight_smile:

Vou tentar amanhã e posto os resultados.

Obrigado novamente e abs,

Pedro Sena

luBS,

Quando tento inserir, no meu xa-datasource a tag:

mySQL

Dá um erro no meu jboss. Por isso estou usando sem essa opção. Fiz as adequações no datasource e no hibernate mas o erro persiste.

Você pode dar um exemplo de algum método onde vc está usando a capacidade de XA transactions? Acredito que o problema esteja em meu método…

Só para tirar a dúvida.

Grato

Mostra ai seu método e também como vc esta fazendo begin e commit na transação.

Opa,

Consegui resolver aqui.

Meu problema era o seguinte:

Eu acessava 2 schemas diferentes do banco(O user tem permissão para tal).

Isso funciona bem para select(consigo dar um join cross-schema) mas nao funciona pra insert,update,delete(claro, transação cross-schema não rola sem XA).

O que eu fiz:

Agora tenho 2 tipos de DAOs, os que puxam do banco do cliente, e os que puxam do banco global.(Obviamente global tem suas entidades e local tem outras)

Agora tudo está funcionando corretamente.

Obrigado pela ajuda,

Pedro Sena

Pessoal, tô aproveitando este tópico pq o meu problema tem a ver com o assunto.

Eu tô usando o JBoss AS 5.1GA. Tentei criar dois data sources usando mas estou com um problema estranho.
Por algum motivo, o Eclipse está reclamando da estrutura do arquivo ds.xml

Eu estou colocando em anexo o printscreen do eclipse com a mensagem de erro.
O Curioso é que eu peguei um exemplo da página da JBoss e o erro é o mesmo.
Existe alguma coisa que estou vacilando ou é algum bug do eclipse? (Eu estou usando o plugin JBoss Tools 3)

Eu tentei acessar a DTD pelo browser mas houve um erro no parser do documento…

Aqui esta a estrutura completa do arquivo ds.xml

[code]<?xml version="1.0" encoding="UTF-8"?>

<xa-datasource>
	<jndi-name>ExemploSeamDatasource2</jndi-name>
	<track-connection-by-tx />
	<isSameRM-override-value>false</isSameRM-override-value>
	<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
	<xa-datasource-property name="URL">jdbc:mysql://localhost:3306/exemploseam2</xa-datasource-property>
	<xa-datasource-property name="User">xxx</xa-datasource-property>
	<xa-datasource-property name="Password">xxx</xa-datasource-property>
	<min-pool-size>5</min-pool-size>
	<max-pool-size>100</max-pool-size>
	<blocking-timeout-millis>5000</blocking-timeout-millis>
	<idle-timeout-minutes>15</idle-timeout-minutes>
	<metadata>
		<type-mapping>mySQL</type-mapping>
	</metadata>
</xa-datasource>

<xa-datasource>
	<jndi-name>GenericXADS</jndi-name>
	<xa-datasource-class>[fully qualified name of class implementing
		javax.sql.XADataSource goes here]</xa-datasource-class>
	<xa-datasource-property name="SomeProperty">SomePropertyValue</xa-datasource-property>
	<xa-datasource-property name="SomeOtherProperty">SomeOtherValue</xa-datasource-property>

	<user-name>x</user-name>
	<password>y</password>
	<transaction-isolation>TRANSACTION_SERIALIZABLE</transaction-isolation>

	<!--pooling parameters-->
	<min-pool-size>5</min-pool-size>
	<max-pool-size>100</max-pool-size>
	<blocking-timeout-millis>5000</blocking-timeout-millis>
	<idle-timeout-minutes>15</idle-timeout-minutes>
	<!--
		sql to call when connection is created <new-connection-sql>some
		arbitrary sql</new-connection-sql>
	-->

	<!--
		sql to call on an existing pooled connection when it is obtained from
		pool <check-valid-connection-sql>some arbitrary
		sql</check-valid-connection-sql>
	-->

	<!-- pooling criteria.  USE AT MOST ONE-->
	<!--
		If you don't use JAAS login modules or explicit login
		getConnection(usr,pw) but rely on user/pw specified above, don't
		specify anything here
	-->

	<!-- If you supply the usr/pw from a JAAS login module -->
	<security-domain />

	<!--
		if your app supplies the usr/pw explicitly getConnection(usr, pw)
	-->
	<application-managed-security />

</xa-datasource>
[/code]

Outra dúvida: O fato de estar usando XA requer que o arquivo tenha o nome no formato XXX-xa-ds.xml? Ou posso deixar como XXX-ds.xml?

Obrigado!