Problema com rollback usando @Transactional (VRaptor+Spring+Hibernate+Atomikos+Postgres)

40 respostas
M

Prezados,

No projeto em que trabalho, tive a necessidade de configurar 2 DataSources diferentes, pois preciso acessar duas bancos de dados diferentes. Achei uma solução com o Atomikos e agora consigo obter os dois sem problemas. O problema agora está no controle de transação do Spring com o @Transactional.

Tenho meu Controller do VRaptor com um método salvar que preciso que seja transacionado, pois ele executa uma série de operações de inserção no banco de dados e se ocorrer algum erro, deve ser realizado Rollback. O problema é que o Rollback não acontece.

Meu applicationContext.xml:

<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-3.0.xsd
			http://www.springframework.org/schema/tx
			http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

	<tx:annotation-driven />

	<bean id="dataSourceUm" class="com.atomikos.jdbc.AtomikosDataSourceBean">
		<property name="uniqueResourceName"><value>dataSourceUm</value></property>
		<property name="xaDataSourceClassName"><value>org.postgresql.xa.PGXADataSource</value></property>
		<property name="xaProperties">
			<props>
				<prop key="user">${db1.usuario}</prop>
				<prop key="password">${db1.senha}</prop>
				<prop key="databaseName">${db1.nomebanco}</prop>
				<prop key="serverName">${db1.url}</prop>
				<prop key="portNumber">${db1.porta}</prop>
			</props>
		</property>
		<property name="maxPoolSize" value="10" />
		<property name="minPoolSize" value="5" />
		<property name="maxIdleTime" value="1200" />
		<property name="testQuery" value="select 1" />
		<property name="reapTimeout" value="0" />
	</bean>

	<bean id="dataSourceDois" class="com.atomikos.jdbc.AtomikosDataSourceBean">
		<property name="uniqueResourceName"><value>dataSourceDois</value></property>
		<property name="xaDataSourceClassName"><value>org.postgresql.xa.PGXADataSource</value></property>
		<property name="xaProperties">
			<props>
				<prop key="user">${db2.usuario}</prop>
				<prop key="password">${db2.senha}</prop>
				<prop key="databaseName">${db2.nomebanco}</prop>
				<prop key="serverName">${db2.url}</prop>
				<prop key="portNumber">${db2.porta}</prop>
			</props>
		</property>
		<property name="maxPoolSize" value="10" />
		<property name="minPoolSize" value="5" />
		<property name="maxIdleTime" value="1200" />
		<property name="testQuery" value="select 1" />
		<property name="reapTimeout" value="0" />
	</bean>

	<bean id="sessionFactoryUm" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
			<property name="dataSource">
					<ref bean="dataSourceUm" />
			</property>
			<property name="annotatedClasses">
				<list>
					//Classes anotadas...
				</list>
			</property>
			<property name="hibernateProperties">
				<props>
					<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
					<!-- atomikos -->
					<prop key="hibernate.current_session_context_class">jta</prop>
					<prop key="hibernate.transaction.factory_class">
						com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory
					</prop>
					<prop key="hibernate.transaction.manager_lookup_class">
							com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
					</prop>
				</props>
			</property>
	</bean>

	<bean id="sessionFactoryDois" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
			<property name="dataSource">
					<ref bean="dataSourceDois" />
			</property>
			<property name="annotatedClasses">
				<list>
					//Classes anotadas...
				</list>
			</property>
			<property name="hibernateProperties">
				<props>
					<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
					<!-- atomikos -->
					<prop key="hibernate.current_session_context_class">jta</prop>
					<prop key="hibernate.transaction.factory_class">
						com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory
					</prop>
					<prop key="hibernate.transaction.manager_lookup_class">
							com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
					</prop>
				</props>
			</property>
	</bean>

	<bean id="um" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactoryUm"/>
	</bean>
	 
	<bean id="dois" class="org.springframework.orm.hibernate3.HibernateTemplate">
			<property name="sessionFactory" ref="sessionFactoryDois"/>
	</bean>

	<bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
        <property name="forceShutdown" value="true" />
		<!-- this prop is in seconds -->
        <property name="transactionTimeout" value="300"/>
	</bean>
	 
	<bean id="AtomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
		<!-- this prop is in seconds -->
        <property name="transactionTimeout" value="300" />
	</bean>
	 
	<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="AtomikosTransactionManager" />
        <property name="userTransaction" ref="AtomikosUserTransaction" />
        <property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
	</bean>

