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