segunda-feira, 12 de maio de 2008

Falando em java


Este domingo irá ocorrer o Falando em Java 2008, organizado pela Caelum.
O ano passado eu tive o prazer de ir a este evento e neste ano não será diferente.
Quem estiver por lá e quiser bater uma papo sobre o itrust, fique a vontade ;)

Observação: Inscrições encerradas, evento lotado.

sexta-feira, 14 de março de 2008

Onde estão os fontes ?

Hi, no artigo anterior eu disse que iria escrever um artigo descrevendo como usar os exemplos do artigo EJB3 + DAO, mas mudei de idéia.
Clique aqui para vc visualizar os códigos fontes. Veja que vc terá duas pastas: itrust e timesheet.
O itrust contem os arquivos do projeto antigo, e o timesheet contem os arquivos atuais. Eu ainda não organizei as pastas, em breve deve ajustar.

quarta-feira, 13 de fevereiro de 2008

EJB3 + DAO

Muito se discute na comunidade se devemos usar um mecanismo de persistência com EJB3, pois o container EJB somente irá injetar o EntityManger nas classes(Servlets e EJB) que são gerenciadas pelo container web, ou seja, isso é um problema pois o nosso DAO não é gerenciado pelo container web, e ele com certeza precisa do EntityManager.

Esse problema não existia anteriormente porque o nosso DAO era responsável por criar a conexão com o banco de dados, o que nos trazia mais problemas, pois tínhamos a questão da transação, que não podia ser gerenciada pelo DAO, apesar deste criar a conexão com o banco de dados.

Outro ponto negativo do EJB3 + DAO é que o nosso DAO não é algo mais complexo, portanto fica simples vc inserir um registro no banco de dados com poucas linhas:

@PersistenceContext
private EntityManager em;

public void persist(Object o){
em.persist(o);
}

Pois é, isso mesmo, o código acima irá gravar qualquer entidade no banco de dados, vc pode colocar essa função no seu EJB e "eureka" resolvido o nosso mecanismo de persistência.

Mas nem tudo é flores, essa abordagem não serve para nós, pois não estamos fazendo um sistema para a padaria da sua esquina, ou para lojinha do seu tio.

O nosso sistema não pode simplismente gravar a entidade no banco de dados e esperar que tudo ocorra normalmente, o método acima pode precisar de uma transação para duas entidades diferentes, ou pior, pode lançar uma exceção do banco de dados para o cliente. Imagina o seu EJB tratando as exceções do banco de dados mais as de negócio do EJB, não vai ficar muito elegante né :-/

Portanto aconselho sim, que vc use um mecanismo de persistência com o seu EJB, e vou demonstrar abaixo como vc pode fazer isso.

Primeiro vamos criar a nossa fabrica de DAO que será responsável por injetar o nosso EntityManager em nossos DAOs. Esta fábrica é um EJB local pois não queremos que clientes fora do nosso container web façam uso dessa classe.
Segue abaixo a interface da nossa fábrica.

@Local
public interface RepositorySession {
@TransactionAttribute(TransactionAttributeType.NEVER)
public LoginRepository getLoginRepository();

@TransactionAttribute(TransactionAttributeType.NEVER)
public TimesheetRepository getTimesheetRepository();
}

Agora segue abaixo a nossa implementação da fábrica.

@Stateless
public class RepositorySessionImpl implements RepositorySession{
@PersistenceContext
private EntityManager em;

@TransactionAttribute(TransactionAttributeType.NEVER)
public LoginRepository getLoginRepository(){
return new LoginDAO().setEntityManager(em);
}

@TransactionAttribute(TransactionAttributeType.NEVER)
public TimesheetRepository getTimesheetRepository(){
return new TimesheetDAO().setEntityManager(em);
}
}

Vc precisa reparar em três anotações importantes:
@Stateless significa que não vamos manter um estado de conversação para um cliente particular. Quando um cliente invoca o método de um bean stateless, as variáveis do bean podem conter um estado, mas somente para a duração da chamada. Ao terminar a execução, o estado é encerrado. Exceto durante a chamada do método, todas as instâncias de um bean stateless são equivalentes, permitindo que o container EJB atribua uma instância a qualquer cliente

@PersistenceContext significa que esta variável deve ser injetada com o EntityManager através do container web.
O PersistenceContext é um conjunto de objetos/entidades gerenciadas por um EntityManager. Objetos que representam entidades serão recuperados, inseridos ou removidos de um banco de dados pelo EntityManager. Estas entidades estarão anexadas ao contexto de persistência o qual será fechado/removido quando o EntityManager for
fechado. Se o contexto for mantido ativo porém o objeto/entidade for desanexada do contexto, então esta entidade não mais será gerenciado pelo EntityManager e modificações na sua instância não serão refletidos no banco de dados

@TransactionAttribute (TransactionAttributeType.NEVER)
Isto significa que o nosso método não irá iniciar uma transação para instanciar as classes, pois não faz sentido já que estamos apenas retornando um objeto sem fazer qualquer alteração no banco de dados.
Agora que vc já sabe como injetar EntityManager em nossos DAOs, vamos fazer uso deles em nossos EJB, segue abaixo um exemplo:

@Local
public interface TimesheetFacade {

@PostConstruct
public void initialize();

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void persist(Timesheet Timesheet);
}

Veja que eu anotei a minha interface como @Local, mas vc poderia anotar com @Remote pois esta classe pode ser usada por clientes externos, pois esta classe contem toda a nossa lógica de negocio, validações e controle das exceções e não tem problema em disponibilizar ela fora do nosso container. Agora segue abaixo a nossa implementação:

@Stateless
public class TimesheetFacadeImpl implements TimesheetFacade {
@EJB
RepositorySession repository;
TimesheetRepository timesheetRepository;

@PostConstruct
public void initialize() {
timesheetRepository = repository.getTimesheetRepository();
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void persist(Timesheet timesheet) {
timesheetRepository.persist(timesheet);
}
}

Precisamos prestar atenção para 3 anotações:
@EJB isso irá fazer como que a nossa variável receba a nossa fábrica de DAOs

@PostConstruct aqui criamos uma instância para o nosso repositório, não podemos colocar isso no construtor da classe pois precisamos esperar a injeção da nossa fábrica.

@TransactionAttribute(TransactionAttributeType.REQUIRED) Agora sim vamos usar uma transação, simples assim, nada de XML, apenas anotamos para que o container web saiba que aqui ele precisa iniciar uma transação.

Pronto, acabamos aqui, vc deve se perguntar: Cade a tal da validação, tratamento de exceção, lógica de negocio e etc, pois bem... deixamos isso para o próximo artigo pois vamos descrever como vc deve usar os seus EJB, e como configurar a sua aplicação.

quinta-feira, 7 de fevereiro de 2008

Novos horizontes

Devido a uma série de alterações no meu trabalho, volto somente agora a postar novos artigos.
A boa noticia é que estou refatorando o ITrust e devo escrever aqui as alterações que eu fiz e como isso pode lhe ajudar.

Eu mudei o servidor de aplicação, pois vamos trabalhar com EJB3, e isso sinceramente é muito bom.
Uma das vantagens de se usar EJB3 é que agora não precisamos mais controlar as transações, pois agora o container web vai fazer isso.
Vamos continuar usando Hibernate, porem não existe mais xml, vamos usar annotions ;-)
Basicamente vamos jogar muita coisa fora, pois até mesmo a abordagem do ITrust foi alterada.

Não pretendo mais fazer diretamente um ERP, mas sim um timesheet. É bem mais simples começar esse projeto fazendo um timesheet, pois a partir disso podemos posteriormente adicionar um controle financeiro, ou seja, vamos acoplar os módulos com o passar do tempo, mas o foco agora é fazer esse timesheet e como isso não é muito complicado eu já fiz uma grande parte.

Bom, chega de papo e vamos ao código.
Começando pelo mecanismo de persistência, vamos alterar um pouco nossas classes e também alguns conceitos que são o mais importante
Veja esse link e entenda o que é um repositório:
http://blog.fragmental.com.br/2007/03/01/voce-pergunta-001-daos-e-repositorios/

Espero que tenha entendido, portanto segue abaixo nossa definição do mecanismo de persistência, que é praticamente idêntico ao artigo que eu postei anteriormente, com algumas pequenas alterações, porem espero que o conceito tenha sido entendido.


package org.iprogramming.model.repository;

public interface GenericRepository<T> {
public void persist(T entity);
public void remove(T entity);
}

Acima vemos um repositório genérico, que será usado no nosso repositório de Login abaixo

package org.iprogramming.model.repository;

import java.util.List;
import org.iprogramming.model.entity.Login;

public interface LoginRepository extends GenericRepository<Login>{
public Login findById(Long id);
public Login findByName(String login);
public List<Login> list();
public List<Login> listOrderedBy(String column);
}


Pronto, nosso repositório ja esta definido, é apenas um conceito, onde nos definimos o que nosso repositório de Login deve fazer.
Quando estivermos fazendo nossos EJB, esses serão os nossos contratos para usar as classes abaixo


package org.iprogramming.model.persistence;

import javax.persistence.EntityManager;
import org.iprogramming.model.repository.GenericRepository;

public class GenericDAO<T, DAOImpl extends GenericRepository> implements GenericRepository<T>{

protected EntityManager em;

public DAOImpl setEntityManager(EntityManager em){
this.em = em;
return (DAOImpl)this;
}

public void persist(T entity){
em.persist(entity);
}

public void remove(T entity){
em.remove(entity);
}
}

Sim, novamente o nosso DAO genérico, porêm com menos funções, o exemplo é meramente demonstrativo, vc pode adicionar outras funções genéricas como bem entender.

package org.iprogramming.model.persistence;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import org.iprogramming.model.entity.Login;
import org.iprogramming.model.repository.LoginRepository;

public class LoginDAO extends GenericDAO<Login, LoginRepository> implements LoginRepository {

private static final Logger logger = Logger.getLogger(LoginDAO.class.getName());

private static final String msg = "Signals that a method has been invoked at an " +
"illegal or inappropriate time. In other words, the Java environment " +
"or Java application is not in an appropriate state for the requested operation";

public Login findById(Long id) {
return (Login)em.createNamedQuery("Login.findByPkLogin").
setParameter("pkLogin", id).getSingleResult();
}

public Login findByName(String name) {
try{
return (Login)em.createNamedQuery("Login.findByName").
setParameter("name", name).getSingleResult();
}catch(NoResultException e){
logger.log(Level.SEVERE, "Cannot find data");
throw new DAOException(e);
}catch(NonUniqueResultException e){
logger.log(Level.SEVERE, "Have more than one result");
throw new DAOException(e);
}catch(IllegalStateException e){
logger.log(Level.SEVERE, msg, e);
throw new DAOException(e);
}
}

public List<Login> list(){
try{
return em.createQuery("SELECT l FROM Login l").getResultList();
}catch(IllegalStateException e){
logger.log(Level.SEVERE, msg, e);
throw new DAOException(e);
}
}

public List<Login> listOrderedBy(String column) {
return em.createQuery("SELECT login FROM Login login order by login."+column).getResultList();
}
}

Ok, terminamos o nosso mecanismo de persistencia para a entidade login. Segue abaixo a entidade login porem agora estamos usando anotações.

package org.iprogramming.model.entity;

import java.io.Serializable;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;