</beans>

Meu DAO:

@Component
public class MeuDaoImpl implements MeuDao {

	@Resource(name = "um")
	private HibernateTemplate hibernateTemplate;
	
	public void salvar(Entidade entity) {
		hibernateTemplate.save(entity);
	}
	
	//...
	
}

Com essa estratégia, consigo obter um ou outro ( "um" ou "dois" ) HibernateTemplate definido no applicationContext.xml, neste caso estou obtendo o "um".

Meu Controller está anotado com @Resource do VRaptor. Meu Controller herda de outra classe (quer herda de outra) mas possui um construtor vazio. O método salvar:

@Autowired
private MeuDao meuDao;

@Post
@Transactional
public void salvar(Entity entity)  throws Exception {
		//Processamento fictício, apenas para testar o Rollback!
		for (int i = 0; i < 2; i++) {
			meuDao.salvar(entity);
		}
		throw new Exception("Forçando erro para testar o Rollback...");	
}

Porém, o rollback não acontece, os dados são persistidos no banco.

Alguém sabe o que posso estar fazendo errado?

Grato pela atenção!

PS.: Tecnologias usadas: VRaptor + Spring + Hibernate + Atomikos + Postgres

40 Respostas

M

Fiz um teste e algo estranho aconteceu. Retirei o @Transactional do método e coloquei na classe: @Transactional(rollbackFor = Exception.class)

Dos 3 registros inseridos, apenas 1 restou. Ele deu rollback em duas inserções, mas uma restou. Estranho.

M

Olá Marcus,

É o seguinte, pelo que estou vendo esta tendo um problema de indentificação da transaction a ser usada pelo proxy do Spring.
Quando você adiciona a um método o @Transaction, você diz ao Spring que neste momento ele deve criar uma transação, assim
quando ele chamar o metodo do seu DAO essa transação já terá sido criada.
Para você informar ao proxy do Spring que ao iniciar o metodo savar no serviço ele deve usar uma ou outra sessionFactory você tem
que dizer o seguinte:

@Transactional(value = "txManagerUm")

Para você identificar suas session você tem que adicionar um qualifier que ficaria assim:

<bean id="um" class="org.springframework.orm.hibernate3.HibernateTemplate">  
           <property name="sessionFactory" ref="sessionFactoryUm"/>  
            <qualifier value="txManagerUm"/>
    </bean>

Desta forma você diz ao Spring o que ele tem que usar na propagação!

abraço!

M

mmaico,

Obrigado por responder!

Cara, tentei da forma como você falou, mas continuo sem conseguir dar o rollback. Todos os registros continuam sendo salvos…

Tentei também colocar o hibernate.connection.autocommit=false mas também não deu resultado.

Grato pela atenção!

M

Marcus,
Se isso não resolveu eu acredito que quem deve estar comitando essa transação é essa api que você adicionou, dê uma olhada nela e vê como ela controla as trasações.
Um teste rapido que você pode fazer é comentar o AtomikosTransactionManager e as linhas :

<prop key="hibernate.transaction.factory_class">  
                        com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory  
                    </prop>  
                    <prop key="hibernate.transaction.manager_lookup_class">  
                            com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup  
                    </prop>

Adicione ou altere o seu annotation driven para <tx:annotation-driven /> <- não especifica o trasaction manager.

