domingo, 22 de junho de 2008

Controle de Transação com Spring, JTA, JPA, Hibernate e Maven

Com o crescimento das aplicações que vêm sendo desenvolvidas um dos maiores problemas na estruturação de um projeto reside no controle de transações. Quando vários datasources entram na história o problema só aumenta. Segue abaixo uma proposta de solução para o controle de transação utilizando String, com JTA e Maven. As configurações abaixo necessitam de um container J2EE para funcionarem, o exemplo foi testado no JBoss 4.0.5ga. Com o uso do Spring o controle transacional se torna transparente, sendo então o problema resolvido. Segue tutorial abaixo:

Configurações do Maven

A configuração do Maven para o uso de tais ferramentas é simples.

Somente são necessárias algumas dependências e um repositório para busca-las.

Adicionando dependências

O seguinte trexo deve ser adicionado nas dependências do projeto no seu arquivo pom.xml.

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.3.1.ga</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.6.ga</version>
<exclusions>
   <exclusion>
        <groupId>javax.transaction</groupId> <!-- Exclusion to remove a classloader conflit with jta of JBoss -->
        <artifactId>jta</artifactId>
   </exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
Aqui estão listados todos os jars necessários para o uso das ferramentas mencionadas neste tutorial.

Adicionando repositório

O seguinte trexo deve ser adicionado nos repositórios do projeto para que todas as dependências sejam satisfeitas.

Tal repositório contém algumas das dependências do Hibernate.

<repository>
<id>maven-repository.dev.java.net</id>
<name>Java Dev Net Repository</name>
<url>http://download.java.net/maven/2/</url>
<releases>
     <enabled>true</enabled>
     <updatePolicy>never</updatePolicy>
</releases>
<snapshots>
    <enabled>false</enabled>
</snapshots>
</repository>

Utilização do Spring

Com a utilização do Spring podemos injetar o EntityManager do JPA no Dao, utilizando como provider o Hibernate.

O controle de transação se torna transparente para o desenvolvedor não sendo mais necessário que o mesmo inicie e finalize cada transação.

Vejamos utilizar o Spring no escopo proposto.

Configuração do applicationContext

Abaixo segue uma configuração padrão para o applicationContext da aplicação

<?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"
xmlns:jboss="http://www.springmodules.org/schema/jboss"
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="dao" scope="prototype"
class="br.com.product.project.commons.dao.DaoImpl">
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jpaProperties">
    <props>
         <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory
         </prop>
         <prop key="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup
         </prop>
    </props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager" >
<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>

No xml acima temos várias configurações, vamos detalhar uma por uma.

Injeção do Dao na Aplicação

No XML acima temos a declaração de um Bean chamado dao. Esse bean implementa o Dao que será utilizado na nossa aplicação.

Para utiliza-lo devemos fazer com que o Spring injete o mesmo na camada de Aplicação.

Como essa injeção deve ser feita foge o escopo desse tutorial.

Configuração do EntityManager do JPA

Vemos também no applicationContext configuração do EntityManagerFactory do JPA.

No property jpaVendorAdapter definimos o uso do Hibernate como provider do JPA.

Definimos também algumas propriedades do Hibernate.

Para que o EntityManager seja inicializado no Dao o seguinte fragmento de código deve existir no mesmo:

private EntityManager entityManager;
@PersistenceContext( type=PersistenceContextType.TRANSACTION, name="name" )
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}

Neste fragmento dizemos ao Spring que a variável entityManager vai receber uma instância criada pelo EntityManagerFactory.

Assim o Spring injeta o EntityManager no Dao.

Controle Transacional

No fim do applicationContext vemos a configuração do Controle de Transação.

Definimos o JpaTransactionManager como o controlador de transações, e logo abaixo definimos que o mesmo seja feito através de annotations.

Logo para definirmos quando um método deve ser executado em uma transação tudo que precisamos fazer é adicionar um annotation em cima do mesmo:

@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void someMethod(){
// do some operation in a transaction
}

O mesmo annotation pode ser adicionado em uma classe ou em uma interface, agindo assim sobre todos os metodos das mesmas.

Programando o DAO

Uma vez que possuímos o EntityManager injetado no Dao e o controle de transação já está definido a programação do Dao se torna muito simples.

Vejamos um exemplo de um método que insere ou atualiza uma Entidade.