@Entity
@Table(name = "login")
@NamedQueries({
@NamedQuery(name = "Login.findByPkLogin",query = "SELECT l FROM Login l WHERE l.pkLogin = :pkLogin"),
@NamedQuery(name = "Login.findByName",query = "SELECT l FROM Login l WHERE l.name = :name"),
@NamedQuery(name = "Login.findByPassword",query = "SELECT l FROM Login l WHERE l.password = :password"),
@NamedQuery(name = "Login.findByEmail",query = "SELECT l FROM Login l WHERE l.email = :email"),
@NamedQuery(name = "Login.findByTelephone",query = "SELECT l FROM Login l WHERE l.telephone = :telephone"),
@NamedQuery(name = "Login.findByDescription",query = "SELECT l FROM Login l WHERE l.description = :description")})

public class Login implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@Column(name = "pk_login", nullable = false)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="seq_login")
@SequenceGenerator(name="seq_login", sequenceName="seq_login", allocationSize=1)
private Integer pkLogin;

@Column(name = "name", nullable = false)
private String name;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "email", nullable = false)
private String email;
@Column(name = "telephone", nullable = false)
private String telephone;
@Column(name = "description")
private String description;
@JoinTable(name = "login_x_role", joinColumns = {@JoinColumn(name = "fk_login", referencedColumnName = "pk_login")}, inverseJoinColumns = {@JoinColumn(name = "fk_role", referencedColumnName = "pk_role")})
@ManyToMany
private Collection fkRoleCollection;
@JoinTable(name = "login_x_page", joinColumns = {@JoinColumn(name = "fk_login", referencedColumnName = "pk_login")}, inverseJoinColumns = {@JoinColumn(name = "fk_page", referencedColumnName = "pk_page")})
@ManyToMany
private Collection fkPageCollection;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "fkLogin")
private Collection timesheetCollection;

public Login() {
}

public Login(Integer pkLogin) {
this.pkLogin = pkLogin;
}

public Login(Integer pkLogin, String name, String password, String email, String telephone) {
this.pkLogin = pkLogin;
this.name = name;
this.password = password;
this.email = email;
this.telephone = telephone;
}

public Integer getPkLogin() {
return pkLogin;
}

public void setPkLogin(Integer pkLogin) {
this.pkLogin = pkLogin;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getTelephone() {
return telephone;
}

public void setTelephone(String telephone) {
this.telephone = telephone;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public Collection getFkRoleCollection() {
return fkRoleCollection;
}

public void setFkRoleCollection(Collection fkRoleCollection) {
this.fkRoleCollection = fkRoleCollection;
}

public Collection getFkPageCollection() {
return fkPageCollection;
}

public void setFkPageCollection(Collection fkPageCollection) {
this.fkPageCollection = fkPageCollection;
}

public Collection getTimesheetCollection() {
return timesheetCollection;
}

public void setTimesheetCollection(Collection timesheetCollection) {
this.timesheetCollection = timesheetCollection;
}

@Override
public int hashCode() {
int hash = 0;
hash += (pkLogin != null ? pkLogin.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Login)) {
return false;
}
Login other = (Login) object;
if ((this.pkLogin == null && other.pkLogin != null) || (this.pkLogin != null && !this.pkLogin.equals(other.pkLogin))) {
return false;
}
return true;
}

@Override
public String toString() {
return "org.iprogramming.model.entity.Login[pkLogin=" + pkLogin + "]";
}

}

Ok, chegamos ao fim desse artigo, no proximo vou usar esse mecanismo através de um EJB e vou descrever como devemos injetar o EntityManager em nossos mecanismos de persistencia.

quinta-feira, 12 de julho de 2007

O que é Domain-Driven Design

Há algum tempo eu tenho lido sobre o que é Domain Driven Design, achei o assunto interessante e gostaria de compartilhar com vc leitor o meu conhecimento.

O link a seguir o levará para o material que contém a tradução do Quckly Domain Driven Design, eu ainda estou editando e assim que terminar eu vou colocar as minha conclusões.

http://docs.google.com/Doc?docid=dhs6md9j_0ffk82g&hl=pt_BR

quarta-feira, 11 de julho de 2007

Controlando as transações parte III

Esse é o ultimo artigo sobre o controle de transações, vou ficar devendo o artigo sobre controle de transações através de EJB. O motivo desse ser o ultimo artigo é devido a mudanças drásticas no projeto ITrust, estamos reformulando o projeto para que este respeite as regras de programação orientada a objetos.

O controle de transações desse artigo retrata uma abordagem com o springframework. Para quem não conhece o springframework como o próprio nome já diz este é um framework, com diversas soluções práticas e nos vamos usar uma pequena parte dos seus recursos que retrata o uso de um container para inversão de controle.

Como eu disse nos artigos anteriores, eu não vou ficar replicando as dezenas de materiais existentes na internet, portanto não vou explicar o que é um container para inversão de controle mas vou disponibilizar o endereço aonde você pode ler e entender sobre o assunto
http://www.martinfowler.com/articles/injection.html

Agora que você já sabe sobre o que estamos falando eu pretendo demonstrar como implementamos isso ao projeto ITrust. Vamos seguir as mesmas classes dos artigos anteriores porem vamos adicionar algumas alterações.

A nossa classe GenericHibernateDAO deverá ficar assim:

public class GenericHibernateDAO <T, ID extends Serializable, DAOImpl extends GenericRepository<T, ID>> extends HibernateDaoSupport implements GenericRepository<T, ID> {

protected Session getCurrentSession() {
SessionFactory SessionFactory = this.getHibernateTemplate().getSessionFactory();
return SessionFactory.getCurrentSession();
}

@SuppressWarnings("unchecked")
public T findById(Class<T> clazz, ID id) {
return (T)getCurrentSession().get(clazz, id);
}

@SuppressWarnings("unchecked")
public List<T> list(Class<T> clazz) {
return getCurrentSession().createCriteria(clazz).list();
}

@SuppressWarnings("unchecked")
public List<T> list(Class<T> clazz, int firstResult, int maxResults) {
Criteria criteria = getCurrentSession().createCriteria(clazz);
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
return criteria.list();
}

@SuppressWarnings("unchecked")
public List<T> listByExample(T entity) {
Criteria criteria = getCurrentSession().createCriteria(entity.getClass());
Example sample = Example.create(entity);
sample.enableLike();
sample.excludeZeroes();
criteria.add(sample);
return criteria.list();
}

public T makePersistent(T entity) {
getCurrentSession().saveOrUpdate(entity);
return entity;
}

public void makeTransient(T entity) {
getCurrentSession().delete(entity);
}
}


A classe segue o mesmo codigo de antes porem com uma pequena alteração:
extends HibernateDaoSupport aqui descrevemos que esta nossa classe deve herdar uma classe do springframework, pois esta classe HibernateDaoSupport é responsavel por disponibilizar uma sessão corrente do hibernate.

Continuando com o código note que excluímos a função setSession pois esta já não é mais uma tarefa de nossa responsabilidades, e mudamos a função getSession para que esta adquira uma seção corrente do hibernate.

Agora nos precisamos configurar o nosso container de inversão de controle e mudar algumas coisas na nossa fabrica HibernateDAOFactory. Começando pela nossa fabrica faça as seguintes alterações:

public class HibernateDAOFactory extends DAOFactory{

private static final ApplicationContext context;

static {
try {
context = new ClassPathXmlApplicationContext("iocForHibernate.xml");
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}

@Override
public CompanyRepository getCompanyRepository() {
return (CompanyDAO) context.getBean("companyDAO");
}

}

Nada muito complicado, o bloco estatico irá configurar o nosso container para inversão de controle e todas as funções devem criar uma instancia de seus respectivos DAOs através da classe ApplicationContext. Abaixo segue o arquivo de configuração do nosso container


<beans>
<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/iprogramming</value>
</property>
<property name="username">
<value>postgres</value>
</property>
<property name="password">
<value>postgres</value>
</property>
</bean>


<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>

<property name="mappingResources">
<list>
<value>org/iprogramming/model/domain/entity/Company.hbm.xml</value>
</list>
</property>

<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="show_sql">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
</props>
</property>
</bean>

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

<bean id="hibernateCompanyDAO" class="org.iprogramming.model.persistence.hibernate.global.HibernateCompanyDAO">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>

<bean id="companyDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>

<property name="target">
<ref local="hibernateCompanyDAO"/>
</property>

<property name="transactionAttributes">
<props>
<prop key="make*">
PROPAGATION_REQUIRED, ISOLATION_READ_COMMITTED
</prop>
<prop key="find*">
PROPAGATION_SUPPORTS, readOnly
</prop>
<prop key="list*">
PROPAGATION_SUPPORTS, readOnly
</prop>
</props>
</property>
</bean>
</beans>

Vc deve possuir uma série de duvidas sobre alguns detalhes do arquivo acima, portanto aconselho que vá até a seção de arquivos desse link
http://groups.google.com/group/itrust-erp?hl=pt-BR
Esse é nosso grupo de discussão do projeto, lá você pode encontar dezenas de materiais e nossas discussões sobre o projeto.

quarta-feira, 11 de abril de 2007

Controlando as transações Parte II


Bom, agora que vc ja esta entretido com o controle de transações, nos vamos fazer este controle de forma implicita, isso irá tornar um pouco obscuro a sua percepção sobre o assunto. O controle de transações descrito neste artigo é uma obra de Davi Luan Carneiro, ele escreveu um artigo no GUJ descrevendo passo a passo como esse processo funciona, por isso eu vou descrever aqui apenas a nossa implementação para este tipo de controle de transação.


Este controle de transação se encaixa perfeitamente com o artigo anterior. Criamos novamente uma classe de negocio que será responsavel por fazer o controle de transações, essas classes devem permanecer na camada de "model", assim como descrevemos anteriormente.

|---+model
|
|---+busines
|---+persistence
|---dao
|---dto
|---factory
|---transaction

Creio que vc ja deva saber porque frizei estes packages, porem o package transaction será um pouco mais complexo, pois ira abrigar as classes desenvolvidas pelo Davi. A grande sacada dessa solução é o uso de Annotations, agora ao invés de ficar replicando codigo, nos iremos apenar anotar se tal metodo requer ou nao uma transação, veja o codigo abaixo.

public class LoginBO{
private DAOFactory factory = null;

public LoginBO(){
factory = DAOFactory.getDAOFactory(DAOFactory.HIBERNATE);
}

@HibernateTransaction
public Person makePersistent(Person person) throws RuntimeException {
return factory.getPersonDAO().makePersistent(person);
}
}

Ainda nao acabamos, pois para que esta anotação funcione nos vamos precisar instanciar a nossa classe de negocio através de uma forma um pouco peculiar, portanto vamos esconder isso usando o pattern Factory. Crie a seguinte factory junto de suas classes de negocios.

public class BOFactory {

public static LoginBO getLogin(){
try{
return (LoginBO)TransactionClass.
create(LoginBO.class, HibernateInterceptorAnnotation.class);
}catch (Exception ex){
return null;
}
}
}

Agora se vc quiser gravar um registro no banco de dados, vc deve proceder da seguinte maneira

LoginBO loginBO = BOFactory.getLogin();
Person person = new Person("Ronildo Braga Jr","26-01-1982","M");
loginBO.makePersistent(person);


Eu vou descrever aqui as demais classes que foram adicionadas ao nosso package transaction, porem aconselho vc a ler o artigo do Davi por inteiro.


package org.iprogramming.model.persistence.hibernateTransaction;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Utilizaremos esta anotação para marcar os métodos que
* queremos que sejam transacionais. Consideramos como
* métodos transacionais aqueles cujo processamento
* constitui uma única transação com o banco
* @author Ronildo
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HibernateTransaction {}




package org.iprogramming.model.persistence.hibernateTransaction;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.apache.log4j.Logger;
import org.iprogramming.Util;


/**
* Cerne do componente para controle de transacao. Faz uso de CGLib,
* uma biblioteca que provê mecanismos de manipulação de bytecode, permitindo
* implementar interfaces dinamicamente, interceptar a chamada de métodos.
* Chamando determinado método, interceptamos a sua ação, e verificamos se este método
* é transacional. Se for, abrimos uma transação, e executamos o método.
* Se ele for executado com sucesso, damos commit na operação.
* Porém, se der exceção, fazemos um rollback
*
* Isso se parece com a solução de Filtros Http, porém com uma vantagem:
* o controle transacional não fica misturado com a API do Controller (C do padrão MVC),
* e é mais flexível, pois não se prende ao contexto web
*
* Observe também que esta solução é melhor que tratar as transações diretamente nas classes Java.
* Isso porque você vai evitar muito código repetitivo (try, catch, commit, rollback, etc),
* e não vai “poluir” inúmeras classes com este tipo de código.
* @author Ronildo
*/
public abstract class HibernateInterceptor implements MethodInterceptor {

private static final Logger logger = Util.startLogger(HibernateInterceptor.class);

/**
* O método intercept, proveniente da interface MethodInterceptor (CGLib), se encarrega
* de fazer o controle transacional. Veja que ele usa o HibernateHelper,
* e que o método transacional é explicitamente invocado, através de methodProxy.invokeSuper(),
* e o seu retorno é explicitamente devolvido
* @throws Throwable
*/
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

if (isTransactional(object, method)){
logger.debug("logica de negocio deve ser executada dentro de uma transacao");
HibernateHelper.beginTransaction();
}

Object result = null;
try {
result = methodProxy.invokeSuper(object, args);
HibernateHelper.commitTransaction();
logger.debug("logica de negocio executada com sucesso");
}catch (Throwable e){
logger.debug("erro ao executar logica de negocio");
HibernateHelper.rollbackTransaction();
throw e;
}

return result;
}


/**
* Definido como abstrato para para obter máxima flexibilidade, pois apenas
* estendendo esta classe e implementando este método, podemos saber se o
* método é transacional ou não de diversas maneiras, como arquivos XML,
* arquivos texto, e anotações! (Esta classe é um caso do pattern Template Method).
*/
public abstract boolean isTransactional(Object object, Method method) ;
}