Ai tenta novamente faz os testes. Eu usava desta forma em um projeto que tinha 3 base de dados(isso mesmo 3 :frowning: )

abraço!

M

Tentei também da seguinte forma:

Adicionei:

<tx:annotation-driven transaction-manager="transactionManager1"/>
<tx:annotation-driven transaction-manager="transactionManager2"/>

E

<bean id="transactionManager1" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="AtomikosTransactionManager" />
        <property name="userTransaction" ref="AtomikosUserTransaction" />
        <property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
	</bean>
	
	<bean id="transactionManager2" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="AtomikosTransactionManager" />
        <property name="userTransaction" ref="AtomikosUserTransaction" />
        <property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
	</bean>

Ficando com:

<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-3.0.xsd
			http://www.springframework.org/schema/tx
			http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

	<tx:annotation-driven transaction-manager="transactionManager1"/>
	<tx:annotation-driven transaction-manager="transactionManager2"/>

	<bean id="dataSourceUm" class="com.atomikos.jdbc.AtomikosDataSourceBean">
		<property name="uniqueResourceName"><value>dataSourceUm</value></property>
		<property name="xaDataSourceClassName"><value>org.postgresql.xa.PGXADataSource</value></property>
		<property name="xaProperties">
			<props>
				//props...
			</props>
		</property>
		<property name="maxPoolSize" value="10" />
		<property name="minPoolSize" value="5" />
		<property name="maxIdleTime" value="1200" />
		<property name="testQuery" value="select 1" />
		<property name="reapTimeout" value="0" />
	</bean>

	<bean id="dataSourceDois" class="com.atomikos.jdbc.AtomikosDataSourceBean">
		<property name="uniqueResourceName"><value>dataSourceDois</value></property>
		<property name="xaDataSourceClassName"><value>org.postgresql.xa.PGXADataSource</value></property>
		<property name="xaProperties">
			<props>
				//props...
			</props>
		</property>
		<property name="maxPoolSize" value="10" />
		<property name="minPoolSize" value="5" />
		<property name="maxIdleTime" value="1200" />
		<property name="testQuery" value="select 1" />
		<property name="reapTimeout" value="0" />
	</bean>

	<bean id="sessionFactoryUm" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
			<property name="dataSource">
					<ref bean="dataSourceUm" />
			</property>
			<property name="annotatedClasses">
				<list>
					//...
				</list>
			</property>
			<property name="hibernateProperties">
				<props>
					<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
					<!-- atomikos -->
					<prop key="hibernate.current_session_context_class">jta</prop>
					<prop key="hibernate.transaction.factory_class">
						com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory
					</prop>
					<prop key="hibernate.transaction.manager_lookup_class">
							com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
					</prop>
				</props>
			</property>
	</bean>

	<bean id="sessionFactoryDois" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
			<property name="dataSource">
					<ref bean="dataSourceDois" />
			</property>
			<property name="annotatedClasses">
				<list>
					//...
				</list>
			</property>
			<property name="hibernateProperties">
				<props>
					<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
					<!-- atomikos -->
					<prop key="hibernate.current_session_context_class">jta</prop>
					<prop key="hibernate.transaction.factory_class">
						com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory
					</prop>
					<prop key="hibernate.transaction.manager_lookup_class">
							com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup
					</prop>
				</props>
			</property>
	</bean>

	<bean id="um" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactoryUm"/>
		<qualifier value="transactionManager1"/>
	</bean>
	 
	<bean id="dois" class="org.springframework.orm.hibernate3.HibernateTemplate">
			<property name="sessionFactory" ref="sessionFactoryDois"/>
			<qualifier value="transactionManager2"/>
	</bean>

	<bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
        <property name="forceShutdown" value="true" />
		<!-- this prop is in seconds -->
        <property name="transactionTimeout" value="300"/>
	</bean>
	 
	<bean id="AtomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
		<!-- this prop is in seconds -->
        <property name="transactionTimeout" value="300" />
	</bean>
	 
	<bean id="transactionManager1" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="AtomikosTransactionManager" />
        <property name="userTransaction" ref="AtomikosUserTransaction" />
        <property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
	</bean>
	
	<bean id="transactionManager2" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="AtomikosTransactionManager" />
        <property name="userTransaction" ref="AtomikosUserTransaction" />
        <property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
	</bean>

