30 de março de 2010 Respondendo aos comentários dos leitores, o autor atualizou a Listagem 9 e a seção terceiro código curto após a Listagem 9. Alterou-se "author.getFirstName() + " " + author.getLastName());" para "lookup.getFirstName() + " " + lookup.getLastName());".
Para a maioria dos desenvolvedores—especialmente os de Java—o termo ligação de dados tornou-se parte comum do vocabulário, junto com encerramentos, singletons e Ajax. Assim como esses outros termos, a ligação de dados é frequentemente definida de maneira incorreta.
Especificamente, quando a maioria dos programadores escuta o termo ligação de dados, na verdade pensam em ligação de dados XML. A adição dessa pequena palavra—XML—faz com que a maioria dos programadores percam um mundo de funcionalidades e flexibilidade adicionais, especialmente quando a API Castor está sendo usada. Isto acontece, pois quando se trata do Castor, a ligação de dados XML é somente uma parte de todo o quebra-cabeça. Além de ligar dados Java em documentos XML, o Castor também oferece a possibilidade de ligar estes dados em bancos de dados SQL. E é nesse ponto que a ligação de dados SQL entra em jogo.
Definindo a ligação de dados SQL.
Ligação de dados SQL pode ser um novo termo, porém trata-se de um conceito bem simples. De fato, é possível entender melhor o termo à luz do que significa um termo mais familiar, ligação de dados XML. A ligação de dados XML é o processo de criação de um mapeamento entre os dados em um documento XML—normalmente armazenados em elementos e atributos—e as propriedades de um modelo de objeto Java. É possível executar um organizador e um desorganizador para mover-se entre os dois. Um organizador armazena os dados de um modelo de objeto Java em um documento XML; um desorganizador carrega os dados de um documento XML nas propriedades de um modelo de objeto Java.
Com base nisso, não deve ser difícil entender que a ligação de dados SQL é o processo de criação de um mapeamento entre os dados em um banco de dados SQL—armazenados em esquemas, tabelas, colunas, etc.—e objetos Java. O mesmo processo de execução de organizar e desorganizar é aplicado, mas a conversão ocorre entre objetos Java e SQL, em vez de objetos Java e XML. De fato, se você trocar XML por SQL e dados de elementos por entradas de tabela na maioria dos artigos sobre ligação de dados, já entenderá do que se trata a ligação de dados SQL.
O valor da ligação de dados SQL
Quando a tecnologia Java apareceu pela primeira vez, era praticamente uma linguagem de brinquedo, pois contava com uma API muito simples e focava em gráficos (você se lembra do AWT?). Um dos marcos do amadurecimento da tecnologia Java foi a adição da Java database connectivity (JDBC), permitindo persistência aos bancos de dados SQL. O único problema foi, e ainda é, que o uso da JDBC é um pouco complicado. Embora não seja algo complexo, seu uso introduz uma quantidade de trabalho adicional significativa para a maioria dos programas.
Com o Castor e a ligação de dados SQL, é possível evitar a maior parte desta
complexidade. Além disso, você passa a usar uma API que funciona praticamente da
mesma forma em contextos XML e SQL. Por fim, como na ligação de dados, há menos
detalhes com os quais se preocupar no seu aplicativo. Lidar com contagens de linhas
e ResultSets JDBC não é mais uma preocupação no seu
código; em vez disso, algumas simples chamadas de organizar e desorganizar tratam da
conversão entre objetos Java e o seu banco de dados SQL.
O mais interessante sobre a ligação de dados SQL é que ela não recebeu mais atenção e consideração. Isso é principalmente verdade considerando que grande parte dos programadores não se importam com XML, ou porque é muito detalhado e de difícil manejo, ou porque preferem a serialização binária. Entretanto, este mesmo grupo adota a SQL sem problema algum. De fato, é difícil achar até mesmo um grupo de programadores amadores que não pensem que a SQL é uma tecnologia legítima de persistência e armazenamento de dados. (Veja, por exemplo, quantos dos nossos parceiros pensam que os bancos de dados relacionais são excessivamente valorizados, ineficientes e irrelevantes.)
Sendo assim, a ligação de dados SQL é comprovadamente uma ferramenta ainda mais útil do que seu primo XML. Sempre é necessário persistir (ou armazenar) dados em um aplicativo corporativo, e a composição de códigos de acesso e recuperação de dados é realmente complicada. A ligação de dados SQL usa uma API familiar (assumindo aqui que você leu os artigos anteriores desta série) e a aplica completamente para o armazenamento de dados SQL. É possível escrever em uma tabela (ou várias tabelas) com poucos comandos, e extrair dados do mesmo modo.
A capacidade de fazer o mapeamento a partir de objetos Java para um esquema de dados SQL sem estar preso a nomes SQL ou Java é particularmente importante na ligação de dados SQL. Mais ainda que em XML, a estrutura de um banco de dados relacional é normalmente muito diferente da estrutura dos seus objetos Java. Tabelas geralmente são coleções de dados, enquanto objetos costumam representar uma única porção (possivelmente uma linha) de dados. Os relacionamentos entre os objetos devem ser traçados com relação a outros objetos, do mesmo modo que os relacionamentos entre as tabelas devem ser feitos com relação a outras tabelas. Porém, não há uma tabela de junção de uma para muitos no seu modelo de objeto Java, e certamente nenhuma junção de muitos para muitos.
Ao começar a se envolver com as partes de complexidade intermediária do seu banco de dados relacional, você passará a projetar as coisas de forma diferente do que faria com um modelo de objeto. Grande parte do trabalho de mapeamento de SQL para objetos Java e vice-versa é definir o mapeamento entre os objetos e as tabelas. Embora o mapeamento possa ser algo bem avançado, neste artigo eu usarei mapeamentos simples para lhe oferecer um entendimento básico sobre como funciona o mapeamento no mundo das ligações de dados SQL.
Isso se trata simplesmente de JDO, certo?
Sim. Bem, não. Mais ou menos. Este ponto é um pouco confuso.
A Sun tem uma especificação chamada de Java Data Objects (JDO). O Java Specification Request (JSR) 12 para JDO 1.0 e o JSR 243 para JDO 2.0 definem uma abordagem específica para a ligação de dados SQL (embora não seja explicitamente chamada "ligação de dados SQL") Se você ler sobre JDO e então reler os parágrafos introdutórios deste artigo, pensará que está lendo a mesma coisa. Porém, a versão da especificação JDO do Castor (sim, o Castor também usa o nome JDO, o que torna as coisas ainda mais confusas) não é a mesma da Sun, ou mesmo tem relação com a Sun, com a única exceção que ambas têm o mesmo objetivo.
Isso pode causar muita confusão, assim, é preciso dizer novamente: se você usa a JDO do Castor, você não está usando a API padrão da SUN. Porém, isto não é tão ruim, ou tão claro, quanto parece. O pessoal no Castor está tentando implementar muitos dos recursos que trabalharam em sua própria API nas ligações de dados da Sun e em APIs JDO. Assim, embora a Castor não seja uma API padrão, muitos dos recursos—e a própria API—podem levá-la a se tornar uma API JDO padronizada.
Configurando um ambiente de exemplo
No caso de ligações de dados XML, seus dois terminais são um modelo de objeto Java e um documento XML (um esquema XML que restrinja documentos). No mundo das ligações de dados SQL, você ainda usaa um modelo de objeto Java, mas o outro terminal é um esquema SQL: uma ou mais tabelas com colunas. Considere um conjunto de terminais relativamente simples para os exemplos deste artigo.
Nesta série, eu usei um modelo de objeto Java para representar livros e autores,
assim, vou usar novamente o mesmo modelo de dados neste artigo. A Listagem 1 mostra o código para a classe Book.
Listagem 1. A classe Book
package ibm.xml.castor;
import java.util.LinkedList;
import java.util.List;
public class Book {
/** The book's ISBN */
private String isbn;
/** The book's title */
private String title;
/** The authors' names */
private List<Author> authors;
public Book() { }
public Book(String isbn, String title, List<Author> authors) {
this.isbn = isbn;
this.title = title;
this.authors = authors;
}
public Book(String isbn, String title, Author author) {
this.isbn = isbn;
this.title = title;
this.authors = new LinkedList<Author>();
authors.add(author);
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getIsbn() {
return isbn;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
public List<Author> getAuthors() {
return authors;
}
public void addAuthor(Author author) {
authors.add(author);
}
} |
A Listagem 2 mostra o código para a classe Author.
Listagem 2. Uma classe para representar os autores
package ibm.xml.castor;
public class Author {
private String firstName, lastName;
public Author() { }
public Author(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
} |
Nos artigos anteriores, você fez a persistência de várias ocorrências destas classes para documentos XML. Nos dois primeiros artigos, você focou na conversão direta do modelo de objeto para documentos XML, usando em ambos os mesmos nomes de propriedades. No terceiro artigo, o foco foi a conversão de objetos Java para XML (e vice-versa), mas com a capacidade de alterar convenções de nomenclatura de um formato para o outro.
Porém, agora, queremos armazenar todos os dados destas classes em um banco de dados SQL. Em vez de elementos e atributos, os dados estão em colunas, linhas e tabelas.
Criando uma amostra do esquema do banco de dados SQL
De certo modo, um esquema do banco de dados SQL normalmente é mapeado de forma parecida a um modelo de objeto Java, principalmente se o modelo não é muito complexo (como no nosso caso). Vamos considerar duas tabelas: dw_books e dw_authors.
É mais fácil criar a tabela dw_authors primeiro, pois a tabela dw_books faz referência a esta. Ela deve ter três colunas: um ID para cada autor, e seus primeiros e últimos nomes. Caso esteja acompanhando, é possível criar esta tabela em MySQL com o código da Listagem 3.
Listagem 3. Uma tabela para os autores
CREATE TABLE 'dw_authors' ( 'id' INT NOT NULL , 'first_name' VARCHAR( 50 ) NOT NULL , 'last_name' VARCHAR( 50 ) NOT NULL , PRIMARY KEY ( 'id' ) , INDEX ( 'first_name' , 'last_name' ) ); |
Observe que eu usei o prefixo dw_ em todas as tabelas deste artigo para distingui-las de outras tabelas que você pode encontrar, principalmente porque authors e books são nomes comuns de tabelas. Embora isto possa ser um pouco confuso, garante que você não terá conflitos de nomenclaturas com outros bancos de dados no seu sistema, caso esteja acompanhando este artigo (e possivelmente você está).
A seguir temos a tabela dw_books. Ela também é muito simples. A data definition language (DDL), que é o idioma usado para criar estruturas em SQL, está na Listagem 4.
Listagem 4. DDL para a tabela dw_books
CREATE TABLE 'dw_books' ( 'isbn' VARCHAR( 13 ) NOT NULL , 'title' VARCHAR( 200 ) NOT NULL , PRIMARY KEY ( 'isbn' ) , INDEX ( 'title' ) ); |
Finalmente, é necessário conectar essas duas tabelas. Aqui, já temos um problema. Um livro pode ter vários autores, o que significa que há um relacionamento de muitos para muitos entre as tabelas dw_books e dw_authors. Em outras palavras, um livro pode ter vários autores e um autor pode escrever vários livros. Logo, é necessária uma tabela para conectar um livro a um autor, e estas conexões devem ser de muitas para muitas.
No mundo de SQL, isto é algo muito comum: é necessária uma tabela de junção, onde cada linha contém um ISBN e um ID de autor. A Listagem 5 mostra como a tabela se parece.
Listagem 5. Tabela de junção (de muitos para muitos) para autores e livros
CREATE TABLE 'dw_books_authors' ( 'book_isbn' VARCHAR( 13 ) NOT NULL , 'author_id' INT NOT NULL , PRIMARY KEY ( 'book_isbn' , 'author_id' ) ); |
Entendendo a integridade referencial
Integridade referencial é um termo especial de SQL que diz respeito a manter todos os dados do seu banco de dados limpos, livres de dados incorretos ou fora de uso, e precisos em termos de junções entre as tabelas. Neste contexto, se você excluir um livro, todos os registros na tabela dw_books_authors com o ISBN deste livro serão excluídos. Uma vez que o livro não existe, também não devem existir registros com o ISBN deste livro. O mesmo serve para o autores; quando um autor é excluído, todos os registros com o ID desse autor também devem ser excluídos.
Para fazer isso, usam-se chaves estrangeiras. Logo, na tabela dw_books_authors, book_isbn contém isbn na tabela
dw_books como um chave estrangeira; o mesmo relacionamento ocorre entre author_id na tabela dw_books_authors e id em dw_authors. Isso garante a integridade referencial entre as
tabelas.
Entretanto, a maioria dos bancos de dados lida com a integridade referencial de forma diferente, e o MySQL não a suporta completamente em muitas versões do software. Assim, para tornar as coisas mais simples neste artigo, nenhuma chave estrangeira foi usada. Se você gosta de criar chaves estrangeiras, o que é uma boa ideia, verifique a documentação do fornecedor para o seu banco de dados ou consulte seu database administrator (DBA).
Preparando-se para a ligação de dados SQL
Mesmo que você já tenha usado o Castor para ligações de dados XML, provavelmente precisará fazer algumas alterações (ou, pelo menos, adições) ao seu ambiente de programação para fazer a ligação de dados SQL funcionar.
Adicionando arquivos JDO do Castor ao seu caminho de classe
Sua primeira necessidade são classes JDO do Castor no seu caminho de classe. Se você acompanhou as etapas de instalação no primeiro artigo desta série (consulte links para o artigo em Recursos), terá todos os arquivos Castor Java ARchive (JAR). Entretanto, é possível que você não tenha adicionado o arquivo específico JDO JAR ao seu caminho de classe. No diretório do Castor, localize e adicione o arquivo castor-1.1.2.1-jdo.jar ao seu caminho de classe (sua versão do arquivo pode ser mais recente, mas o procedimento é o mesmo).
Este arquivo junta-se aos outros arquivos JARs do Castor que você provavelmente já adicionou, como castor-1.1.2.1.jar, castor-1.1.2.1-xml.jar, e os arquivos Xerces e Commons Logging JAR. Se você usa um servidor de aplicativos ou IDE para executar seu código, certifique-se de também alterar esses caminhos de classe.
Adicionando um driver JDBC ao seu ambiente
Uma vez que você irá se conectar a um banco de dados SQL, é necessário um driver JDBC do seu fornecedor. Drivers JDBC para IBM DB2® e Informix®, Oracle, MySQL, PostgreSQL, entre outros, estão disponíveis gratuitamente. Isto já pode estar resolvido caso você tenha aplicativos com acesso a bancos de dados. Se você está começando a conhecer o JDBC, consulte os Recursos para links para drivers JDBC da DataDirect, que funcionam muito bem em DB2 e Informix, assim como em muitos outros servidores de bancos de dados. Caso não esteja usando DB2 ou Informix, encontre o driver JDBC para o seu fornecedor de banco de dados através de um busca rápida no Google, faça o download de um JAR e adicione-o ao seu caminho de classe.
Para este artigo, o banco de dados MySQL é usado na maioria dos exemplos. Caso for usar MySQL, você precisará do arquivo mysql-connector-java-3.0.17-ga-bin.jar, disponível gratuitamente no Web site MySQL (consulte os Recursos para mais informações).
Mapeando uma classe para uma tabela
Considere a mais simples das tarefas de ligação de dados SQL: mapear para uma única tabela um objeto que não faz referência a nenhum outro objeto. Esta é a principal operação que você irá executar na ligação de dados SQL, sendo que todos os recursos mais avançados vêm a ser variações deste tema.
Primeiro, mapeie a classe Author para a tabela dw_authors.
O mapeamento desejado é como o exibido na Tabela 1.
Tabela 1. Mapeamento entre o objeto Author e a tabela dw_authors.
| Propriedade Java | Coluna SQL |
|---|---|
| firstName | first_name |
| lastName | last_name |
Isto é suficientemente simples, porém há um problema óbvio: a tabela dw_authors
também contém uma coluna id. De fato, esta é a chave
primária da tabela dw_authors, assim, deve obrigatoriamente existir. Como resultado,
é necessário fazer uma adição à classe Author, como
exibido na Listagem 6.
Listagem 6. Adicionando suporte a IDs em Author.java
package ibm.xml.castor;
public class Author {
private String firstName, lastName;
private int id;
public Author() { }
public Author(int id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
} |
Agora é possível atualizar a tabela que mostra o mapeamento entre uma ocorrência de
Author e uma linha na tabela dw_authors, como exibido
na Tabela 2.
Tabela 2. Adicionado um campo ID à tabela de mapeamento
| Propriedade Java | Coluna SQL |
|---|---|
| id | id |
| firstName | first_name |
| lastName | last_name |
Criando um mapeamento para o Castor
Agora é necessário indicar ao Castor quais propriedades Java mapeiam para quais
colunas SQL. Para isso, usa-se um arquivo de mapeamento similar aos arquivos de
mapeamento que você usou para XML no terceiro artigo desta série. A Listagem 7 mostra o mapeamento usado para converter
ocorrências de Author para linhas na tabela
dw_authors.
Listagem 7. Mapeando objetos Author para a tabela dw_authors
<mapping>
<class name="ibm.xml.castor.Author" identity="id">
<map-to table="dw_authors" />
<field name="id" type="int">
<sql name="id" type="integer" />
</field>
<field name="firstName" type="string">
<sql name="first_name" type="varchar" />
</field>
<field name="lastName" type="string">
<sql name="last_name" type="varchar" />
</field>
</class>
</mapping> |
Não há muito o que falar sobre isso, pois é semelhante aos arquivos de mapeamento usados com objetos Java e XML. Salve este arquivo como sql-mapping.xml, e agora é possível indicar para o Castor como mapear as propriedades dos seus objetos Java nas suas tabelas do banco de dados.
Em comparação com o que você viu no mapeamento em Java e XML, a única novidade no
arquivo de mapeamento é o uso do atributo identity, que
indica o campo de chave primária para a tabela dw_authors. Provavelmente você não
tinha usado o atributo identity antes, pois o Castor
normalmente infere ou não precisa dele para funcionar. Porém, na ligação de dados
SQL, este atributo informa ao Castor o campo que define especificamente um objeto;
neste caso, o campo id de um autor.
Configurando o Castor para o acesso SQL
No mundo das ligações de dados XML, é necessário estar preparando para escrever códigos para organizar XML para objetos Java e vice-versa. Porém, no modelo SQL, há outra parte do software a considerar: o banco de dados. É preciso informar ao Castor onde o seu banco de dados está localizado, como efetuar login e qual arquivo de mapeamento deve ser usado. Isto é feito através de um arquivo de configuração, normalmente nomeado como jdo-conf.xml. A Listagem 8 fornece um arquivo de configuração, que você pode usar para MySQL.
Listagem 8. Definindo um esquema de conexão MySQL para o Castor
<?xml version="1.0" ?>
<!DOCTYPE jdo-conf PUBLIC "-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN"
"http://castor.org/jdo-conf.dtd">
<jdo-conf>
<database name="YOUR-DATABASE-NAME" engine="mysql">
<driver class-name="com.mysql.jdbc.Driver"
url="jdbc:mysql://YOUR-DATABASE-SERVER-HOSTNAME/YOUR-DATABASE-NAME">
<param name="user" value="YOUR-DATABASE-USERNAME"/>
<param name="password" value="YOUR-DATABASE-PASSWORD"/>
</driver>
<mapping href="sql-mapping.xml"/>
</database>
<transaction-demarcation mode="local"/>
</jdo-conf> |
Adicionado, procurando e excluindo uma entrada
Após todas estas configurações, você está finalmente pronto para interagir com o
banco de dados. Usando estes arquivos de configuração, é necessário carregar a
configuração, abrir um banco de dados e interagir com ele. A Listagem 9 faz tudo isso, criando uma nova ocorrência de Author, armazenando-a no banco de dados, procurando e, a
seguir, removendo-a.
Listagem 9. Usando a ligação de dados SQL com um autor
import ibm.xml.castor.Author;
import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;
public class SQLTester {
public static void main(String args[]) {
try {
JDOManager.loadConfiguration("jdo-conf.xml");
JDOManager jdoManager = JDOManager.createInstance("bmclaugh");
Database database = jdoManager.getDatabase();
database.begin();
Author author = new Author(1001, "Joseph", "Conrad");
database.create(author);
Author lookup = (Author)database.load(Author.class,
new Integer(1001));
System.out.println("Located author is named " +
lookup.getFirstName() + " " + lookup.getLastName());
database.remove(lookup);
database.commit();
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} |
Vamos observar este processo passo a passo.
Primeiro, é necessário um trabalho inicial para carregar suas informações de conexão e conectar-se ao banco de dados. Esta primeira etapa é basicamente a mesma em todo aplicativo onde você usa ligação de dados SQL, assim, recomenda-se memorizar estes comandos (ou adicionar este artigo aos seus favoritos):
JDOManager.loadConfiguration("jdo-conf.xml"); JDOManager
jdoManager = JDOManager.createInstance("bmclaugh"); Database database =
jdoManager.getDatabase(); database.begin(); |
O objetivo aqui é adquirir uma ocorrência do objeto Database, que lhe permite criar, procurar e excluir entradas. Após
adquirir a ocorrência, é possível criar uma nova ocorrência de Author e armazená-la no banco de dados:
Author author = new Author(1001, "Joseph", "Conrad");
database.create(author); |
Ao chamar create(), o Castor usa o seu arquivo de
mapeamento (sql-mapping.xml) para descobrir como armazenar o objeto que você passa
para esse método como dados SQL. Os seus dados são colocados no banco de dados e
você já está pronto para usá-los.
A seguir, é possível procurar objetos por sua identidade (é
aqui que o atributo identity entra em jogo):
Author lookup = (Author)database.load(Author.class, new
Integer(1001)); System.out.println("Located author is named " +
lookup.getFirstName() + " " + lookup.getLastName()); |
Usa-se o método load() com o tipo de classe que você
deseja usar, assim como o valor de identidade para a entrada que deseja. load() retorna uma nova ocorrência, que pode ser usada como
qualquer outro objeto Java, novamente usando os mapeamentos definidos em
sql-mapping.xml.
Este exemplo exclui a entrada, deixando o banco de dados no mesmo estado em que estava antes da execução do programa:
database.remove(lookup); |
Por fim, mas não menos importante, é necessário executar as alterações e fechar o banco de dados:
database.commit(); database.close(); |
Como você pode ver, não é muito difícil trabalhar com um simples mapeamento de classe
para tabela. Entretanto, as coisas ficam mais interessantes quando é necessário
trabalhar com uma situação mais complicada, como modelo de objeto Java com Books relacionados a ocorrências de Author.
Definindo um mapeamento SQL básico para um livro
Assim como em Author, é necessário definir um mapeamento
em sql-mapping.xml para tomar as propriedades da classe Book e persisti-las para um banco de dados relacional. Comece com as
adições da Listagem 10; estas tratam das propriedades
básicas de um livro.
Listing 10. Adding a book to the mappings file
<mapping>
<class name="ibm.xml.castor.Book" identity="isbn">
<map-to table="dw_books"/>
<field name="isbn" type="string">
<sql name="isbn" type="varchar" />
</field>
<field name="title" type="string">
<sql name="title" type="varchar" />
</field>
</class>
<class name="ibm.xml.castor.Author" identity="id">
<map-to table="dw_authors" />
<field name="id" type="int">
<sql name="id" type="integer" />
</field>
<field name="firstName" type="string">
<sql name="first_name" type="varchar" />
</field>
<field name="lastName" type="string">
<sql name="last_name" type="varchar" />
</field>
</class>
</mapping> |
Nada disto é surpresa; o código simplesmente toma a classe Book e identifica a tabela onde este deve ser mapeado (dw_books) e como
mapear as propriedades do objeto (isbn e title).
Definindo um relacionamento de muitos para muitos
Agora vem a parte interessante: informar ao Castor como os livros se relacionam com os autores. Lembre-se, o relacionamento funciona da seguinte maneira:
- Cada livro contém um único ISBN.
- Cada autor contém um único ID.
- Quando um livro é escrito por um autor, uma entrada em dw_books_authors contém o ISBN do livro e o ID do autor.
- Um livro pode ter vários autores (nesse caso, dw_books_authors contém várias entradas com o mesmo ISBN, mas com IDs de autores diferentes).
- Um autor pode escrever vários livros (nesse caso, dw_books_authors contém várias entradas com o mesmo ID de autor, mas ISBNs diferentes).
Primeiro, examine essa relação a partir do lado da classe Book na equação. Em uma ocorrência de Book,
você deseja preencher a propriedade authors com os
autores para dito livro. O mesmo serve para o contrário. Se uma ocorrência de Book contém dados em sua propriedade authors, é necessário armazenar estes autores por meio do ID de cada
autor. É necessário mapear a propriedade—authors—para entradas em dw_books_authors.
Para fazer isso funcionar, você começa com outro elemento field do Castor, como exibido na Listagem
11.
Listagem 11. Usando um elemento field para mapear autores para um array em Books
<field name="authors" type="ibm.xml.castor.Author"
collection="arraylist">
</field> |
Até aqui não temos nada de novo; você mapeia a propriedade authors, e dessa vez o tipo é uma classe, em vez de string ou int. Você usa collection="arraylist" para informar ao Castor que a propriedade é um
conjunto e não um valor único.
Agora, porém, é necessário indicar o campo para armazenar o ID do autor, assim como em qual tabela o campo deve ser armazenado, pois o ID do autor nunca acaba na tabela dw_books. A Listagem 12 mostra como indicar o nome da tabela de muitos para muitos—neste caso, dw_books_authors—assim como a chave para o ISBN do livro nessa mesma tabela.
Listagem 12. O elemento sql define uma tabela de muitos e um relacionamento de muitos para muitos
<field name="authors" type="ibm.xml.castor.Author"
collection="arraylist">
<sql name="author_id"
many-table="dw_books_authors"
many-key="book_isbn" />
</field> |
Trace essa lógica com atenção. Primeiro, lembre-se que este elemento trata de valores
na propriedade authors das ocorrências de Book. O campo real sendo mapeado é uma instância de Author, e você deseja mapear a parte identificadora desse
objeto—o id— para uma coluna chamada author_id. Entretanto, isso não é feito na tabela dw_books,
assim, usa-se many-table para identificar que a tabela
para onde o ID é mapeado é dw_books_authors. Por fim, o atributo many-key indica que o ISBN da ocorrência de Book também deve ser armazenado na sua tabela de muitos para muitos.
A Listagem 13 mostra o arquivo sql-mapping.xml com isso integrado no resto do arquivo.
Listagem 13. Adicionando suporte de muitos para muitos para Books
<mapping>
<class name="ibm.xml.castor.Book" identity="isbn">
<map-to table="dw_books"/>
<field name="isbn" type="string">
<sql name="isbn" type="varchar" />
</field>
<field name="title" type="string">
<sql name="title" type="varchar" />
</field>
<field name="authors" type="ibm.xml.castor.Author"
collection="arraylist">
<sql name="author_id"
many-table="dw_books_authors"
many-key="book_isbn" />
</field>
</class>
<class name="ibm.xml.castor.Author" identity="id">
<map-to table="dw_authors" />
<field name="id" type="int">
<sql name="id" type="integer" />
</field>
<field name="firstName" type="string">
<sql name="first_name" type="varchar" />
</field>
<field name="lastName" type="string">
<sql name="last_name" type="varchar" />
</field>
</class>
</mapping> |
A Listagem 14 mostra como revisar o programa SQLTester de modo que este crie um novo Author e, a seguir, um Book relacionado com
este autor.
Listagem 14. Criando um novo autor e um novo livro
import java.util.Iterator;
import ibm.xml.castor.Author;
import ibm.xml.castor.Book;
import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;
public class SQLTester {
public static void main(String args[]) {
try {
JDOManager.loadConfiguration("jdo-conf.xml");
JDOManager jdoManager = JDOManager.createInstance("bmclaugh");
Database database = jdoManager.getDatabase();
database.begin();
Author author = new Author(1001, "Joseph", "Conrad");
Book book = new Book("1892295490", "Heart of Darkness", author);
database.create(author);
database.create(book);
database.commit();
database.begin();
Book lookup = (Book)database.load(Book.class, "1892295490");
System.out.println("Located book is named " + lookup.getTitle());
System.out.println("Authors:");
for (Iterator i = lookup.getAuthors().iterator(); i.hasNext(); ) {
Author bookAuthor = (Author)i.next();
System.out.println(" " + bookAuthor.getFirstName() + " " +
bookAuthor.getLastName());
}
database.commit();
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
Isto é bem óbvio. Você se conecta ao banco de dados, cria um novo autor e um novo
livro, e adiciona ambos ao banco de dados (fechando com commit()):
Database database = jdoManager.getDatabase(); database.begin();
Author author = new Author(1001, "Joseph", "Conrad"); Book book = new
Book("1892295490", "Heart of Darkness", author); database.create(author);
database.create(book); database.commit(); |
A seguir, verifica-se se os dados foram inseridos com sucesso.
Para simular melhor a persistência de dados, os dados são comprometidos e uma nova
transação é iniciada (com o segundo database.begin(),
após os dados de livro e do autor serem comprometidos). Isso garante que a versão
local do Castor dos dados não seja usada, mas que o banco de dados seja de fato
consultado na segunda metade do programa.
Dê uma olhada nos dados em suas tabelas para certificar-se que estes estão de acordo com suas expectativas. Você deverá ter algo semelhante aos dados na Tabela 3 da sua tabela dw_books.
Tabela 3. Dados em dw_books após a execução do SQLTester
| isbn | título |
|---|---|
| 1892295490 | Heart of Darkness |
A Tabela 4 mostra a tabela dw_authors após a inserção.
Tabela 4. Dados em dw_authors após executar o SQLTester
| id | first_name | last_name |
|---|---|---|
| 1001 | Joseph | Conrad |
Por fim, a Tabela 5 mostra a junção entre um livro e seu autor, que, de várias formas, compreende os dados mais importantes que você já viu. Se autores e livros não podem ser relacionados, então nada disso é útil.
Tabela 5. A tabela de muitos para muitos dw_books_authors com dados a partir do SQLTester
| book_isbn | author_id |
|---|---|
| 1892295490 | 1001 |
A Listagem 15 é um pequeno programa utilitário que exclui
os dados inseridos pelo SQLTester. Conforme você
experimenta a ligação de dados SQL e adiciona livros e autores (o que deve com
certeza fazer), é possível atualizar esta classe para remover essas entradas. Manter
as classes separadas lhe dá uma chance de examinar o banco de dados entre inserções
e exclusões, que é uma parte importante no aprendizado e depuração de quaisquer
problemas que possam ocorrer.
Listagem 15. Excluindo os dados criados pelo SQLTester
import ibm.xml.castor.Author;
import ibm.xml.castor.Book;
import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;
public class SQLClean {
public static void main(String args[]) {
try {
JDOManager.loadConfiguration("jdo-conf.xml");
JDOManager jdoManager = JDOManager.createInstance("bmclaugh");
Database database = jdoManager.getDatabase();
database.begin();
try {
Book book = (Book)database.load(Book.class, "1892295490");
database.remove(book);
} catch (org.exolab.castor.jdo.ObjectNotFoundException ignored) { }
try {
Author author = (Author)database.load(Author.class, 1001);
database.remove(author);
} catch (org.exolab.castor.jdo.ObjectNotFoundException ignored) { }
database.commit();
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
O programa conta com a manipulação de exceções aninhada para lidar com situações nas quais procura por dados inexistentes para excluir, o que o torna um pouco mais robusto.
O Castor não persiste o seu relacionamento por padrão
Um ponto interessante no SQLTester (na Listagem 14) é que ambas as ocorrências de Author
e a ocorrência de Book são persistidas para o
banco de dados. De fato, retirando um desses dois, irá obter um livro ou autor, mas
não o outro, e não obterá quaisquer dados em dw_books_authors.
Para programadores Java, isto é de fato um pouco estranho. Se uma ocorrência de Book contém um Author, os dois
não estão vinculados? É possível persistir um livro sem seu autor? Bem, no Castor
sim, sendo este o comportamento padrão. Porém, se você deseja que o Castor siga em
frente em todos os relacionamentos como esse, pode emitir o seguinte comando:
database.setAutoStore(true); |
É necessário fazer isso antes que qualquer transação seja criada, assim, a Listagem 16 trata dessa tarefa com uma versão um pouco diferente da Listagem 14.
Listagem 16. Programa de teste para o armazenamento automático de relacionamentos
import java.util.Iterator;
import ibm.xml.castor.Author;
import ibm.xml.castor.Book;
import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;
public class SQLTester {
public static void main(String args[]) {
try {
JDOManager.loadConfiguration("jdo-conf.xml");
JDOManager jdoManager = JDOManager.createInstance("bmclaugh");
Database database = jdoManager.getDatabase();
database.setAutoStore(true);
database.begin();
Author author = new Author(1001, "Joseph", "Conrad");
Book book = new Book("1892295490", "Heart of Darkness", author);
// We can persist just the book, and Castor will handle the author, as well
// database.create(author);
database.create(book);
database.commit();
database.begin();
Book lookup = (Book)database.load(Book.class, "1892295490");
System.out.println("Located book is named " + lookup.getTitle());
System.out.println("Authors:");
for (Iterator i = lookup.getAuthors().iterator(); i.hasNext(); ) {
Author bookAuthor = (Author)i.next();
System.out.println(" " + bookAuthor.getFirstName() + " " +
bookAuthor.getLastName());
}
database.commit();
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
Experimente com isto (certifique-se de executar o SQLClean
primeiro, caso ainda tenha dados no seu banco de dados), e você irá obter os mesmo
resultados que antes—com exceção da chamada adicional database.create(). Embora isso seja apenas uma pequena melhora,
considera adicionar centenas (ou milhares!) de livros e autores. Salvar o trabalho
adicional é realmente importante.
Enquanto a ligação de dados XML está estabelecida e predominantemente está sendo acrescida em vez de alterada, JDO—tanto a versão da Sun quanto da Castor—ainda é uma API volátil. Porém, isto não significa que você deve evitá-la, pois a ligação de dados SQL e o JDO (de qualquer fornecedor) podem facilitar muito sua vida de programador. Use este artigo com um ponto de partida, e não como uma abordagem definitiva, para aprender sobre as tecnologias.
Experimente com a ligação de dados SQL e veja se ela pode ajudar o seu aplicativo (provavelmente irá). Em seguida, confira as versões da Sun e do Castor do JDO e veja o que acha delas. Finalmente, escolha uma delas para trabalhar. Mesmo que a versão do Castor não seja padrão, ela é mais flexível, e com o seu trabalho para se envolver com a Sun, o padrão oficial pode incorporar muitos recursos do Castor no futuro.
E o mais importante, melhore suas habilidades de programação. Aprenda como a ligação de dados SQL pode lhe ajudar, economize esforço e, o principal, aumente a qualidade do seu trabalho e dos aplicativos que desenvolve. Experimente usar as ligações de dados SQL e XML com o mesmo aplicativo para entender como o mesmo tipo de mapeamento é usado nas duas ligações. Você irá se beneficiar com isso, e provavelmente irá se livrar quase completamente do JDBC, e ninguém vai dizer que isso não é bom.
| Descrição | Nome | Tamanho | Método de download |
|---|---|---|---|
| Java source code | javaSourceCode.zip | 3KB | HTTP |
| Castor configuration files | castorConfigurationFiles.zip | 1KB | HTTP |
Informações sobre métodos de download
Aprender
- Data
binding with Castor, Part 1: Install and set up Castor (Brett McLaughlin,
developerWorks, novembro de 2007): No primeiro artigo da série, são abordados os
primeiros passos para executar o Castor no seu equipamento, incluindo download,
instalação, configuração, problemas de caminhos de classe, entre outros.
- Data
binding with Castor, Part 2: Marshall and unmarshall XML (Brett McLaughlin,
developerWorks, dezembro de 2007): Converta suas classes Java para XML e converta
este XML de volta em código Java. Além disso, aprenda como o Castor projeta e
trabalha com suas classes para que essas funcionem com a API.
- Data
binding with Castor, Part 3: Map between schemas (Brett McLaughlin,
developerWorks, janeiro de 2008): Converta dados em um inconveniente documento XML
para os seus objetos Java personalizados usando o Castor. Você não terá mais
restrições de nomes de elementos no seu documento XML, ou de nomes de variáveis de
membros em suas classes Java.
- O Web site do
Castor: Visite o hub on-line para saber tudo sobre o Castor.
- Classes do
Castor: Leia o JavaDoc.
- Introdução ao JDO do Castor: Uma ótima introdução para este mapeamento
relacional de objetos e estrutura de ligação de dados.
- Página dos Java
Data Objects (JDO) da Sun: Visite o local principal para aprender sobre a
versão oficial do JDO.
- JSR
12: Leia a especificação que define o JDO 1.0, os Java Data Objects
originais.
- JSR 243: Aprenda sobre esta especificação para o JDO 2.0 e os esforços para
facilitar o uso do JDO, padronizar o suporte ao banco de dados do JDO e expandir seu
escopo.
- Get started with
Castor JDO (Bruce Snyder, developerWorks, agosto de 2002): Leia este antigo
artigo do developerWorks. Parte da linguagem específica deste artigo está
desatualizada, mas os conceitos ainda são válidos.
- Practical
data binding (Brett McLaughlin, developerWorks, maio de 2004): Comece com o
artigo introdutório de uma série de várias partes para aprender mais sobre a API de
ligação de dados da SUN, a JAXB.
- Certificação XML da
IBM: Descubra como se tornar um Desenvolvedor Certificado pela IBM em XML e
tecnologias relacionadas.
- Biblioteca
técnica de XML: Veja na Zona de XML do developerWorks uma grande variedade
de artigos técnicos e dicas, tutoriais, padrões e Redbooks da IBM.
- Zona
de XML do developerWorks: Saiba tudo sobre XML.
- Eventos técnicos e webcasts do developerWorks: Fique atualizado sobre a
tecnologia por meio dessas sessões.
- Podcasts: Sintonize-se e informe-se com os especialistas técnicos da IBM.
Obter produtos e tecnologias
- Driver JDBC para MySQL:
Faça o download e aprenda sobre este driver JDBC Java original em
MySQL.com.
- Java and XML, Third Edition (Brett McLaughlin e Justin Edelson, O'Reilly
Media, Inc.): Aprenda tudo sobre XML, do começo ao fim, incluindo informações
extensivas sobre ligação de dados e mapeamento.
- Java and XML Data Binding (Brett McLaughlin, O'Reilly Media, Inc.): Neste
antigo livro, encontre detalhes sobre o Castor e os conceitos envolvidos na ligação
de dados.
- Castor Professional
Services: Procurando suporte ou ajuda pagos para o Castor? Confira os
serviços profissionais do Castor.
- DB2
Express-C: Faça o download desta versão sem custos do DB2, ótima para
experimentar o JDO. O banco de dados IMB DB2 é um banco de dados SQL que funciona
muito bem com o JDO do Castor, assim como com o JDO padrão da Sun.
- IBM Informix: Confira uma
linha de produtos que oferece produtos de servidores de banco de dados de força
industrial.
- Drivers JDBC para DB2 e
Informix: Experimente este pacote de drivers da DataDirect conveniente e de
fácil download.
- Software de teste IBM Elabore seu próximo projeto de desenvolvimento com o
software de teste IBM, disponível para download diretamente no
developerWorks.
Discutir
- Participar do fórum de discussão.
- Fóruns de
discussão da zona XML: Participe de várias discussões relacionadas a
XML.
- Zona de XML do developerWorks: Compartilhe suas ideias: Após ler esse
artigo, poste seus comentários e ideias neste fórum. Os editores da zona de XML são
moderadores do fórum e apreciam a sua participação.
- Blogs do developerWorks: Veja estes blogs e faça parte da comunidade do
developerWorks.

Brett McLaughlin é um autor de não ficção premiado e campeão de vendas. Seus livros sobre programação de computadores, home theater e análise e design já venderam mais de 100.000 cópias. Há quase 10 anos, ele escreve, edita e produz livros técnicos, e sente-se confortável tanto na frente de um processador de texto quanto tocando violão, correndo pela casa atrás dos seus dois filhos ou assistindo reprises de Arrested Development (Caindo na Real, no Brasil) com sua esposa. Seu último livro, Head First Object Oriented Analysis and Design, ganhou o prêmio Jolt Technical Book 2007. Além disso, seu clássico Java and XML continua sendo um dos trabalhos definitivos sobre o uso de tecnologias XML em linguagem Java.