package org.iprogramming.model.persistence.hibernateTransaction;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
* Está é a nossa implementação de HibernateInterceptor. O método isTransactional
* procura no método a anotação @HibernateTransaction. Se encontrar, retorna true,
* indicando que o método é transacional. Se não encontrar, retorna false.
* @author Ronildo
*/
public class HibernateInterceptorAnnotation extends HibernateInterceptor {

public boolean isTransactional(Object object, Method method) {
Annotation annotation = method.getAnnotation(HibernateTransaction.class);
return annotation == null ? false : true;
}
}





package org.iprogramming.model.persistence.hibernateTransaction;

import net.sf.cglib.proxy.Enhancer;

/**
* Para podermos interceptar os nossos métodos usando CGLib, não podemos criar
* suas classes através de um construtor. Devemos criá-las a partir do objeto Enhancer,
* fornecido pela API. Assim, implementamos uma classe para nos ajudar na construção
* de objetos que possuam métodos transacionais.
*
* Esta classe possui o método create, que recebe dois argumentos do tipo Class: o beanClass,
* que se refere à classe que desejamos criar; e o interceptorClass, que se refere ao tipo de Interceptor usado.
* Neste último argumento, passaremos HibernateInterceptorAnnotation.class, mas poderíamos
* também passar HibernateInterceptorXML.class, HibernateInterceptorTextFile.class, enfim,
* qualquer coisa que a sua imaginação permitir.
*
* Esta classe nao deve ser conhecida em vários lugares da aplicação. Procure escondê-la utilizando o
* pattern Factory, ou integrá-la com o seu mecanismo de IoC (como Spring Framework ou PicoContainer).
* Assim, você evita “poluir” suas classes desnecessariamente.
* Bem, em termos práticos: se quisermos que determinada classe possua um método transacional,
* ela deve ser criada por meio de TransactionClass
* @author Ronildo
*
*/
public class TransactionClass {

public static Object create(Class beanClass, Class interceptorClass) throws InstantiationException, IllegalAccessException{
HibernateInterceptor interceptor = (HibernateInterceptor)interceptorClass.newInstance();
Object object = Enhancer.create(beanClass, interceptor);
return object;
}
}


Portanto finalizamos aqui, no proximo artigo irei demonstar soluções usando o SpringFramework e finalmente depois uma solução com EJB.

Controlando as transações Parte I


Este artigo é primeiro artigo sobre controle de transações do hibernate. O controle de transações pode ser algo muito facil ou muito dificil dependendo da abordagem que você queira adotar, por este motivo demonstrarei não somente a minha opção mas também as diversas opções que você pode adotar, após estes artigos espero que vc seja capaz escolher a melhor solução para você.


A primeira opção é relativamente simples, nos vamos criar uma classe de negocio que será responsavel por fazer o controle de transações, e o programador responsavel por escrever essas classes deve saber executar de forma explicita as rotinas de begin transaction, commit e rolback do hibernate. Essas classes devem permanecer na camada de "model", por isso descrevemos abaixo aonde vc deve adicionar tais classes

|---+model
|
|---+busines
|---+persistence
|---dao
|---dto
|---factory
|---transaction