</beans>

Adicionei também os qualifiers.

O Método:

@Post
	@Transactional("transactionManager1")
	public void adicionar(Material material)  throws Exception {  
			for (int i = 0; i < 2; i++) {
				materialDao.salvar(material);
			}
                        throw new Exception("Forçando erro para testar o Rollback...");   
	}
M

mmaico,

Vou tentar e dou o retorno.

Obrigado!

M

Achei um tópico no fórum da Atomikos que fala sobre isso:

http://fogbugz.atomikos.com/default.asp?community.6.2117.7

Porém não foi dada nenhuma solução. Tenso.

M

Outra possibilidade é configurar o JTA direto no seu web container, isso se estiver usando JBoss, Websphere, WebLogic e cia.

M

Infelizmente preciso usar o TomCat, por isso o Atomikos. =(

M

mmaico,

Pesquisando mais, percebi que não há necessidade de utilizar o Atomikos, pois ele seria necessário apenas se eu quisesse realizar operações em bancos diferentes na mesma transação. Simplifiquei um pouco minha vida. =)

Só que ocorre um problema estranho, se eu inserir 10 registros na base e forçar o rollback, ele continua com 1 registro inserido, dando rollback em 9. Se eu inserir 5 e forçar o rollback, ele continua com 1 salvo e dá rollback como se fosse uma regra de X - 1 rollbacks.

Fiz um teste usando o Change Value do Eclipse. Adicionei 3 entidades com nomes: ENTIDADE1, ENTIDADE2, ENTIDADE3 (modificando os nomes na hora de inserir). Na quarta, forcei o erro. Ele inseriu a ENTIDADE3.

Tem alguma ideia do que pode ser?

Minha classe Controller está anotada com:

@Transactional("txManagerUm")

Meu método está anotado com:

@Transactional(value="txManagerUm", readOnly=false, propagation=Propagation.REQUIRES_NEW, rollbackFor={Exception.class})

Meu novo applicationContext.xml:

<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-3.0.xsd
			http://www.springframework.org/schema/tx
			http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

	<tx:annotation-driven /> 

	<bean id="dataSourceUm" class="org.apache.commons.dbcp.BasicDataSource">
	    <property name="driverClassName" value="org.postgresql.Driver" />
	    <property name="url" value="jdbc:postgresql://.../base1" />
	    <property name="username" value="postgres" />
	    <property name="password" value="postgres" />
	    <property name="initialSize" value="5" />
	    <property name="maxActive" value="5" />
	</bean>
	
	<bean id="dataSourceDois" class="org.apache.commons.dbcp.BasicDataSource">
	    <property name="driverClassName" value="org.postgresql.Driver" />
	    <property name="url" value="jdbc:postgresql://.../base2" />
	    <property name="username" value="postgres" />
	    <property name="password" value="postgres" />
	    <property name="initialSize" value="5" />
	    <property name="maxActive" value="5" />
	</bean>

	<bean id="sessionFactoryUm" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
	    <property name="dataSource" ref="dataSourceUm" />
	    <property name="annotatedClasses">
	        <list>
	        		...
	        </list>
	    </property>
	    <property name="hibernateProperties">
	        <props>
	            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
	            <prop key="hibernate.show_sql">true</prop>
	        </props>
	    </property>
	</bean>

	<bean id="sessionFactoryDois" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
	    <property name="dataSource" ref="dataSourceDois" />
	    <property name="annotatedClasses">
	        <list>
					...
	        </list>
	    </property>
	    <property name="hibernateProperties">
	        <props>
	            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
	            <prop key="hibernate.show_sql">true</prop>
	        </props>
	    </property>
	</bean>

	<bean id="txManagerUm" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    	<property name="sessionFactory" ref="sessionFactoryUm"/>
	</bean>
	
	<bean id="txManagerDois" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    	<property name="sessionFactory" ref="sessionFactoryDois"/>
	</bean>
	
	<bean id="sispes" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactoryUm"/>
		<qualifier value="txManagerUm"/>
	</bean>
	 
	<bean id="sss" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactoryDois"/>
		<qualifier value="txManagerDois"/>
	</bean>

