Duvida @Transactional Spring

Boa noite pessoal, estou tendo um problema com o @Transactional do Spring e acredito que esteja relacionado a utilização de Generics onde eu anoto a classe filha com @Transactional porém a classe pai que irá realizar as operações que devem utilizar transações! Por exemplo: Quando eu crio uma classe de Serviço concreta que instância um Dao concreto, e esta classe de serviço está anotada com @Transactional e chama o método save() do Dao através do Serviço por exemplo, tudo corre bem.

Porém quando eu possuo uma classe filha PessoaService anotada com @Transactional que extende para uma Classe genéria que é responsável por recuperar o Dao e chamar o método, o erro acontece.

Classe filha:

@Service
@Transactional
public class PessoaService extends GenericService<Pessoa>{

}

Classe genérica:


public class GenericService<T extends Serializable> implements IGenericService<T> {
	
	private Class<T> typeClass;
	private GenericDao<T> dao;
	
	public GenericService(){
		typeClass = GenericUtils.getGenericTypeClass(getClass());
	}
	
	public T save(T obj) {
		return getDao().save(obj);
	}

	public void delete(Long id) {
		getDao().delete(id);
	}

	public void delete(T obj) {
		getDao().delete(obj);
	}

	public void delete(String filter) {
		getDao().delete(filter);
	}

	public void delete(FilterCriteria filter) {
		getDao().delete(filter);
	}

	public T update(T obj) {
		getDao().update(obj);
		return obj;
	}

	public T get(Long id) {
		return getDao().get(id);
	}

	public T get(String filter) {
		return getDao().get(filter);
	}

	public T get(FilterCriteria filter) {
		return getDao().get(filter);
	}

	public List<T> list(String filter) {
		return getDao().list(filter);
	}

	public List<T> list(FilterCriteria filter) {
		return getDao().list(filter);
	}
	
	public GenericDao<T> getDao(){
		if(dao == null){
			try {
				return GenericUtils.springContext.getBean(ClassUtils.getDaoOfModel(typeClass));
			} catch (BeansException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return dao;
	}
		
}

Exceção:

org.hibernate.HibernateException: save is not valid without active transaction
	at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:340)
	at $Proxy30.save(Unknown Source)

Oi djeffersonx,

você configurou o gerenciador de transações do Spring?

dica… aonde tiver processo de acesso a banco vc deve por o @Transactional

no seu caso só faltou por no GenericService, coloque nele que ja vai funcionar

Sim o Spring está configurado certinho, tanto que quando eu faço uma classe concreta ele funciona.
Eu já coloquei o @Transactional na GenericService, mas quando eu faço isso, não consigo mais recuperar o PessoaService no contexto do Spring, no caso o PessaService fica com @Service apenas, e o GenericService com o @Transactional, mas quando eu faço isso não consigo mais recuperar o PessoaService do contexto do spring, o Spring me diz que não tem esse Bean.

:confused:

To pensando em gerenciar as transações na mão memsmo, fazer un session.beginTransaction() e depois um commit, afinal é só isso uq o spring faz pra mim msm certo ?

Abrç, e obrigado pela atenção!

Nâo é mais fácil você simplesmente incluir as anotações na classe pai já de uma vez?

Tenho de verificar o código fonte do Spring: se bobear, ele só lê as anotações da classe filha ignorando os métodos da classe pai.

[quote=djeffersonx]Sim o Spring está configurado certinho, tanto que quando eu faço uma classe concreta ele funciona.
Eu já coloquei o @Transactional na GenericService, mas quando eu faço isso, não consigo mais recuperar o PessoaService no contexto do Spring, no caso o PessaService fica com @Service apenas, e o GenericService com o @Transactional, mas quando eu faço isso não consigo mais recuperar o PessoaService do contexto do spring, o Spring me diz que não tem esse Bean.

:confused:

To pensando em gerenciar as transações na mão memsmo, fazer un session.beginTransaction() e depois um commit, afinal é só isso uq o spring faz pra mim msm certo ?

Abrç, e obrigado pela atenção![/quote]

a implementação do seu GenericDao vc anotou com @Transactional ?
tira do GenericService a anotação…

Se eu coloco @Transactional no GenericDao, acontece a mesma coisa que acorre com o service, que ai o spring não recupera mais a classe filha, que no caso estaria anotada com @Repository tipo:

GenericDao:


@Transactional
public class GenericDao<T extends Serializable> implements IGenericDao<T>{
     [...]
}

PessoaDao:

@Repository
public class PessoaDao extends GenericDao<Pessoa>{
	
}

Desta forma quando eu tento recuperar o PessoaDao da fábrica do spring, ele diz que não existe nenhum bean registrado.

É uma má pratica eu relizar a abertura e fechamento da transação ? chamando o beginTransaction() e commit() ???

mas então eu faço assim…

no genericDao coloco @Transactional e nas filhas alem de colocar @Transactional adiciono o @Repository

exemplo :

@Transactional public abstract class GenericDAOImpl<T> implements GenericDAO<T>

@Repository("AeronaveDAO") @Transactional public class AeronaveDAOImpl extends GenericDAOImpl<Aeronave> implements AeronaveDAO

e no meu applicationContext, coloco:

[code]
<context:annotation-config />

<context:component-scan base-package=“meu.pacote” />

<tx:annotation-driven />[/code]

e funciona … tenta fazer algo assim… se nao funcionar posta seu applicationContext.xml

Tentei como você falou e o spring não localizou mais a minha classe filha.

segue o applicationContext

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
						   http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
						   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
						   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


	<context:component-scan base-package="idws.*"/>
	    
