Olá Pessoal,
No post de hoje, veremos como integrar Spring com Hibernate. Ou seja, como podemos deixar tudo ao comando do Spring? Deixar ele abrir ,fechar, commit , dar roll back nas transações? Diretamente o Spring não faz isso, mas ele delega para um cara que pode fazer isso. A moral da história que ao invés de nós programadores ter que dar beginTransaction, isso serrá feito nos bastidores. No exemplo a seguir fazeremos apenas um insert no banco de dados.
Lets go…
Requisitos
Antes de mais nada certifique que vc tem os .jars baixados:
- – Mysql 5 .x
- – Hibernate 3.6
- – Spring 3.x
- – AOP 1.x
- – AspectJ
- – commons-logging 1.x
Depois de garantir que você tem todos os .jars mãos na massa agora.
Starting…
Primeiro passo é que nosso exemplo será um Java Application, o objetivo é fazer a integração de maneira mais simples e funcional possível. Então crie seu Java Project e deixe conforme a imagem a seguir:
No final teremos os seguintes packages e classes:
Por onde começar?
Eu gosto sempre de iniciar pela parte de configuração, assim quando vou começar o desenvolvimento não preciso voltar para configurar, exceto quando é algo bem pontual. Então, iniciaremos pelo arquivo de configuração do Spring, no package config, crie um springconfiguration.xml.
Vou colocar o arquivo por partes e explicando, além disso coloquei uns comentários no .xml para facilitar o entendimento:
<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: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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd”>
Se vc vem acompanhando os outros posts e já conhece o básico do Spring deve conhecer o beans. A única coisa de novo que adicionamos aqui foi o xmlns tx para as transaction, que veremos mais na frente a sua importância. Portanto deixe seu beans conforme o código acima.
<!– buscando os beans –>
<context:component-scan base-package=“*” />
<!– transaction via annotations –>
<tx:annotation-driven />
<!– Translate exception –>
<bean class=“org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor”/>
Aqui temos um cara novo que é o PersistenceExceptionTransalationPostProcessor que é responsável por “traduzir” as exceções checked em unchecked do Spring, assim não precisamos ficar tratando na nossa application. Habilitamos que as transaction serão via annotation, ou seja, o Spring vai procurar pela annotation @Transaction. E também informamos que todo bean do Spring será via annotation e base de package colocamos todos.
DataSource
Agora precisamos criar um DataSource assim vamos dizer a que banco estaremos conectado em qual url e com o usuário xxxx, com a senha yyyyy.
<bean id=“dataSource” class=“org.springframework.jdbc.datasource.DriverManagerDataSource”>
<property name=“driverClassName” value=“com.mysql.jdbc.Driver”/>
<property name=“url” value=“jdbc:mysql://localhost/test”/>
<property name=“username” value=“root”/>
<property name=“password” value=“bahia”/>
</bean>
SessionFactory
Agora precisamos configurar nossa sessionFactory, nesse caso estamos dizendo que vamos usar annotation para persistência. Lembra que fazíamos isso diretamente no Hibernate na hora de criar a sessionFactory (new annotationXXX)? Aqui há alguns pontos importantes:
<bean id=“sessionFactory” class=“org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean”>
<!– informando o datasource, de onde vem a conexão? –>
<property name=“dataSource” ref=“dataSource”/>
<!– onde estão as classes com annotation? informando aqui –>
<property name=“packagesToScan” value=“br.com.camilolopes.model.bean”/>
<!– as configs do orm – hibernate –>
<property name=“hibernateProperties”>
<props>
<prop key=“hibernate.dialect”>org.hibernate.dialect.MySQL5InnoDBDialect”</prop>
<prop key=“hibernate.hbm2ddl.auto”>update</prop>
</props>
</property>
</bean>
Agora precisamos criar o cara que vai gerenciar as transaction no nosso caso como estamos usando o Hibernate será o HibernateTransactionManager:
<!– esse é o cara que gerencia as transactions –>
<bean id=“transactionManager” class=“org.springframework.orm.hibernate3.HibernateTransactionManager”>
<property name=“sessionFactory” ref=“sessionFactory”/>
</bean>
Pronto terminamos a parte de configuração. Vamos agora para parte Java
Development
No package dao. Criaremos uma interface. Nossa aplicação vai salvar um produto apenas.
public interface ProductDAO {
void save(ProductTech product);
}
Agora vem a classe que implementa a interface:
@Repository
public class ProductDAOImpl implements ProductDAO {
private SessionFactory sessionFactory;
@Autowired
public ProductDAOImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private Session currentSession(){
return sessionFactory.getCurrentSession();
}
@Override //para execução deste método é requerido uma transação, é isso que estamos dizendo aqui
@Transactional(propagation=Propagation.REQUIRED,readOnly=false)
public void save(ProductTech product) {
currentSession().save(product);
System.out.println(“product saved with sucess”);
}
}
E a nossa entidade:
@Entity
public class ProductTech implements Serializable{
@Id
@GeneratedValue
private Long id;
private String nameProduct;
//getters/setters ommitidos
Testando
Vamos criar uma classe com método main para testar:
public class MainTestDAO {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“config/springconfiguration.xml”);
ProductDAO bean = (ProductDAO) applicationContext.getBean(“productDAOImpl”);
ProductTech product= new ProductTech();
product.setNameProduct(“tablet samsung galaxy”);
bean.save(product);
}
}
Antes de rodar garanta que o Banco está rodando. Ao executar:
Fácil não? Nos preocupamos mais com a parte de negócio, toda aquela parte chata de transação por exemplo está sendo cuidada nos bastidores.
Bom, vou ficando por aqui. E espero que tenham gostado do post.
Abraços see ya!!
Olá Camilo, muito esclarecedor o seu post. Parabéns!
Eu possuo uma estrutura semelhante no meu projeto, no entanto, tenho um Facade onde os métodos são anotados com @Transacional. Meu Facade é injetado em ManagedBeans (do JSF) de onde invoco os métodos para realizar as transações de salvar, excluir, etc.
Estou com um cenário onde um método desse ManagedBean invoca várias vezes o mesmo método do Facade que fará a persistência de algumas entidades (JPA/Hibernate). Estou usando OpenSessionInView do Spring e preciso que as transações realizadas a partir desse método do MBean sejam atômicas. O commit esta ocorrendo sempre que o método transacional conclui, porém eu gostaria que o commit fosse realizado ao final da execução do método do MBean. Cheguei a anotar o método do MBean com @Transactional, mas não funcionou.
Sabes me dizer o que posso fazer para resolver esse problema?
Atenciosamente.
Seu MBean é gerenciado pelo Spring?
Não. É gerenciado pelo JSF. A injeção dos services nos Beans, eu faço utilizando ManagedProperty. Quanto a injeção dos DAO’s nos services, se dá pela utilização do Autowired. Digamos que 98% dos meu Beans possuem duas anotações básicas: @ManagedBean e @ViewScoped. Não sei se é relevante as informações abaixo, mais vou comentar, caso ajude:
A aplicação roda no JBoss 4.2.3, porém ela não usa a implementação JSF do servidor. Na época da criação da arquitetura padrão das aplicações, foi pego a implementação JSF 2.0 do JBoss 6 e colocado dentro da aplicação que esta configurada para usar a implementação que estiver dentro da própria aplicação, ignorando assim a implementação que esta no server. Resumindo, uso JSF 2.0 no JBoss 4.2.3 com Servlet 2.5.
Grato pela atenção
olá Jeremias,
obrigado por comentar, mas você está com dúvida? Não entendi direito.
Sim, estou com dúvidas. Preciso que as transações sejam atômicas, porém cada entidade de negócio, tem o seu Service que também tem o seu DAO. Ou seja, se tenho uma entidade chamado Cliente e outra chamado Endereco, quando preciso persistir Cliente, invoco ClienteService.save e quando preciso persistir Endereco, invoco EnderecoService.save.
O ponto é que, o método save de cada um dos services esta anotado com:
@Transactional(readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
Dessa forma, quando dentro do Bean XPTO ao ser clicado num botão Salvar, por exemplo, eu invoco o método salvar do Bean que realiza algumas validações e em seguida, invoca ClienteService.save e EnderecoService.save.
Se ocorrer um erro on método EnderecoService.save, a aplicação deveria fazer rollback do Endereco e do Cliente e isso, não acontece. O que percebi é que, a transação é aberta quando o método save inicia e é encerrada com commit, ao final do mesmo método, dessa forma, quando encerra a execução do save do Cliente, o registro já esta comitado na base de dados, não sendo possível rollback caso algum problema ocorra nos códigos que seguem a invocação desse método.
Esse é o meu problema e não sei como resolver isso.