</beans>

Mais uma vez grato pela atenção!

M

Marcus,
Você esta certo, quando se fala de JTA se pensa em remoção do controle trasacional do banco e controle do mesmo na aplicação para situações que você citou.

Esse comportamento que você mencionou é muito estranhooo!!!

No seu DAO tem alguma anotação de transação?

Se tiver um @Trasacional na classe ou no metodo ele irá pegar a transação criada no seu controller, se ele tiver um requires_new ai ele irá criar uma nova transação e o seu rollback
no controller não fará efeito nele.

abraço!

L

esse comportamento de salvar X vezes e só manter 1 é por causa do seu código:

for (int i = 0; i < 2; i++) {  
    meuDao.salvar(entity);  
}

vc tá salvando o mesmo objeto, dentro da mesma transação… o hibernate vai considerar como um save só…

vc teria que testar com vários objetos diferentes…

desconfio que as transações estão desabilitadas no banco… se vc não usa o Atomikos funciona?

M

Opaaa, nesse caso não deveria salvar nenhuma vez, porque tem uma exception no final do metodo, então ele deveria fazer o rollback de tudo :slight_smile:

L

sim, supondo que o método está participando da transação…

dúvida: como vc está pegando a Session/EntityManager?

M

Lucas, pego da seguinte forma:

@Component  
public class MeuDaoImpl implements MeuDao {  
  
    @Resource(name = "um")  
    private HibernateTemplate hibernateTemplate;  
      
    public void salvar(Entidade entity) {  
        hibernateTemplate.save(entity);  
    }  
      
    //...  
      
}

Estou usando o HibernateTemplate, que referencia o:

<bean id="um" class="org.springframework.orm.hibernate3.HibernateTemplate">  
        <property name="sessionFactory" ref="sessionFactoryUm"/>  
    </bean>

Teria algum problema com esta abordagem?

M

Lucas, mas quando não lanço a exceção, ele salva todas as vezes os N objetos, mesmo sendo o mesmo.

L

depende… precisa ver se a operação está de fato participando da mesma transação que o @Transactional… senão estiver está certo não fazer o rollback…

to achando que a configuração do TransactionManager não está feita corretamente.

M

Detalhe: uso Tomcat.

L

o tomcat não deveria influenciar nisso, vc tá usando o spring…

o mesmo código, mas com um banco só, sem o Atomikos , funciona?

M

Não cheguei a testar com um banco só…

Inicialmente eu estava seguindo o guia de integração que é mostrado na apostila FJ-28 da Caelum, mas como precisei dos dois datasources, acabei me desviando.

L

teste isso por favor.

M

Beleza, vou testar e digo o resultado!

Obrigado pela atenção! =D

M

Lucas e mmaico,

Com um só transaction manager o rollback funciona tranquilamente.

applicationContext.xml:

<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-3.0.xsd
			http://www.springframework.org/schema/tx
			http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

	<tx:annotation-driven /> 

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
	    <property name="driverClassName" value="org.postgresql.Driver" />
	    <property name="url" value="jdbc:postgresql://.../base" />
	    <property name="username" value="postgres" />
	    <property name="password" value="postgres" />
	    <property name="initialSize" value="5" />
	    <property name="maxActive" value="5" />
	</bean>

	<bean id="template" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactory"/>
	</bean>

	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
	    <property name="dataSource" ref="dataSource" />
	    <property name="annotatedClasses">
	        <list>
					...
	        </list>
	    </property>
	    <property name="hibernateProperties">
	        <props>
	            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
	            <prop key="hibernate.show_sql">true</prop>
	        </props>
	    </property>
	</bean>

	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    	<property name="sessionFactory" ref="sessionFactory"/>
	</bean>

</beans>

E na minha classe a única coisa que fiz foi anotar o método com @Transactional e forçar um nullpointer.

L

então é alguma configuração do atomikos que está errada… não sei se eu consigo ajudar pq nunca mexi com ele…

tenta ir adicionando ele aos poucos até ver onde começa a aparecer o erro

M

Felizmente tirei o Atomikos de questão. =)

Como nesta fase do projeto iremos apenas fazer inserções em um dos bancos (o outro será apenas consulta), tentei o seguinte:

<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-3.0.xsd
			http://www.springframework.org/schema/tx
			http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

	<tx:annotation-driven /> 

	<bean id="dataSourceUm" class="org.apache.commons.dbcp.BasicDataSource">
	    <property name="driverClassName" value="org.postgresql.Driver" />
	    <property name="url" value="jdbc:postgresql://.../banco1" />
	    <property name="username" value="postgres" />
	    <property name="password" value="postgres" />
	    <property name="initialSize" value="5" />
	    <property name="maxActive" value="5" />
	</bean>

	<bean id="dataSourceDois" class="org.apache.commons.dbcp.BasicDataSource">
	    <property name="driverClassName" value="org.postgresql.Driver" />
	    <property name="url" value="jdbc:postgresql://.../banco2" />
	    <property name="username" value="postgres" />
	    <property name="password" value="postgres" />
	    <property name="initialSize" value="5" />
	    <property name="maxActive" value="5" />
	</bean>

	<bean id="dois" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactoryDois"/>
	</bean>

	<bean id="um" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactoryUm"/>
	</bean>
	
	<bean id="sessionFactoryDois" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
	    <property name="dataSource" ref="dataSourceDois" />
	    <property name="annotatedClasses">
	        <list>
					...
	        </list>
	    </property>
	    <property name="hibernateProperties">
	        <props>
	            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
	            <prop key="hibernate.show_sql">true</prop>
	        </props>
	    </property>
	</bean>

	<bean id="sessionFactoryUm" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
	    <property name="dataSource" ref="dataSourceUm" />
	    <property name="annotatedClasses">
	        <list>
	        		...
	        </list>
	    </property>
	    <property name="hibernateProperties">
	        <props>
	            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
	            <prop key="hibernate.show_sql">true</prop>
	        </props>
	    </property>
	</bean>

	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    	<property name="sessionFactory" ref="sessionFactoryUm"/>
	</bean>

</beans>

Mas não obtive sucesso, ele dá o commit de cara. D=

M

Marcus,

O que acontece se você trocar o seu hibernate template injetado no seu dao por uma session factory, assim:

@Autowired
	@Qualifier("um")
	protected  SessionFactory sessionFactory;

de depois você fazer um this.sessionFactory.getCurrentSession().save(objeto), o getCurrentSession sempre deve ser chamado, nunca o getSession().

Depois me fala o que aconteceu!

L

se vc usa desse jeito, só a sessionFactoryUm participa da transação, pq só ela tá no transactionManager.

M

Pra esse DAO sim, mas a idéia é ver se existe alguma outra transação sendo propagada!

Outra questão é verificar se os metodos de persistencia deste Hibernate Template esta usando o getCurrentSession e não o getSession

M

A ideia é que a apenas a sessionFactoryUm participe da transação por enquanto. Vou fazer o teste com a sessionFactory e dou um retorno.