    <mvc:annotation-driven/>

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="src/main/webapp/WEB-INF/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"><value>org.postgresql.Driver</value></property>
        <property name="url"><value>jdbc:postgresql://localhost:5432/idws_service_1</value></property>
        <property name="username"><value>postgresql</value></property>
        <property name="password"><value>postgresql</value></property>
    </bean>
    
     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource">
        	<ref local="dataSource"/>
        </property>
        <property name="packagesToScan" value="idws.application.model" />
        <property name="hibernateProperties">
	        <props>
	            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
	            <prop key="hibernate.show_sql">true</prop>
	            <prop key="hibernate.format_sql">true</prop>
	            <prop key="hibernate.c3p0.idle_test_period">50</prop>  
				<prop key="hibernate.current_session_context_class">thread</prop>
				<prop key="hibernate.hbm2ddl.auto">create-drop</prop> 
	        </props>
        </property>
    </bean>

	<tx:annotation-driven />
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory"><ref local="sessionFactory"/></property>
    </bean>
</beans>

[quote=djeffersonx]Sim o Spring está configurado certinho, tanto que quando eu faço uma classe concreta ele funciona.
Eu já coloquei o @Transactional na GenericService, mas quando eu faço isso, não consigo mais recuperar o PessoaService no contexto do Spring, no caso o PessaService fica com @Service apenas, e o GenericService com o @Transactional, mas quando eu faço isso não consigo mais recuperar o PessoaService do contexto do spring, o Spring me diz que não tem esse Bean.

:confused:

To pensando em gerenciar as transações na mão memsmo, fazer un session.beginTransaction() e depois um commit, afinal é só isso uq o spring faz pra mim msm certo ?

Abrç, e obrigado pela atenção![/quote]

mas entao… vc precisa falar pro spring que a sua GenericService deve entrar no contexto… nao é para se colocar @transaction em serviço, só em dao’s de preferencia… anota a generic com @Service somente e faz igual eu falei anteriormente… e me diz se continua dando o erro

Fiz como você falou, coloquei na GenericService as anotações @Service e @Transactional apenas extendi a PessoaService para GenericService, porém desta forma o Spring não está registrando a classe PessoaService no contexto, pois deta forma ele está lançando a exception:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [idws.application.service.PessoaService] is defined: expected single bean but found 0: 

Existe alguma config no spring pra isso ?

E quando eu faço desta forma:

@Repository
@Transactional
public class PessoaDao extends GenericDao<Pessoa>{
	
}

E a generic:

[code]
public class GenericDao implements IGenericDao{…}

[code]

Da o erro de transação, dizendo que : save is not valid to (ALGUMA COISA) transaction…

Segue o applicationConfig.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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
						   http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
						   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
						   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


	<context:component-scan base-package="idws.*"/>
	    
    <mvc:annotation-driven/>

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="src/main/webapp/WEB-INF/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"><value>org.postgresql.Driver</value></property>
        <property name="url"><value>jdbc:postgresql://localhost:5432/idws_service_1</value></property>
        <property name="username"><value>postgresql</value></property>
        <property name="password"><value>postgresql</value></property>
    </bean>
    
     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource">
        	<ref local="dataSource"/>
        </property>
        <property name="packagesToScan" value="idws.application.model" />
        <property name="hibernateProperties">
	        <props>
	            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
	            <prop key="hibernate.show_sql">true</prop>
	            <prop key="hibernate.format_sql">true</prop>
				<prop key="hibernate.current_session_context_class">thread</prop>
				<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
				
				<prop key="hibernate.c3p0.min_size">5</prop>
			    <prop key="hibernate.c3p0.max_size">20</prop>
			    <prop key="hibernate.c3p0.timeout">300</prop>
			    <prop key="hibernate.c3p0.max_statements">50</prop>
			    <prop key="hibernate.c3p0.idle_test_period">3000</prop>
				 
	        </props>
        </property>
    </bean>

	<tx:annotation-driven />
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory"><ref local="sessionFactory"/></property>
    </bean>
</beans>

GenericDao:

public class GenericDao<T extends Serializable> implements IGenericDao<T>{

	public GenericDao(){
		
	}
	
	private Class<T> getTypeClass(){...}
	
	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory){
		this.sessionFactory = sessionFactory;
	}
	
	public SessionFactory getSessionFactory(){
		return this.sessionFactory;
	}
	
	public Session getSession(){
		return sessionFactory.getCurrentSession();
	}
	
	public T save(T obj){		
		return (T) getSession().save(obj);
	}
	
	public void delete(Long id){
		getSession().delete(get(id));
	}
	
	public void delete(T obj) {
		getSession().delete(obj);
	}
	
	public void delete(String filter) {
		List<T> lines = list(filter);
		Session session = getSession(); 
		for(T l : lines){
			session.delete(l);	
		}
	}
	
	public void delete(SearchCriteria filter) {
		List<T> lines = list(filter);
		Session session = getSession(); 
		for(T l : lines){
			session.delete(l);	
		}
	}

	public T update(T obj) {
		Session session = getSession();
		session.update(obj);
		return obj;
	}

	public T get(Long id) {
		return (T) getSession().get(getTypeClass(), id);
	}
	
	public T get(String filter) {
		return (T) configureCriteriaByFilter(filter).uniqueResult();
	}
	
	public T get(SearchCriteria filter) {
		return (T) loadFilterToCriteria(filter).uniqueResult();
	}
	
	public List<T> list(String filter) {
		return configureCriteriaByFilter(filter).list();
	}
	
	
	
}

PessoaDao


@Repository
@Transactional
public class PessoaDao extends GenericDao<Pessoa>{
	
}

GenericService e PessoaService seguem a mesma lógica!!