Note que agora eu frizei dois packages(busines e transaction), apesar de nossas classes se encontrarem no package busines, nos iremos usar as classes no package transaction tambem, pois nela se encontra as rotinas do hibernate, os quais a propria documentacao do hibernate disponibiliza. Lembre-se que nossos packages é divido por funcionalidades, e por isso as classes de busines não estao contidas no package de persistence.


Abaixo segue uma solução que vc pode usar para controlar as suas transacoes do hibernate, quando eu disse que vc devia executar de forma explicita as rotinas de begin transaction, commit e rolback do hibernate, eu quis dizer exatamente isso: HibernateHelper.beginTransaction();. A classe HibernateHelper será descrita posteriormente.

public class LoginBO{
private DAOFactory factory = null;

public LoginBO(){
factory = DAOFactory.getDAOFactory(DAOFactory.HIBERNATE);
}

public Person makePersistent(Person person) throws RuntimeException {

try {
HibernateHelper.beginTransaction();
person = factory.getPersonDAO().makePersistent(person);
HibernateHelper.commitTransaction();
}catch (RuntimeException e){
HibernateHelper.rollbackTransaction();
throw e;
}
return person;
}
}

As funcoes ou metodos da nossa classe de negocio poderão lançar uma RumtimeException, pois a ideia aqui é simples: A nossa classe deve fazer o controle de transacoes e tratar qualquer erro da camada de persistencia, e para isso nos executamos rolback para tratar a exceção, porem nos lançamos novamente a exceção para avisar a camada acima que ocorreu algum erro, mas que nao precisa se preocupar em resolver, pois gerenciar o banco de dados é nossa responsabilidade.


Agora se vc quiser gravar um registro no banco de dados, vc deve proceder da seguinte maneira

LoginBO loginBO = new LoginBO();
Person person = new Person("Ronildo Braga Jr","26-01-1982","M");
loginBO.makePersistent(person);


Abaixo exibimos a classe HibernateHelper, como dito anteriormente, a intenção aqui não é ficar replicando os materiais da internet. Basta dizer que esta classe deve: Gerenciar os objetos Session e Transaction, bem como deverá ser usada na confecção dos DAOs (Data Access Object). Ela faz uso de ThreadLocal, garantindo que cada thread possua uma única instância de Session e Transaction, e protegendo esses objetos de qualquer problema de concorrência

public class HibernateHelper {

private static final SessionFactory sessionFactory;
private static final ThreadLocal sessionThread = new ThreadLocal();
private static final ThreadLocal transactionThread = new ThreadLocal();

static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
//Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}

public static Session currentSession() {
if (sessionThread.get() == null) {
Session session = sessionFactory.openSession();
sessionThread.set(session);
}
return (Session) sessionThread.get();
}

private static void closeSession() {
Session session = (Session) sessionThread.get();
if (session != null)
session.close();

sessionThread.set(null);
}

public static void beginTransaction() {
if (transactionThread.get() == null){
Transaction transaction = currentSession().beginTransaction();
transactionThread.set(transaction);
}
}

public static void commitTransaction() {
Transaction transaction = (Transaction) transactionThread.get();
if (transaction != null && !transaction.wasCommitted() && !transaction.wasRolledBack()) {
transaction.commit();
transactionThread.set(null);
}

closeSession();
}

public static void rollbackTransaction() {
Transaction transaction = (Transaction) transactionThread.get();
if (transaction != null && !transaction.wasCommitted() && !transaction.wasRolledBack()) {
transaction.rollback();
transactionThread.set(null);
}

closeSession();
}
}


Portanto finalizamos aqui, no proximo artigo iremos fazer o controle de transação de forma implicita, ou seja, nao iremos ficar replicando muito codigo. A solução acima é bem pratica e funcional, porem é um pouco tedioso ficar toda hora executando begin transaction, comit e etc



quinta-feira, 5 de abril de 2007

A Fabrica de DAOs


Este artigo explora as classes envolvidas na fabrica de DAOs do ITrust, nossa fabrica encontra-se dentro do package "org.iprogramming.model.persistence.factory". A fabrica de DAOs tem a funcão de abstrair qual a forma de acesso ao banco de dados e setar a sessão corrente do hibernate. Apesar de nosso foco ser a integração com o hibernate, nao podemos ficar presos a esta ferramenta, entao vamos dar a possibilidade de vc acessar o banco de dados via JDBC. Segue abaixo nosso exemplo

|---+persistence
|
|---dao
|---dto
|---factory
|---transaction


public abstract class DAOFactory {
public static final int HIBERNATE = 1;
public static final int SQLServer = 2;

public static DAOFactory getDAOFactory(int whichFactory){
switch(whichFactory){
case 1:
return new HibernateDAOFactory();
case 2:
return new SQLDAOFactory();
default:
return null;
}
}

public abstract LoginDAO getLoginDAO();
}


Essa é a nossa implementação para uma fabrica de DAOs do Hibernate

public class HibernateDAOFactory extends DAOFactory{
private static Session getCurrentSession() {
return HibernateHelper.currentSession();
}

public LoginDAO getLoginDAO() {
return new HibernateLoginDAO().setSession(getCurrentSession());
}
}

Ao instancia o nosso DAO, precisamos setar a sessão corrente do hibernate, isso é feito através do método que herdamos da classe GenericHibernateDAO. Portanto finalizamos aqui, agora se vc precisa consultar os registros da tabela de login basta vc fazer o seguinte procedimento:

DAOFactory factory = DAOFactory.getDAOFactory(DAOFactory.HIBERNATE);
LoginDAO loginDAO = factory.getLoginDAO();
List list = loginDAO.list(Login.class);

No próximo artigo vamos estudar como fazer o controle de transações do hibernate.

Data Access Object Generic


Esse artigo explora as classes envolvidas no DAO do ITrust. O DAO que iremos usar é implementado com Hibernate e JDK5, muita gente acha desnecessário implementar DAO com Hibernate, portanto mostramos aqui o porque dessa implementação. O DAO do ITrust encontra-se dentro do package org.iprogramming.model.persistence.dao e nele encontramos as sepaçoes de packages descritas abaixo