Grato!

M

mmaico,

Ao tentar acessar o sessionFactory.getCurrentSession(), lança a seguinte exceção:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

M

Rapaz, fiz um teste em outro Controller, só com um transactionManager e não funcionou. Será que o problema está no meu Controller?

ß) O Spring transactional usa o Spring AOP para gerenciar as transações. O Spring AOP tem uma restrição
forte: para poder decorar suas classes ele usa proxies da Cglib. Esses proxies precisamque sua classe tenha
um construtor sem argumentos, ou que as dependências da sua classe sejam sempre interfaces. E como
programar voltado a interfaces é uma boa prática, pois diminui o acoplamento de uma classe comas suas
dependências, vamos optar pela segunda solução.

Esse Controller problemático extende de uma classe que extende de outra. Seria algum problema nesse sentido? O Controller que funciona não extende de ninguém.

M

O problema não é a herança, fiz um teste no Controller que funciona, adicionei uma hierarquia e o funcionamento continunou correto. Estranho.

M

Uma dúvida, pq isso não funciona?

class Classe {

    public void metodo1() {
        try {
             metodo2();
        catch (Exception e) {
               //...
        }
    }

    @Transactional
    public void metodo2() {
         //Processamento que lança exceção
    }

}

O escopo da transação não deveria ser o correto funcionamento (ou não) do metodo2()? Pq capturar a exceção no metodo1() influencia no rollback? Se eu tirar o try/catch o rollback funciona.

Grato!

L

depende da exceção lançada… tenta colocar o transactional num @Component e chamá-lo do controller (de preferencia via interface)…

outra coisa: vc configurou o component-scan com o pacote correto?

M

Mas meus @Component são DAOs, não faria muito sentido os @Transactional nos DAOs.

O component-scan está correto.

Uma dúvida: Como farei para tratar as exceções se apenas é realizado o rollback quando eu lanço a exceção pro usuário?

L

claro que faz sentido ter @Transactional nos DAOs… principalmente nos métodos que modificam coisas…

se um método que é @Transactional chama outro que tb é, só uma transação é feita, pode usar sem medo…

faz o teste com o @Transactional no DAO (e não no controller)

M

Antes todos os @Transactional estavam nos DAOs, mas um método no DAO só realiza uma operação, por exemplo:

@Transactional public void save(Entity entity) { hibernateTemplate.save(entity); }

O @Transactional não faz mais sentido na camada superior?

@Transactional public void processamento() { operacao1(); operacao2(); operacao3(); }

Então dando erro, por exemplo, na operacao3(), ele daria rollback em tudo que fez anteriormente.

L

vc pode ter o transactional nas duas camadas, ele só considera a transação mais externa, por padrão. Colocando no dao vc garante que todos os saves rodam dentro de uma transaction.

M

Pessoal,

Para entender isso tem que conhecer como o Spring trabalha, e ele faz o seguinte:
Se estiver um @Trasaction no controller ele irá iniciar a transação no controller e propagar para o DAO, quando o metodo no controller finalizar é onde o Spring irá finalizar aquela transação.

Se você colocar um @Transaction no DAO, neste caso não irá impactar em nada porque o perfil transacional default é required, nesse perfil ele usa a transação que  foi propagada do controller
 e não cria uma nova. Se você colocasse um @ Transaction com Required New no DAO aiii sim, iria ter 2 transações uma iniciada no controller e outra iniciada no DAO.


Resumo,  @Transaction no DAO não fará efeito algum nesse caso, fará quando um serviço sem transação o chamar como ele irá ver que não existe uma transação corrente ele irá iniciar uma nova.
M

Estou dando uma lida no Spring in Action pra entender melhor esse comportamento.

mmaico e Lucas, muito obrigado pelo apoio! :smiley:

Criado 22 de novembro de 2012
Ultima resposta 23 de nov. de 2012
Respostas 40
Participantes 3