segunda-feira, 31 de março de 2008

Configurando grails para usar classes de domínio em Java

No post anterior eu fiz um resumo das tags JPA que são usadas (que eu uso) para fazer os mepeamentos objeto-relacional.

Neste post eu quero documentar aqui como configurar e grails para usar as classes Java anotadas.

Vamos lá:
1) Criar as classes java no diretório [grails-app-root-dir]/src/Java de sua aplicação em grails.
Geralmente eu crio para minhas aplicações o seguinte package "br.com.[nomeSistema].model".


2) Fazer o mapeamento objeto-relacional com as tags JPA, conforme documentado no post anterior.

Arquivo Pessoa.java
package br.com.applicacaoTeste.model;

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

/**
* Classe que representa uma pessoa
*/
@Entity
@Table(name="PESSOA")
public class Pessoa {

/**
* Identificador da propriedade
*/
@Id
@GeneratedValue
@Column(name="PESSOA_ID", nullable=false)
private Long id;

/**
* Nome da propriedade
*/
@Column(name="NOME", length=50, nullable=false, unique=true)
private String nome;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getNome() {
return nome;
}

public void setNome(String nome) {
this.nome = nome;
}
}



3) Para cada classe Java criada, criar um arquivo groovy para conter as "constraints" do groovy.

Exemplo:
Arquivo PessoaConstraints.groovy
package br.com.aplicacaoTeste.model

constraints = {
nome(size:1..50,nullable:false)
}

No código em groovy acima, estamos dizendo para o grails que o nome tem que ter pelo menos 1 caracter e não pode ser nulo. Estas constraints podem ser melhor entendidas de acordo com a documentação do grails [veja aqui]

4) Criar o arquivo [grails-app-root-dir]\grails-app\conf\hibernate\hibernate.cfg.xml da seguinte forma:

<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<mapping class="br.com.aplicacaoTeste.model.Pessoa" />
</session-factory>
</hibernate-configuration>

5) O quinto é ultimo passo é adicionar no arquivo [grails-app-root-dir]/conf/DataSource.groovy uma linha, conforme mostrado abaixo:


dataSource {
pooled = false
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""

//... configuração necessária para reconhecimento das TAGS JPA pelo grails
configClass = org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration }

... [Restante do conteúdo do arquivo foi omitido]

Agora as telas do cadastro de pessoa podem ser criadas com o comando do GRAILS

grails generate-all br.com.aplicacaoTeste.model.Pessoa

quinta-feira, 6 de março de 2008

Mapeamentos do Hibernate

Atualmente, mesmo usando o GRAILS, tenho preferido utilizar classes de domínio escritas em JAVA e JPA annotations para definição de como será realizada a persistencia.

Sempre que tenho que fazer alguma coisa nova, ou mesmo alguma coisa que já tenho feito acabo tendo dúvidas e isso me faz ir para o site do hibernate e ficar procurando lá as coisas.

Vou fazer um resumo aqui das tags que tenho usado e as referencias para o site do hibernate. Provavelmente eu serei o usuário que mais consultará este post.. rs.

Lembre-se: No caso de dúvidas, sempre consulte o site do hibernate.

Vamos lá...

Definição de uma entidade... [hibernate]
@Entity
@Table(name="USUARIOS")
class User { ...

Definição da chave primária (PK)... [hibernate]
@Id
@GeneratedValue
@Column(name = "ID_USUARIO")
Long id;

Definição de campo String... (para campo numérico deve remover o atributo length) [hibernate]
@Column(name="NOME_USUARIO", nullable=false, length=50)

String name;

Definição de campo que não será mapeado para o banco (transiente) [hibernate]
@Transient
int

RELACIONAMENTOS - Aqui acho um pouco mais complicado...

UM-PARA-UM
[hibernate]

A -> B - Relacionamento unidirecional

Existem 3 soluções resolver o mapeamento físico (veja link hibernate)... mas escolhi um aqui.
Escolhi a classe A para possuir a FK para a classe B.

Na classe A colocamos

class ClassA {
...
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="FK_B", nullable=false)
private ClasseB classeB; // referencia para a classe B
...
}

A <-> B - Relacionamento bidirecional

class ClassA {
...
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="FK_B", nullable=false)
private ClasseB classeB;
...
}


class ClassB {
...
@OneToOne(mappedBy="classB") // classB é o atributo na classe A
private ClassA classA;
...
}

MUITOS-PARA-UM - [hibernate]

*A -> B - Relacionamento unidirecional
Um objeto da classe B pode pertencer referenciado por mais de um objeto da classe A

class ClassA {
...
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
@JoinColumn(name="FK_B", nullable=false)
private ClassB classB;
...
}

UM-PARA-MUITOS - [hibernate]

A -> * B - Relacionamento unidirecional

Neste exemplo a ClassA é a "proprietária" do relacionamento

class ClassA {
...
@OneToMany
List classBList = new ArrayList();
...
}

A <-> * B - Relacionamento bidirecional

Estou aqui escolhendo que a ClassB é a responsável pelo relacionamento

class ClassA {
...
@OneToMany(mappedBy="classA",cascade = CascadeType.ALL) // classA é o atributo na classe B
List classBList = new ArrayList();
...
}

class ClassB {
....
@ManyToOne
@JoinColumn(name="PK_A")
private ClassA classA;
...
}

O mesmo exemplo agora tendo a ClassA como "proprietária" do relacionamento.

class ClassA {
...
@OneToMany
@JoinColumn(name="PK_A") //we need to duplicate the physical information
List classBList = new ArrayList();
...
}

class ClassB {
....
@ManyToOne
@JoinColumn(name="PK_A", insertable=false, updatable=false)
private ClassA classA;
...
}

Exemplos acima corrigidos de acordo com comentários do Felipe Nascimento (valeu Felipe)


MUITOS-PARA-MUITOS [hibernate] - veja seção 2.2.5.3.3.

Neste exemplo de referencia foi escolhida a ClassA como a "proprietária" do relacionamento.

class ClassA {
...
@ManyToMany(
targetEntity=packageA.ClassB.class,
cascade={CascadeType.PERSIST, CascadeType.MERGE}
)
@JoinTable(
name="TabelaA_TabelaB",
joinColumns={@JoinColumn(name="FK_A")},
inverseJoinColumns={@JoinColumn(name="FK_B")}
)
private List classAList = new ArrayList();
...
}

class ClassB {
...
@ManyToMany(
cascade={CascadeType.PERSIST, CascadeType.MERGE},
mappedBy="classAList"
targetEntity=ClassA.class
)
private List classBList = new ArrayList();
...
}

Meu primeiro post

Incentivado pelo meu amigo João Paulo Madureira, estou começando meu primeiro blog, onde pretendo compartilhar com vcs o uso das tecnologias que tenho trabalhado atualmente.

Essas são as tecnologias que atualmente estou usando:
- Java
- Grails / Groovy
- C/C++

Meu ambiente de trabalho é variado, mas atualmente estou preferindo usar o Mac OS X (Tiger ainda). Mas também uso o RWindow$ e é claro, o Ubuntu GNU/Linux.