|---+dao
| |
| |---generic
| |---hibernate

Dentro do package dao nos iremos adicionar todas as nossas interfaces de DAO, estas interfaces irão abstrair qual a forma de acesso ao banco de dados e serve para especificar quais os metodos padrões que um DAO deve possuir. Porem todos os DAOs vão possuir metodos semelhantes no caso do hibernate, sendo assim, iremos criar primeiro um GenericDAO para pouparmos um certo trabalho, portanto a interface GenericDAO sera o nosso exemplo e ainda terá dupla funcionalidade descrita posteriormente

public interface GenericDAO <T, ID extends Serializable>{
public T findById(Class clazz, ID id);
public List list(Class clazz);
public List list(Class clazz, int firstResult, int maxResults);
public List listByExample(T entity);
public T makePersistent(T entity);
public void makeTransient(T entity);
}

Devemos observar que esta interface será implementada em todas as demais interfaces, e isso so foi possivel através do recurso Generic implementado no JDK5, porem cada DAO possui as suas peculiaridades, e para criarmos as peculiaridades de cada DAO nos vamos simplismente criar nossos DAOs extendendo a GenericDAO e adicionando os metodos peculiares, segue exemplo abaixo.

interface LoginDAO extends GenericDAO<Login, Long>{
public Login findLoginByNamedQuery(Login arg);
}

Note que ao extendermos a interface GenericDAO nos precisamos especificar os tipo genericos atribuidos a GenericDAO, para vc entender melhor imagine que no lugar T e ID será substituido por Login e Long. Depois nos criamos um metodo especifico para o DAO Login, sendo assim, nao precisamos ficar escrevendo os metodos padroes como insert, delete, update e list.


Agora que ja possuimos a nossa interface para o DAO de Login nos precismos de fato criar uma classe que implementa esta interface, porem agora vamos descrever algo muito peculiar do hibernate. Nos vamos criar um GenericHibernateDAO para abastrair os metodos de insert, delete, update e list, isso tambem é possivel usando o recurso de Generic do jdk5. Segue abaixo o nosso exemplo:

public class GenericHibernateDAO <T, ID extends Serializable, DAOImpl extends GenericDAO<T, ID>> implements GenericDAO<T, ID> {
private Session session;

public DAOImpl setSession(Session s) {
this.session = s;
return (DAOImpl)this;
}

protected Session getSession() {
if (session == null)
throw new IllegalStateException("Session has not been set on DAO before usage");
return session;
}

public T findById(Class clazz, ID id) {
return (T)getSession().get(clazz, id);
}

public List list(Class clazz) {
return getSession().createCriteria(clazz).list();
}

public List list(Class clazz, int firstResult, int maxResults) {
Criteria criteria = getSession().createCriteria(clazz);
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
return criteria.list();
}

public List listByExample(T entity) {
Criteria criteria = getSession().createCriteria(entity.getClass());
Example sample = Example.create(entity);
sample.enableLike();
sample.excludeZeroes();
criteria.add(sample);
return criteria.list();
}

public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}

public void makeTransient(T entity) {
getSession().delete(entity);
}
}

A classe GenericHibernateDAO é o cerne de todos os nossos DAOs, pois este ira conter todas as rotinas basicas para as nossas tabelas no banco de dados. Ainda podemos perceber a segunda funcionalidade da Inteface GenericDAO, pois agora a interface esta sendo implementada ao inves de extendida. Não perca tempo imaginando o que é isso <T, ID extends Serializable, DAOImpl extends GenericDAO<T, ID>> implements GenericDAO<T, ID> Apenais saiba que o conceito aqui é o mesmo descrito para a interface GenericDAO, ou seja, isto são tipos genericos que serão especificados quando alguem extender essa classe


Agora vc deve entender o uso do Generics em nossos DAOs, pois ao extender a classe GenericHibernateDAO nos especificamos quais sao os atributos que a classe 'Pai' deve usar. Note que nao precisamos escrever as demais rotinas de insert, delete, update e list. Agora tente implementar outro DAO e vc irá perceber que vc so precisar criar algumas classes e extender outras, se vc ja criou DAOs usando JDBC vc irá entender porque o hibernate é uma ótima ferramenta.

public class HibernateLoginDAO extends GenericHibernateDAO<Login, Long, LoginDAO> implements LoginDAO {
public Login findLoginByNamedQuery(Login arg)
Query select = getSession().getNamedQuery("findUser");
select.setLong("pkUsers", arg.getPkLogin());
select.setString("roleName", arg.getRoles().iterator().next().getName());

List<Login> logins = (List<Login>) select.list();
for(Login login : logins)
return login;

return null;
}
}


Terminamos aqui os nossos DAOs, veja abaixo o UML e estude, pois no proximo artigo vamos descrever como vc deve criar as instancia de seus DAOs e como controlar as transacoes do hibernate, provavelmente vc deve estar se perguntando: O que sao os metodos setSession e getSession na classe GenericHibernateDAO. Esses metodos serão descritos no proximo artigo.


quarta-feira, 4 de abril de 2007

O Inicio


O ITrust é o inicio de um ERP open source, com cadastro de clientes, contas a pagar e etc. Este é o primeiro artigo de uma série, descrevendo a aplicação dos patterns envolvidos e bibliotecas do projeto. O conteudo dos arquivos encontra-se no source forge em https://sourceforge.net/projects/itrust.


Vamos iniciar o sistema descrevendo a hierarquia de pastas do projeto.

--+ITrust
|
|--+src
| |
| |---+org.iprogramming
| |
| |---+controller
| |---+model
| |---+view
|--+web
|
|---+css
|---+images
|---+include
|---+js
|---+pages
|---+WEB-INF