public void save(T t) throws DataBaseException {
try{
   //Consideremos que todas as entidades passadas para esse metodo possuam o metodo getId
   Object id = t.getId();
   if( id != null ){
       entityManager.merge( t );
   else{
       entityManager.persist(t);
       entityManager.flush();
} catch (Exception e) {
   throw new DataBaseException( "Erro ao persistir objeto <" + t.getClass().getSimpleName() + ">", e  );
}
}

Mapeamento no Hibernate com Annotations

O mapeamento das entidades pode ser feito todo via annotation reduzindo o número de XML's para a configuração da aplicação.

O único XML necessário é o persistence.xml, padrão do JPA que explicita as características na Unidade de Persistencia.

Configuração do persistence.xml

Um exemplo de configuração do persistence.xml pode ser visto a seguir

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
     http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
     version="1.0">
<persistence-unit name="name" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/MySqlAccounter</jta-data-source>
    <class>br.com.product.project.domain.vo.Home</class>
    <class>br.com.product.project.domain.vo.Room</class>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
        <property name="hibernate.cache.use_second_level_cache" value="false" />
        <property name="hibernate.cache.use_query_cache" value="false" />
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.generate_ddl" value="true" />
    </properties>
</persistence-unit>
</persistence>

No arquivo configuramos a Unidade de persistencia que será usada pelo JPA.

Perceba que definimos um nome para a mesma, esse nome é o mesmo que deve ser adicionado sobre o entity manager no annotation que o injeta no Dao, mostrado no item Configuração do EntityManager do JPA.

A primeira configuração mostra que é o provider do JPA será o hibernate.

Logo em seguida é configurado o endereço do DataSource da base de dados que a Unidade de Persistência irá utilizar.

Em seguida são listadas todas as classes que queremos mapear para que o JPA possa encontrálas e iniciar o mapeamento das mesmas.

No exemplo o dialeto MySQLDialect é usado pois o mesmo se conectará a uma base de dados MySQL, tal variável deve ser alterada de acordo com o SGBD utilizado, também desabilitamos o segundo nível de cache e a cache de queries.

Annotations de Mapeamento

Uma vez que definimos quais classes queremos mapear, agora precisamos inserir metadados nas mesmas para que o mapeamento seja feito corretamente.

Para isso utilizamos os annotations da javax.persistence.*.

Se tivermos a seguinte tabela em nossa base de dados:

CREATE TABLE TAB_HOME (
id INT AUTO_INCREMENT PRIMARY KEY,
address VARCHAR(50)
);

Vejamos um exemplo de mapeamento para a mesma

package br.com.product.project.domain.vo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.GeneratedValue;

@Entity
@Table(name = "TAB_HOME" )
public class DomainEnumSubscriber {

@Id @GeneratedValue
@Column( name="id" )
private Integer id;

@Column( name="address" )
private String address;

public Integer getId(){
    return this.id;
}

public void setId( Integer id ){
    this.id = id
}

public String getAddress(){
    return this.address;
}

public void setAddress( String address ){
    this.address = address;
}
}

No código usamos o annotation @Entity para mostrarmos que a classe representa uma entidade.

Em seguida usamos o anntation @Table para mostrar qual tabela estamos mapeando.

O annotation @Id mostra que o atributo se referencia a uma chave primária. e o anntation @GeneratedValue que a mesma é gerada automaticamente pelo banco de dados.

Em seguida o annotation @Collumn serve para mapear qual a coluna que o atributo se referencia.

Existem várias anotações cada uma com seus atributos, veja mais na Documentação Oficial.

Mapeamento de Chaves Estrangeiras

Com o JPA podemos mapear as chaves estrangeiras das tabelas para que objetos já sejam inseridos dentro das entidades que as contém. Suponhamos as seguintes tabelas no banco de dados

CREATE TABLE TAB_HOME (
id INT AUTO_INCREMENT PRIMARY KEY,
address VARCHAR(50)
);

CREATE TABLE TAB_ROOM (
id INT AUTO_INCREMENT PRIMARY KEY,
home INT REFERENCES TAB_HOME( id ),
size INT,
);

Podemos mapear essa chave estrangeira de duas maneiras. Fazendo o Objeto Home ter uma lista de todos os comodos que possui (mapeamento do tipo OneToMany), ou fazendo cada comodo possuir a qual casa ele pertence (Mapeamento do tipo ManyToOne).

Mapeamento OneToMany

Para que possamos ter uma lista em uma entidade pai de todas as entidades filhas que a referenciam utilizamos o mapeamento OneToMany.

Para tanto só precisamos utilizar o annotation @OneToMany ao fazer o mapeamento de um atributo de uma das classes mapeadas.

Vejamos um exemplo:

//O atributo mappedby se referencia a uma instancia de Home dentro da classe Room com o nome home
@OneToMany( targetEntity=Room.class, mappedby="home" )
private Collection rooms;

Com isso ao criar o objeto o mesmo busca uma lista de todos os comodos que referenciam a ele de modo lazy.

Mapeamento ManyToOne

Para que possamos ter uma instância de um objeto ao qual uma chave estrangeira aponta utilizamos o mapeamento ManyToOne.

Para tanto só precisamos utilizar os annotations @ManyToOne e @JoinColumn ao fazer o mapeamento de um atributo de uma das classes mapeadas.

Vejamos um exemplo:

@ManyToOne( targetEntity=Home.class )
@JoinColumn( name="home" )
private Home home;

Com isso ao criar o objeto o mesmo busca o objeto ao qual a chave estrangeira se relaciona e o traz de modo lazy.

Mais pode ser encontrado em sites como o de referencia do Spring ou nesse tutorial sobre J2EE.

Por favor, se você leu esse material deixa sua crítica, comentário e sujestões de melhoras

[]'s e até a proxima

sábado, 21 de junho de 2008

Olá ninguém!

Olá a ninguém que está lendo isso no momento. Ninguém pq esse é o primeiro post do meu primeiro blog. Minha motivação em criar esse blog foi a de inserir aqui algumas idéias sobre programação, processos de software e análises sobre linguagens e frameworks, espero que o conteúdo fique de bom nível e que com isso os leitores comecem a aparecer. Bom, até o próximo e primeiro post com conteúdo.