Podemos ver que as pastas são bem intuitivas e ao decorrer dos artigos vamos explorar o conteudo de cada pasta. Eu presumo que vc ja saiba criar um projeto na sua IDE de preferencia e criar esta hierarquia de pastas. Portanto vamos comecar adicionando as bibliotecas do projeto, eu uso as seguintes: Hibernate, JSF, Ajax4JSF, Tomahawk. Estas são as bibliotecas principais, porem iremos adicionar muitas outras para dar suporte a estas. Portanto baixe as bibliotecas e adicione estas ao seu projeto, caso vc tenha problemas em achar todas as bibliotecas envolvidas vá ao site do projeto no source forge e veja em detalhes as bibliotecas usadas.


Agora que ja temos o nosso projeto criado, vamos começar explorando as classes no package model, nele vamos encontar todas as classes de persistencia, acesso ao banco de dados e logica de negocio. A divisão de 'packages' usadas nesse projeto não é obrigatoria, porem voces vão perceber que eu costumo separar as classes em seus devidos packages caracterizados por funcionalidades. Vejamos abaixo uma breve descrição dos packages contidos no model.

model
|
|
|---busines
|--+persistence
|
|---+dao
| |
| |---generic
| |---hibernate
|
|---dto
|---factory
|---transaction

model --------> Contem todos os packages resposaveis pelo modelo do distema
busines ------> Representa as classes de logica de negocio do sistema
persistence --> Contem todos os packages envolvidos ao banco de dados
dao ----------> Representa as interfaces de nossos Data Access Object(DAO)
generic ------> Representa as classes e as interfaces genericas para dar suporte ao nossos DAOs
hibernate ----> Representa as classes de persistencia no banco de dados
dto ----------> Representa as classes referente aos registros no banco de dados
factory ------> Representa as classes para criar instancia de nossos DAOs
transaction --> Representa as classes para controle de transações do hibernate



Vamos iniciar explorando as classes do package dto, aqui devemos criar as classes que deverão representar os registros no banco de dados do nosso ERP. Vamos descrever cada tabela, porem vamos começar com tres tabelas basicas Company, Person e Login. Essas tabelas podem ser criadas usando a seguinte sintaxe para um banco de dados postgres

create sequence seq_company;
CREATE TABLE company
(
pk_company bigint NOT NULL DEFAULT nextval('seq_company'::regclass),
name character varying(45) NOT NULL,
description text,
CONSTRAINT ctr_company PRIMARY KEY (pk_company)
);

create sequence seq_person;
CREATE TABLE person
(
pk_person bigint NOT NULL DEFAULT nextval('seq_person'::regclass),
fk_company bigint NOT NULL,
name character varying(45) NOT NULL,
born date,
description text,
CONSTRAINT ctr_person PRIMARY KEY (pk_person),
CONSTRAINT ctr_company_in_person FOREIGN KEY (fk_company)
REFERENCES company (pk_company) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);

create sequence seq_login;
CREATE TABLE "login"
(
pk_login bigint NOT NULL DEFAULT nextval('seq_login'::regclass),
fk_person bigint NOT NULL,
"login" character varying(10) NOT NULL,
"password" character varying(10) NOT NULL,
description text,
CONSTRAINT ctr_login PRIMARY KEY (pk_login),
CONSTRAINT ctr_person_in_login FOREIGN KEY (fk_person)
REFERENCES person (pk_person) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);

Essas tabelas serão respresentadas no nosso package DTO da seguinte maneira:

public class Company implements java.io.Serializable {
//Non Fields
public boolean marked = false;
// Fields
private long pkCompany;
private String name;
private String description;
private Set<Person> persons = new HashSet<Person>(0);

//...geters and seters
}

Creio que a representação do DTO company descrito acima ja seja suficiente para que vc possa criar sozinho os demais DTO.
Existe algo fora do padrão no nosso DTO, isso é devido ao atributo marked. Esse atributo que nao existe na tabela será explorado posteriormente quando estudarmos a camada de apresentação feita em JSF. Agora que ja possuimos as nossas tabelas no banco de dados e nosso DTO, precisamos entao configurar o hibernate. Será necessário que vc crie o arquivo Company.hbm.xml, segue abaixo exemplo

<hibernate-mapping>
<class name="org.iprogramming.model.persistence.dto.Company" table="company" schema="public">
<id name="pkCompany" type="long">
<column name="pk_company" />
<generator class="sequence">
<param name="sequence">seq_company</param>
</generator>
</id>
<property name="name" type="string">
<column name="name" length="45" not-null="true" />
</property>
<property name="description" type="string">
<column name="description" />
</property>
<set name="persons" inverse="true">
<key>
<column name="fk_company" not-null="true" />
</key>
<one-to-many class="br.com.verano.model.persistence.dto.Person" />
</set>
</class>
</hibernate-mapping>

Ok, mapeamos no hibernate como funciona a nossa tabela Company. Lembre-se que estamos explorando o package DTO, portanto o arquivo Company.java e Company.hbm.xml encontra-se no package dto. Agora devemos configurar o hibernate de fato, e para isso ja existe dezenas de materiais, portanto aconselho que leia os tutorias na pasta de documentação do projeto no source forge. Não quero ficar replicando aqui os tutoriais ja existentes, o GUJ possui uma serie de artigos sobre hibernate e eu adicionei eles na pasta de documentacao do projeto, portanto eu aconselho que leia os tutorias pois no proximo artigo vamos presumir que vc ja saiba no minimo fazer uma consulta usando hibernate.


Esse foi nosso primeiro pattern abordado, o pattern DTO. O conceito descrito sobre esse pattern é relativamente simples e creio que vc nao teve muitos problemas para entender. No proximo artigo vamos explorar o Pattern DAO com hibernate e as coisas vão se tornar mais complexas