5. Versão 1: Arquitetura Spring / JPA
Propõe-se escrever um aplicativo de console e um aplicativo gráfico que permitam elaborar o holerite das babás empregadas pela “Casa da Primeira Infância” de um município. Esse aplicativo terá a seguinte arquitetura:
![]() |
5.1. BD O banco de dados
Os dados estáticos necessários para elaborar a folha de pagamento serão armazenados em um banco de dados que, doravante, designaremos como dbpam. Esse banco de dados poderá conter as seguintes tabelas:
Estrutura:
chave primária | |
número de versão – aumenta a cada modificação da linha | |
número de previdência social do funcionário – único | |
nome do funcionário | |
nome próprio | |
seu endereço | |
sua cidade | |
seu CEP | |
chave estrangeira no campo [ID] da tabela [INDEMNITES] |
Seu conteúdo poderia ser o seguinte:

Estrutura:
chave primária | |
número de versão – aumenta a cada modificação da linha | |
porcentagem: contribuição social generalizada + contribuição para o pagamento da dívida social | |
porcentagem: contribuição social generalizada dedutível | |
porcentagem: previdência social, pensão de viuvez, aposentadoria | |
porcentagem: aposentadoria complementar + seguro-desemprego |
Seu conteúdo poderia ser o seguinte:
![]()
As alíquotas das contribuições sociais são independentes do empregado. A tabela anterior possui apenas uma linha.
chave primária | ||
número da versão – aumenta a cada modificação da linha | ||
índice de processamento – único | ||
preço líquido em euros por hora de plantão | ||
subsídio de manutenção em euros por dia de plantão | ||
Subsídio de alimentação em euros por dia de plantão | ||
Subsídio de férias remuneradas. Trata-se de uma porcentagem a ser aplicada ao salário-base. | ||
Seu conteúdo poderia ser o seguinte:

Observe-se que os subsídios podem variar de uma babá para outra. Na verdade, eles estão associados a uma babá específica por meio do índice salarial dela. Assim, a Sra. Marie Jouveinal, que possui um índice salarial de 2 (tabela EMPLOYES), tem um salário por hora de 2,1 euros (tabela INDEMNITES).
5.2. Método de cálculo do salário de uma babá
Apresentamos agora o método de cálculo do salário mensal de uma babá. Ele não pretende ser o método utilizado na prática. Tomamos como exemplo o salário da Sra. Marie Jouveinal, que trabalhou 150 horas em 20 dias durante o mês a ser pago.
Os seguintes elementos são levados em consideração: | | |
O salário-base da babá é calculado pela seguinte fórmula: | ||
Uma determinada quantia de contribuições sociais devem ser deduzidas desse salário base: | | |
Total das contribuições sociais: | ||
Além disso, a babá tem direito, por cada dia trabalhado, a um auxílio de subsistência e a um auxílio-alimentação. A esse título, ela recebe os seguintes auxílios: | | |
No final, o salário líquido a ser pago à babá é o seguinte: |
5.3. Funcionamento do aplicativo de console
Aqui está um exemplo de execução do aplicativo de console em uma janela do DOS:
Vamos escrever um programa que receberá as seguintes informações:
- número de previdência social da babá (254104940426058 no exemplo — linha 1)
- número total de horas trabalhadas (150 no exemplo — linha 1)
- número total de dias trabalhados (20 no exemplo — linha 1)
Observamos que:
- linhas 9-14: exibem as informações relativas à funcionária cujo número de previdência social foi fornecido
- linhas 17-20: exibem as alíquotas das diferentes contribuições
- linhas 23-26: exibem os subsídios associados ao índice salarial do funcionário (aqui, o índice 2)
- linhas 29-33: exibem os elementos que compõem o salário a ser pago
O aplicativo sinaliza eventuais erros:
Chamada sem parâmetros:
dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar
Syntaxe : pg num_securite_sociale nb_heures_travaillées nb_jours_travaillés
Chamada com dados incorretos:
dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar 254104940426058 150x 20x
Le nombre d'heures travaillées [150x] est erroné
Le nombre de jours travaillés [20x] est erroné
Chamada com número de previdência social incorreto:
dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar xx 150 20
L'erreur suivante s'est produite : L'employé de n°[xx] est introuvable
5.4. Funcionamento do aplicativo gráfico
O aplicativo gráfico permite o cálculo dos salários das babás por meio de um formulário Swing:
![]() |
- as informações que antes eram passadas como parâmetros para o programa de console agora são inseridas por meio dos campos de entrada [1, 2, 3].
- O botão [4] solicita o cálculo do salário
- o formulário exibe os diferentes componentes do salário até o valor líquido a ser pago [5]
A lista suspensa [1, 6] não exibe os números SS dos funcionários, mas sim seus nomes e sobrenomes. Presume-se aqui que não há dois funcionários com o mesmo nome e sobrenome.
5.5. Criação do banco de dados
Executamos o WampServer e utilizamos a ferramenta PhpMyAdmin [1]:
![]() |
- no [2], selecionamos a opção [Bases de données],
![]() |
- em [3], cria-se um banco de dados [dbpam_hibernate],
- em [4], o banco de dados criado. Selecionamos-o,
![]() |
- em [5], deseja-se importar um script SQL,
- em [6], usa-se o botão [Parcourir] para selecionar o arquivo,
![]() |
- em [7,8], seleciona-se o script SQL,
- em [9], executa-se o script,
![]() |
- em [10], as tabelas foram criadas. Seu conteúdo é o seguinte:
tabela EMPLOYES

tabela INDEMNITES

tabela COTISATIONS
![]()
5.6. Implementação JPA
5.6.1. Camada JPA / Hibernate
Vamos configurar a camada JPA no seguinte ambiente:
![]() |
Um programa de console trabalhará com o banco de dados. Para isso, é necessário:
- ter um banco de dados,
- ter o driver JDBC do SGBD, neste caso o MySQL,
- implementar a camada JPA com o Hibernate,
- escrever o programa de console.
Criamos o projeto Maven [mv-pam-jpa-hibernate] [1]:
![]() |
Na arquitetura do nosso aplicativo, precisamos dos seguintes elementos:
- o banco de dados,
- o driver JDBC do SGBD MySQL,
- a camada JPA / Hibernate (entidades e configuração),
- o programa de console de teste.
5.6.1.1. O banco de dados
Primeiro, vamos criar o banco de dados vazio. Executamos o WampServer e utilizamos a ferramenta PhpMyAdmin [1]:
![]() |
- no [2], selecionamos a opção [Bases de données],
![]() |
- em [3], cria-se um banco de dados [dbpam_hibernate],
- em [4], o banco de dados criado.
5.6.1.2. Configuração da camada JPA
A conexão entre a camada JDBC e o banco de dados é feita no arquivo [persistence.xml], que configura a camada JPA. Esse arquivo pode ser criado com o NetBeans:
![]() |
- na guia [services] [1], conecta-se ao banco de dados com o driver JDBC de MySQL [2],
- em [3], o nome do banco de dados ao qual se deseja conectar.
- em [4], o nome do banco de dados,
- em [5], conectamo-nos como root sem senha,
- no [6], é possível testar a conexão,
- em [7], a conexão foi bem-sucedida.
![]() |
- a conexão aparece em [8] e em [9],
- em [10], adiciona-se um novo elemento ao projeto,
![]() |
- em [11], escolhe-se a categoria [Persistence] e, em [12], o elemento [Persistence Unit],
- em [13], atribui-se um nome a essa unidade de persistência,
- em [14], escolhe-se uma implementação do Hibernate,
- em [15], designa-se a conexão que acabamos de criar com o banco de dados MySQL,
- em [16], indicamos que, ao instanciar a camada JPA, esta deve criar as tabelas correspondentes às entidades JPA do projeto.
Ao final do assistente, é gerado o arquivo [persistence.xml]:
![]() |
- o arquivo aparece em um novo ramo do projeto, na pasta [META-INF] [1],
- que corresponde à pasta [src/main/resources] do projeto [2,3].
Seu conteúdo é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
- linha 3: o nome da unidade de persistência e o tipo de transações. RESOURCE_LOCAL indica que o próprio projeto gerencia as transações. Aqui, caberá ao programa de console fazer isso,
- linha 4: a implementação JPA utilizada é o Hibernate,
- linhas 6-9: as características JDBC da conexão com o banco de dados,
- linha 11: solicita a criação das tabelas correspondentes às entidades JPA. Na verdade, o NetBeans gera aqui uma configuração incorreta. A configuração deve ser a seguinte:
<property name="hibernate.hbm2ddl.auto" value="create"/>
Com a opção create**, o Hibernate, ao instanciar a camada JPA, exclui e, em seguida, cria as tabelas correspondentes às entidades JPA. A opção create-drop** faz o mesmo, mas, ao final do ciclo de vida da camada JPA, ela exclui todas as tabelas. Existe outra opção:
<property name="hibernate.hbm2ddl.auto" value="update"/>
Essa opção cria as tabelas caso elas não existam, mas não as exclui se já existirem.
Adicionaremos mais três propriedades à configuração do Hibernate:
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
Elas solicitam que o Hibernate exiba os comandos SQL que ele envia ao banco de dados. O arquivo completo fica, portanto, da seguinte forma:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>jpa.Cotisation</class>
<class>jpa.Employe</class>
<class>jpa.Indemnite</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>
5.6.1.3. As dependências
Voltemos à arquitetura do projeto:
![]() |
Configuramos a camada JPA por meio do arquivo [persistence.xml]. A implementação escolhida foi o Hibernate. Isso gerou dependências no projeto:
![]() |
Essas dependências se devem à inclusão do Hibernate no projeto. Precisamos adicionar outra dependência: o driver JDBC do MySQL, que implementa a camada JDBC da arquitetura. Alteramos o arquivo [pom.xml] da seguinte maneira:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
</dependency>
...
<dependency>
<groupId>org.hibernate.common</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>4.0.1.Final</version>
</dependency>
</dependencies>
As linhas 8 a 12 adicionam a dependência do driver JDBC do MySQL.
5.6.1.4. As entidades JPA
![]() |
Pergunta: Seguindo o procedimento do exemplo do parágrafo 4.4, gere as entidades [Cotisation, Indemnite, Employe].
Notas:
- as entidades farão parte de um pacote denominado [jpa],
- cada entidade terá um número de versão,
- se duas entidades estiverem ligadas por uma relação, apenas a relação principal @ManyToOne será criada. A relação inversa @OneToMany não será criada.
5.6.1.5. O código da classe principal
Incluímos no projeto as entidades JPA e [1], desenvolvidas anteriormente:
![]() |
e, em seguida, adicionamos [2], a seguinte classe [main.Main]:
package main;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class Main {
public static void main(String[] args) {
// basta criar o Entity Manager para construir a camada JPA
EntityManagerFactory emf = Persistence.createEntityManagerFactory("mv-pam-jpa-hibernatePU");
EntityManager em=emf.createEntityManager();
// liberação de recursos
em.close();
emf.close();
}
}
- linha 10: criamos a EntityManagerFactory da unidade de persistência denominada [mv-pam-jpa-hibernatePU]. Esse nome provém do arquivo [persistence.xml]:
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>
- linha 12: cria-se o EntityManager. Essa criação gera a camada JPA. O arquivo [persistence.xml] será processado e, portanto, as tabelas do banco de dados serão criadas,
- linhas 14-15: liberam-se os recursos.
5.6.1.6. Tests
Voltemos à arquitetura do nosso projeto:
![]() |
Todas as camadas foram implementadas. Executamos o projeto [2].
![]() |
Os resultados no console são os seguintes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | |
Encontram-se na console apenas logs do Hibernate, uma vez que o programa executado não realiza nenhuma outra ação além de instanciar a camada JPA. Observe os seguintes pontos:
- linha 43: o Hibernate tenta excluir a chave estrangeira da tabela [EMPLOYES],
- linhas 51-55: exclusão das três tabelas,
- linha 57: criação da tabela [COTISATIONS],
- linha 67: criação da tabela [EMPLOYES],
- linha 80: criação da tabela [INDEMNITES],
- linha 91: criação da chave estrangeira da tabela [EMPLOYES].
No NetBeans, é possível visualizar as tabelas na conexão criada anteriormente:
![]() |
As tabelas criadas dependem tanto da implementação da camada JPA utilizada quanto da implementação da camada SGBD utilizada. Assim, uma implementação JPA / EclipseLink com o mesmo banco de dados pode gerar tabelas diferentes. É isso que veremos agora.
5.6.2. Camada JPA / EclipseLink
Vamos criar um novo projeto Maven no seguinte ambiente:
![]() |
Seguiremos o procedimento descrito no parágrafo anterior:
- criar uma base de dados MySQL [dbpam_eclipselink]. Usaremos o script [dbpam_eclipselink.sql] para gerá-la,
- criar o arquivo [persistence.xml] do projeto. Utilizar a implementação JPA 2.0 EclipseLink,
- adicionar, nas dependências geradas, a dependência do driver JDBC do MySQL,
- adicionar as entidades JPA e o programa de console,
- realizar os testes.
O arquivo [persistence.xml] ficará da seguinte forma:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="pam-jpa-eclipselinkPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>jpa.Cotisation</class>
<class>jpa.Employe</class>
<class>jpa.Indemnite</class>
<properties>
<property name="eclipselink.target-database" value="MySQL"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="eclipselink.logging.level" value="FINE"/>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
- as propriedades 9 a 13 foram geradas pelo assistente do NetBeans,
- linha 14: essa propriedade nos permite definir o nível de logs do EclipseLink. O nível FINE nos permite saber quais comandos SQL o EclipseLink emitirá no banco de dados,
- linha 15: ao instanciar a camada JPA / EclipseLink, as tabelas das entidades JPA serão destruídas e, em seguida, criadas.
Os resultados obtidos no console são os seguintes:
- linhas 26-30: conexão com o banco de dados MySQL,
- linhas 31-34: confirmação de que a conexão foi bem-sucedida,
- linha 36: exclusão da chave estrangeira da tabela [EMPLOYES],
- linha 37: exclusão da tabela [COTISATIONS],
- linha 38: criação da tabela [COTISATIONS]. É interessante notar que a chave primária ID não possui o atributo MySQL auto_increment. Isso significa que não é MySQL que gera os valores da chave primária,
- linha 39: exclusão da tabela [EMPLOYES],
- linha 40: criação da tabela [EMPLOYES]. Sua chave primária ID não possui o atributo MySQL auto_increment,
- linha 41: exclusão da tabela [INDEMNITES],
- linha 42: criação da tabela [INDEMNITES]. Sua chave primária ID não possui o atributo MySQL auto_increment,
- linha 43: criação da chave estrangeira da tabela [EMPLOYES] para a tabela [INDEMNITES],
- linha 44: criação da tabela [SEQUENCE]. Ela será utilizada para gerar as chaves primárias das três tabelas anteriores,
- linha 47: ocorre uma exceção, pois essa tabela já existia,
- linhas 51-53: inicialização da tabela [SEQUENCE].
A existência das tabelas geradas pode ser verificada no NetBeans [1]:
![]() |
Portanto, a partir das mesmas entidades JPA, as implementações JPA, Hibernate e EclipseLink não geram as mesmas tabelas. No restante do documento, quando a implementação JPA utilizada for:
- Hibernate, será utilizada a base de dados [dbpam_hibernate];
- EclipseLink, será utilizado o banco de dados [dbpam_eclipselink].
5.6.3. Tarefa a ser realizada
Seguindo a mesma abordagem da etapa anterior,
- crie e teste um projeto [mv-pam-jpa-hibernate-oracle] utilizando uma implementação JPA do Hibernate e uma implementação SGBD do Oracle,
- criar e testar um projeto [mv-pam-jpa-hibernate-mssql] utilizando uma implementação JPA do Hibernate e um servidor SGBD SQL,
- criar e testar um projeto [mv-pam-jpa-eclipselink-oracle] utilizando uma implementação JPA EclipseLink e um servidor Oracle SGBD,
- criar e testar um projeto [mv-pam-jpa-eclipselink-mssql] utilizando uma implementação JPA EclipseLink e um servidor SGBD SQL,
5.6.4. Lazy ou Eager?
Voltemos a uma possível definição da entidade [Employe]:
package jpa;
...
@Entity
@Table(name="EMPLOYES")
public class Employe implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
@Column(name="VERSION",nullable=false)
private int version;
@Column(name="SS", nullable=false, unique=true, length=15)
private String SS;
@Column(name="NOM", nullable=false, length=30)
private String nom;
@Column(name="PRENOM", nullable=false, length=20)
private String prenom;
@Column(name="ADRESSE", nullable=false, length=50)
private String adresse;
@Column(name="VILLE", nullable=false, length=30)
private String ville;
@Column(name="CP", nullable=false, length=5)
private String codePostal;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="INDEMNITE_ID",nullable=false)
private Indemnite indemnite;
...
}
As linhas 27 a 29 definem a chave estrangeira da tabela [EMPLOYES] para a tabela [INDEMNITES]. O atributo fetch da linha 27 define a estratégia de pesquisa do campo indemnite da linha 29. Existem dois modos:
- FetchType.LAZY: quando um funcionário é pesquisado, o valor da indenização correspondente a ele não é retornado. Ele será retornado quando o campo [Employe].indemnite for referenciado pela primeira vez.
- FetchType.EAGER: quando um funcionário é pesquisado, o valor da indenização correspondente a ele é exibido. Esse é o modo padrão quando nenhum modo é especificado.
Para entender a utilidade da opção FetchType.LAZY, podemos considerar o seguinte exemplo. Uma lista de funcionários sem as indenizações é exibida em uma página da web com um link [Details]. Ao clicar nesse link, são exibidas as indenizações do funcionário selecionado. Observa-se que:
- para exibir a primeira página, não é necessário incluir os funcionários com seus benefícios. O modo FetchType.LAZY é, portanto, adequado;
- para exibir a segunda página com os detalhes, é necessário fazer uma consulta adicional ao banco de dados para obter os subsídios do funcionário selecionado.
O modo FetchType.LAZY evita trazer dados em excesso dos quais o aplicativo não precisa imediatamente. Vejamos um exemplo.
O projeto [mv-pam-jpa-hibernate] é duplicado:
![]() |
- em [1], o projeto é copiado;
- em [2], indicamos a pasta da cópia e, em [3], seu nome,
- em [4], o novo projeto tem o mesmo nome que o antigo. Alteramos isso:
![]() |
- em [1], renomeamos o projeto,
- em [2], renomeamos o projeto e seu artifactId,
- em [3], o novo projeto.
Modificamos o programa [Main.java] da seguinte maneira:
package main;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import jpa.Employe;
public class Main {
// a consulta JPQL abaixo retorna um funcionário
// a chave estrangeira [Employe].indemnite está em FetchType.LAZY
public static void main(String[] args) {
// basta criar o Entity Manager para construir a camada JPA
EntityManagerFactory emf = Persistence.createEntityManagerFactory("pam-jpa-hibernatePU");
// primeira tentativa
EntityManager em = emf.createEntityManager();
Employe employe = (Employe) em.createQuery("select e from Employe e where e.nom=:nom").setParameter("nom", "Jouveinal").getSingleResult();
em.close();
// exibimos o funcionário
try {
System.out.println(employe);
} catch (Exception ex) {
System.out.println(ex);
}
// segunda tentativa
em = emf.createEntityManager();
employe = (Employe) em.createQuery("select e from Employe e left join fetch e.indemnite where e.nom=:nom").setParameter("nom", "Jouveinal").getSingleResult();
// liberar os recursos
em.close();
// exibe o funcionário
try {
System.out.println(employe);
} catch (Exception ex) {
System.out.println(ex);
}
// liberação de recursos
emf.close();
}
}
- linha 15: criamos o EntityManagerFactory a partir da camada JPA,
- linha 17: obtemos o EntityManager, que nos permite interagir com a camada JPA,
- linha 18: solicita-se o funcionário com o nome Jouveinal,
- linha 19: fecha-se o EntityManager. Isso tem como efeito fechar o contexto de persistência.
- linha 22: exibimos o funcionário recebido.
A classe [Employe] é a seguinte:
package jpa;
...
@Entity
@Table(name="EMPLOYES")
public class Employe implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
@Column(name="VERSION",nullable=false)
private int version;
@Column(name="SS", nullable=false, unique=true, length=15)
private String SS;
@Column(name="NOM", nullable=false, length=30)
private String nom;
@Column(name="PRENOM", nullable=false, length=20)
private String prenom;
@Column(name="ADRESSE", nullable=false, length=50)
private String adresse;
@Column(name="VILLE", nullable=false, length=30)
private String ville;
@Column(name="CP", nullable=false, length=5)
private String codePostal;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="INDEMNITE_ID",nullable=false)
private Indemnite indemnite;
/**
* Returns a string representation of the object. This implementation constructs
* that representation based on the id fields.
* @return a string representation of the object.
*/
@Override
public String toString() {
return "jpa.Employe[id=" + getId()
+ ",version="+getVersion()
+",SS="+getSS()
+ ",nom="+getNom()
+ ",prenom="+getPrenom()
+ ",adresse="+getAdresse()
+",ville="+getVille()
+",code postal="+getCodePostal()
+",indice="+getIndemnite().getIndice()
+"]";
}
...
}
- linha 27: o campo indemnite é revertido para o modo LAZY,
- linha 47: utiliza o campo indemnite. Se o método toString for chamado enquanto o campo indemnite ainda não tiver sido revertido, ele será revertido nesse momento. A menos que o contexto de persistência tenha sido fechado, como no exemplo.
Voltemos ao código do [Main]:
- linhas 21-25: deveria ocorrer uma exceção. De fato, o método toString será chamado. Ele utilizará o campo indemnite. Esse campo será procurado. Como o contexto de persistência foi fechado, a entidade [Employe] recuperada não existe mais, daí a exceção.
- linha 27: cria-se um novo EntityManager,
- linha 28: solicita-se o funcionário Jouveinal, indicando explicitamente na consulta JPQL o valor da indenização correspondente. Essa solicitação explícita é necessária porque o modo de pesquisa dessa indenização é LAZY,
- linha 30: encerra-se o EntityManager,
- linhas 32-36: exibe-se novamente o funcionário. Não deve haver nenhuma exceção.
Para executar o projeto, é necessário um banco de dados preenchido. Ele será criado seguindo os passos descritos no parágrafo 5.5. Além disso, o arquivo [persistence.xml] deve ser modificado:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>jpa.Cotisation</class>
<class>jpa.Employe</class>
<class>jpa.Indemnite</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
</properties>
</persistence-unit>
</persistence>
- retiramos a opção que criava as tabelas. O banco de dados aqui já existe e está preenchido,
- e removemos as opções que faziam com que o Hibernate registrasse os comandos SQL que ele enviava para o banco de dados.
A execução do projeto gera as duas exibições a seguir no console:
- linha 1: a exceção que ocorreu ao tentar buscar a indenização que faltava, já que a sessão estava encerrada. Percebe-se que a indenização não foi recuperada devido ao modo LAZY,
- linha 2: o funcionário com seu adicional obtido por meio de uma consulta que contornou o modo LAZY.
5.6.5. Tarefa a ser realizada
Seguindo um procedimento semelhante ao que acabou de ser seguido, crie um projeto [mv-pam-pa-eclipselink-lazy] que mostre o comportamento do EclipseLink em comparação com o modo LAZY.
Obtêm-se os seguintes resultados:
No modo LAZY, ambas as consultas retornaram o valor da indenização junto com o funcionário. Ao pesquisar na internet sobre essa anomalia, descobre-se que a anotação [FetchType.LAZY] (linha 1):
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="INDEMNITE_ID",nullable=false)
private Indemnite indemnite;
não é uma ordem, mas uma sugestão. O implementador JPA não é obrigado a segui-la. Vemos, portanto, que o código às vezes passa a depender da implementação JPA utilizada. É possível definir, por meio de configuração, o comportamento esperado para o modo LAZY no EclipseLink.
5.6.6. Para a continuação
A arquitetura do aplicativo a ser construído é a seguinte:
![]() |
Para o restante do documento, duplicaremos o projeto Maven [mv-pam-jpa-hibernate] nos projetos [mv-pam-spring-hibernate] e [1, 2, 3]:
![]() |
- e, em seguida, renomearemos o novo projeto para [4, 5, 6].
Alteraremos as dependências do novo projeto. O arquivo [pom.xml] passa a ter o seguinte conteúdo:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-pam-spring-hibernate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-pam-spring-hibernate</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>swing-layout</id>
<layout>default</layout>
<name>Repository for library Library[swing-layout]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
- linhas 25-31: a dependência para os testes JUnit,
- linhas 32-41: as dependências para o pool de conexões do Apache DBCP,
- linhas 42-65: as dependências para o framework Spring,
- linhas 67-71: as dependências para a implementação JPA / Hibernate,
- linhas 72-76: a dependência do driver JDBC do MySQL,
- linhas 77-81: a dependência da interface Swing. Ela é adicionada automaticamente pelo NetBeans quando se adiciona uma interface Swing ao projeto.
Além disso, serão geradas as duas bases MySQL:
- [dbpam_hibernate] a partir do script [dbpam_hibernate.sql],
- [dbpam_eclipselink] a partir do script [dbpam_eclipselink.sql],
5.7. , interfaces das camadas [metier] e [DAO]
Voltemos à arquitetura do aplicativo:
![]() |
Na arquitetura acima, qual interface a camada [DAO] deve oferecer à camada [metier] e qual interface a camada [metier] deve oferecer à camada [ui]? Uma primeira abordagem para definir as interfaces das diferentes camadas é examinar os diferentes casos de uso (use cases) do aplicativo. Aqui, temos dois, dependendo da interface de usuário escolhida: console ou formulário gráfico.
Vamos examinar o modo de uso da aplicação em console:
A aplicação recebe três informações do usuário (ver linha 1 acima)
- o número do plano de saúde da babá
- o número de horas trabalhadas no mês
- o número de dias trabalhados no mês
A partir dessas informações e de outras registradas em arquivos de configuração, o aplicativo exibe as seguintes informações:
- linhas 4-6: os valores inseridos
- linhas 8-10: as informações relacionadas ao funcionário cujo número de previdência social foi fornecido
- linhas 12-14: as alíquotas das diferentes contribuições sociais
- linhas 16-17: os diversos valores pagos à babá
- linhas 19-24: os elementos da folha de pagamento da babá
A camada [metier] deve fornecer algumas informações à camada [ui]:
- as informações relacionadas a uma babá identificada por seu número de previdência social. Essas informações se encontram na tabela [EMPLOYES]. Isso permite exibir as linhas 6-8.
- os valores das diversas alíquotas de contribuições sociais a serem deduzidas do salário bruto. Essas informações estão na tabela [COTISATIONS]. Isso permite exibir as linhas 10 a 12.
- os valores das diversas indenizações relacionadas à função de babá. Essas informações estão na tabela [INDEMNITES]. Isso permite exibir as linhas 14-15.
- os componentes do salário exibidos nas linhas 18 a 22.
A partir disso, seria possível definir um primeiro lançamento da interface [IMetier], apresentada pela camada [metier] à camada [ui]:
- linha 1: os elementos da camada [metier] são inseridos no pacote [metier]
- linha 5: o método [ calculerFeuilleSalaire ] recebe como parâmetros as três informações obtidas pela camada [ui] e retorna um objeto do tipo [FeuilleSalaire] contendo as informações que a camada [ui] exibirá no console. A classe [FeuilleSalaire] poderia ser a seguinte:
- linha 9: o funcionário ao qual se refere a folha de pagamento — informação nº 1 exibida pela camada [ui]
- linha 10: as diferentes alíquotas de contribuição — informação nº 2 exibida pela camada [ui]
- linha 11: os diversos subsídios vinculados ao índice do funcionário — informação nº 3 exibida pela camada [ui]
- linha 12: os elementos que compõem o salário do funcionário — informação n.º 4 exibida pela camada [ui]
Um segundo caso de uso da camada [métier] surge com a interface gráfica:
![]() |
Vê-se acima que a lista suspensa [1, 2] apresenta todos os funcionários. Essa lista deve ser solicitada à camada [métier]. A interface , que utiliza essa camada, passa então a ter a seguinte aparência:
- linha [10]: o método que permitirá que a camada [ui] solicite a lista de todos os funcionários à camada [métier].
A camada [metier] só pode inicializar os campos [Employe, Cotisation, Indemnite] do objeto [FeuilleSalaire] acima consultando a camada [DAO], pois essas informações estão nas tabelas do banco de dados. O mesmo se aplica à obtenção da lista de todos os funcionários. É possível criar uma única interface [DAO] que gerencie o acesso às três entidades [Employe, Cotisation, Indemnite]. No entanto, decidimos aqui criar uma interface [DAO] para cada entidade.
A interface [DAO] para o acesso às entidades [Cotisation] da tabela [COTISATIONS] será a seguinte:
- na linha 6, a interface [ICotisationDao] gerencia o acesso à entidade [Cotisation] e, portanto, à tabela [COTISATIONS] do banco de dados. Nosso aplicativo precisa apenas do método [findAll] da linha 16, que permite recuperar todo o conteúdo da tabela [COTISATIONS]. Aqui, quisemos abordar um caso mais geral, em que todas as operações CRUD (Create, Read, Update, Delete) são realizadas na entidade.
- linha 8: o método [create] cria uma nova entidade [Cotisation]
- linha 10: o método [edit] modifica uma entidade [Cotisation] existente
- linha 12: o método [destroy] exclui uma entidade [Cotisation] existente
- linha 14: o método [find] permite recuperar uma entidade [Cotisation] existente por meio de seu identificador id
- linha 16: o método [findAll] retorna em uma lista todas as entidades [Cotisation] existentes
Vamos nos deter na assinatura do método [create]:
O método create possui um parâmetro cotisation do tipo Cotisation. O parâmetro cotisation deve ser persistido, c.a.d. aqui inserido na tabela [COTISATIONS]. Antes dessa persistência, o parâmetro cotisation possui um identificador id sem valor. Após a persistência, o campo id possui um valor que é a chave primária do registro adicionado à tabela [COTISATIONS]. O parâmetro cotisation é, portanto, um parâmetro de entrada/saída do método create. Não parece necessário que o método create retorne também o parâmetro cotisation como resultado. Como o método chamador possui uma referência ao objeto [Cotisation cotisation], caso este seja modificado, ele terá acesso ao objeto modificado, uma vez que possui uma referência a ele. Portanto, ela pode saber o valor que o método create atribuiu ao campo id do objeto [Cotisation cotisation]. A assinatura do método poderia, portanto, ser mais simples:
Ao escrever uma interface, é importante lembrar que ela pode ser utilizada em dois contextos diferentes: local e distant. No contexto local, o método chamador e o método chamado são executados no mesmo JVM:
![]() |
Se a camada [metier] chamar o método create da camada [DAO], ela possui, de fato, uma referência no parâmetro [Cotisation cotisation] que é passada para o método.
No contexto distant, o método chamador e o método chamado são executados em JVM diferentes:
![]() |
No exemplo acima, a camada [metier] é executada na JVM 1 e a camada [DAO] na JVM 2, em duas máquinas diferentes. As duas camadas não se comunicam diretamente. Entre elas, intercala-se uma camada que chamaremos de camada de comunicação [1]. Esta é composta por uma camada de transmissão [2] e uma camada de recepção [3]. O desenvolvedor geralmente não precisa escrever essas camadas de comunicação. Elas são geradas automaticamente por ferramentas de software. A camada [metier] é escrita como se fosse executada na mesma JVM que a camada [DAO]. Portanto, não há nenhuma modificação no código.
O mecanismo de comunicação entre a camada [metier] e a camada [DAO] é o seguinte:
- a camada [metier] chama o método create da camada [DAO], passando-lhe o parâmetro [Cotisation cotisation1]
- esse parâmetro é, na verdade, passado para a camada de emissão [2]. Esta transmitirá pela rede o valor do parâmetro cotisation1 e não sua referência. A forma exata desse valor depende do protocolo de comunicação utilizado.
- A camada de recepção [3] recuperará esse valor e, a partir dele, reconstruirá um objeto [Cotisation cotisation2], que é uma réplica do parâmetro inicial enviado pela camada [metier]. Agora temos dois objetos idênticos (em termos de conteúdo) em duas camadas JVM diferentes: cotisation1 e cotisation2.
- A camada de recepção passará o objeto cotisation2 para o método create da camada [DAO], que o armazenará no banco de dados. Após essa operação, o campo id do objeto cotisation2 foi inicializado com a chave primária do registro adicionado à tabela [COTISATIONS]. Esse não é o caso do objeto cotisation1, ao qual a camada [metier] faz referência. Se quisermos que a camada [metier] tenha uma referência ao objeto cotisation2, é preciso enviá-la a ela. Por isso, é necessário alterar a assinatura do método create da camada [DAO]:
- Com essa nova assinatura, o método create retornará como resultado o objeto persistido cotisation2. Esse resultado é retornado à camada de recepção [3], que havia chamado a camada [DAO]. Esta, por sua vez, retornará o valor (e não a referência) de cotisation2 à camada de emissão [2].
- A camada de emissão [2] recuperará esse valor e, a partir dele, reconstruirá um objeto [Cotisation cotisation3], que é uma representação do resultado retornado pelo método create da camada [DAO].
- O objeto [Cotisation cotisation3] é devolvido ao método da camada [metier], cuja chamada ao método create da camada [DAO] havia iniciado todo esse mecanismo. A camada [metier] pode, portanto, conhecer o valor da chave primária atribuído ao objeto [Cotisation cotisation1], cuja persistência ela havia solicitado: trata-se do valor do campo id de cotisation3.
A arquitetura anterior não é a mais comum. É mais frequente encontrar as camadas [metier] e [DAO] na mesma JVM:
![]() |
Nessa arquitetura, são os métodos da camada [metier] que devem retornar resultados, e não os da camada [DAO]. No entanto, a seguinte assinatura do método create da camada [DAO]:
permite que não façamos suposições sobre a arquitetura efetivamente implementada. Utilizar assinaturas que funcionem independentemente da arquitetura escolhida, seja ela local ou remota, implica que, no caso de um método chamado modificar alguns de seus parâmetros:
- esses parâmetros também devem fazer parte do resultado do método chamado
- o método chamador deve utilizar o resultado do método chamado e não as referências dos parâmetros modificados que ele transmitiu ao método chamado.
Assim, mantemos a possibilidade de migrar de uma arquitetura locale para uma arquitetura distante sem alteração de código. Reexaminemos, sob essa perspectiva, a interface [ICotisationDao]:
- linha 8: o caso do método create foi tratado
- linha 10: o método edit utiliza seu parâmetro [Cotisation cotisation1] para atualizar o registro da tabela [COTISATIONS] que possui a mesma chave primária que o objeto cotisation. Ele retorna como resultado o objeto cotisation2, que representa o registro modificado. O parâmetro cotisation1, por sua vez, não é alterado. O método deve retornar cotisation2 como resultado, independentemente de se estar no contexto de uma arquitetura distante ou locale.
- linha 12: o método destroy exclui o registro da tabela [COTISATIONS] que possui a mesma chave primária que o objeto cotisation passado como parâmetro. Este não é alterado. Portanto, não precisa ser retornado.
- linha 14: o parâmetro id do método find não é alterado pelo método. Ele não precisa fazer parte do resultado.
- linha 16: o método findAll não possui parâmetros. Portanto, não é necessário analisá-lo.
Por fim, apenas a assinatura do método create deve ser adaptada para ser utilizável no contexto de uma arquitetura distante. Os raciocínios anteriores serão válidos para as demais interfaces [DAO]. Não vamos repeti-los e utilizaremos diretamente assinaturas que podem ser usadas tanto no contexto de uma arquitetura distante quanto no de uma locale.
A interface [DAO] para o acesso às entidades [Indemnite] da tabela [INDEMNITES] será a seguinte:
- Na linha 6, a interface [IIndemniteDao] gerencia os acessos à entidade [Indemnite] e, portanto, à tabela [INDEMNITES] do banco de dados. Nosso aplicativo precisa apenas do método [findAll] da linha 16, que permite recuperar todo o conteúdo da tabela [INDEMNITES]. Aqui, quisemos abordar um caso mais geral, em que todas as operações CRUD (Create, Read, Update, Delete) são realizadas na entidade.
- linha 8: o método [create] cria uma nova entidade [Indemnite]
- linha 10: o método [edit] modifica uma entidade [Indemnite] existente
- linha 12: o método [destroy] exclui uma entidade [Indemnite] existente
- linha 14: o método [find] permite localizar uma entidade [Indemnite] existente por meio de seu identificador id
- linha 16: o método [findAll] retorna em uma lista todas as entidades [Indemnite] existentes
A interface [DAO] para acessar as entidades [Employe] da tabela [EMPLOYES] será a seguinte:
- na linha 6, a interface [IEmployeDao] gerencia o acesso à entidade [Employe] e, portanto, à tabela [EMPLOYES] do banco de dados. Nosso aplicativo precisa apenas do método [findAll] da linha 16, que permite recuperar todo o conteúdo da tabela [EMPLOYES]. Aqui, quisemos abordar um caso mais geral, em que todas as operações CRUD (Create, Read, Update, Delete) são realizadas na entidade.
- linha 8: o método [create] cria uma nova entidade [Employe]
- linha 10: o método [edit] modifica uma entidade [Employe] existente
- linha 12: o método [destroy] exclui uma entidade [Employe] existente
- linha 14: o método [find] permite localizar uma entidade [Employe] existente por meio de seu identificador id
- linha 16: o método [find(String SS)] permite recuperar uma entidade [Employe] existente por meio de seu número SS. Vimos que esse método era necessário para o aplicativo de console.
- linha 18: o método [findAll] retorna, em uma lista, todas as entidades [Employe] existentes. Vimos que esse método era necessário para o aplicativo gráfico.
5.8. A classe [PamException]
A camada [DAO] trabalhará com a classe API e JDBC do Java. Essa API lança exceções controladas do tipo [SQLException], que apresentam duas desvantagens:
- elas tornam o código mais pesado, pois este deve, obrigatoriamente, lidar com essas exceções por meio de try/catch.
- elas devem ser declaradas na assinatura dos métodos da interface [IDao] por meio de um “throws SQLException”. Isso impede que essa interface seja implementada por classes que lançariam uma exceção controlada de um tipo diferente de [SQLException].
Para resolver esse problema, a camada [DAO] “repassará” apenas exceções não controladas do tipo [PamException].
![]() |
- a camada [JDBC] lança exceções do tipo [SQLException]
- a camada [JPA] lança exceções específicas da implementação JPA utilizada
- a camada [DAO] lança exceções do tipo [PamException] não controladas
Isso tem duas consequências:
- a camada [metier] não terá a obrigação de tratar as exceções da camada [DAO] com try/catch. Ela poderá simplesmente deixá-las subir até a camada [ui].
- os métodos da interface [IDao] não precisam incluir na sua assinatura a natureza da exceção [PamException], o que permite implementar essa interface com classes que lançariam outro tipo de exceção não controlada.
A classe [PamException] será colocada no pacote [exception] do projeto NetBeans:
![]() |
Seu código é o seguinte:
- linha 4: [PamException] deriva de [RuntimeException]. Trata-se, portanto, de um tipo de exceção que o compilador não nos obriga a tratar por meio de um `try/catch` nem a incluir na assinatura dos métodos. É por esse motivo que [PamException] não consta na assinatura dos métodos da interface [IDao]. Isso permite que essa interface seja implementada por uma classe que lance outro tipo de exceção, desde que essa classe também derive de [RuntimeException].
- Para diferenciar os erros que podem ocorrer, utiliza-se o código de erro da linha 7. Os três construtores das linhas 14, 19 e 24 são os da classe pai [RuntimeException], aos quais foi adicionado um parâmetro: o código de erro que se deseja atribuir à exceção.
O funcionamento do aplicativo, do ponto de vista das exceções, será o seguinte:
- a camada [DAO] encapsulará qualquer exceção encontrada em uma exceção do tipo [PamException] e a reenviará para a camada [métier].
- A camada [métier] permitirá que as exceções lançadas pela camada [DAO] sejam repassadas. Ela encapsulará todas as exceções que ocorrerem na camada [métier] em uma exceção do tipo [PamException] e a reenviará para a camada [ui].
- A camada [ui] intercepta todas as exceções que são repassadas pelas camadas [métier] e [DAO]. Ela se limitará a exibir a exceção no console ou na interface gráfica.
Vamos agora examinar, sucessivamente, a implementação das camadas [DAO] e [metier].
5.9. A camada [DAO] do aplicativo [PAM]
Estamos no contexto da seguinte arquitetura:
![]() |
5.9.1. Implementação
Leituras recomendadas: parágrafo 3.1.3 de [ref1]
Questão: Utilizando a integração Spring / JPA, escreva as classes [CotisationDao, IndemniteDao, EmployeDao] para implementar as interfaces [ICotisationDao, IIndemniteDao, IEmployeDao]. Cada método de classe interceptará uma eventual exceção e a encapsulará em uma exceção do tipo [PamException] com um código de erro específico para a exceção interceptada.
As classes de implementação farão parte do pacote [dao]:
![]() |
5.9.2. Configuração
Leituras recomendadas: parágrafo 3.1.5 de [ref1]
A integração DAO / JPA é configurada pelo arquivo Spring [spring-config-dao.xml] e pelos arquivos JPA e [persistence.xml]:
![]() |
Pergunta: escreva o conteúdo desses dois arquivos. Suponha-se que o banco de dados utilizado seja o MySQL5 [dbpam_hibernate], gerado pelo script SQL [dbpam_hibernate.sql]. O arquivo Spring definirá os três beans a seguir: employeDao do tipo EmployeDao, indemniteDao do tipo IndemniteDao, cotisationDao do tipo CotisationDao. Além disso, a implementação JPA utilizada será Hibernate.
5.9.3. Testes
Leituras recomendadas: parágrafos 3.1.6 e 3.1.7 de [ref1]
Agora que a camada [DAO] está escrita e configurada, podemos testá-la. A arquitetura dos testes será a seguinte:
![]() |
5.9.4. InitDB
Vamos criar dois programas de teste para a camada [DAO]. Esses programas serão colocados no pacote [dao] [2] do ramo [Test Packages] [1] do projeto NetBeans. Esse ramo não está incluído no projeto gerado pela opção [Build project], o que garante que os programas de teste que colocarmos nele não serão incluídos no arquivo .jar final do projeto.
![]() |
As classes colocadas no ramo [Test Packages] têm acesso às classes presentes no ramo [Source Packages], bem como às bibliotecas de classes do projeto. Se os testes precisarem de bibliotecas diferentes das do projeto, estas devem ser declaradas no ramo [Test Libraries] [2].
As classes de teste utilizam a ferramenta de testes unitários JUnit:
- [JUnitInitDB] não realiza nenhum teste. Ela preenche o banco de dados com alguns registros e, em seguida, os exibe no console.
- [JUnitDao] realiza uma série de testes e verifica os resultados.
A estrutura da classe [JUnitInitDB] é a seguinte:
- o método [init] é executado antes do início da série de testes (anotação @BeforeClass). Ele instancia a camada [DAO].
- O método [clean] é executado antes de cada teste (anotação @Before). Ele esvazia o banco de dados.
- O método [initDB] é um teste (anotação @Test). É o único. Um teste deve conter instruções de verificação Assert.assertCondition. Aqui, não haverá nenhuma. O método é, portanto, um falso teste. Sua função é preencher o banco de dados com algumas linhas e, em seguida, exibir o conteúdo do banco de dados no console. São utilizados aqui os métodos create e findAll das camadas [DAO].
Questão: complete o código da classe [JUnitInitDB]. Use como referência o exemplo do parágrafo 3.1.6 de [ref1]. O código irá gerar o conteúdo apresentado no parágrafo 5.1.
5.9.5. Implementaçã e dos testes
Agora estamos prontos para executar o [InitDB]. Descrevemos o procedimento com o SGBD e o MySQL5:
![]() |
- as classes [1], os arquivos de configuração [2] e as classes de teste da camada [DAO] e [3] estão configuradas,
![]() |
- o projeto é compilado [4]
- a classe [JUnitInitDB] é executada [5]. O SGBD MySQL5 é iniciado com uma base [dbpam_hibernate] existente,
- a janela [Test Results] e [6] indicam que os testes foram bem-sucedidos. Essa mensagem não é relevante neste caso, pois o programa [JUnitInitDB] não contém nenhuma instrução de asserção Assert.assertCondition que pudesse causar a falha do teste. No entanto, isso demonstra que não houve nenhuma exceção durante a execução do teste.
A janela [Output] contém os logs da execução, os do Spring e os do próprio teste. As exibições feitas pela classe [JUnitInitDB] são as seguintes:
As tabelas [EMPLOYES, INDEMNITES, COTISATIONS] foram preenchidas. Isso pode ser verificado por meio de uma conexão do NetBeans ao banco de dados [dbpam_hibernate].
![]() |
- em [1], na aba [services], visualizam-se os dados da tabela [employes] da conexão [dbpam_hibernate] [2],
- em [3], o resultado.
5.9.6. JUnitDao
Agora, vamos nos concentrar em uma segunda classe de testes, [JUnitDao]:
![]() |
A estrutura da classe será a seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
Na classe de testes anterior, o banco de dados é esvaziado antes de cada teste.
Pergunta: escreva os seguintes métodos:
1 - test02: inspire-se no test01
2 - test03: um funcionário possui um campo do tipo Indemnite. Portanto, é necessário criar uma entidade Indemnite e uma entidade Employe
3 - test04.
Seguindo o mesmo procedimento utilizado para a classe de testes [JUnitInitDB], obtêm-se os seguintes resultados:
![]() |
- em [1], executa-se a classe de testes
- em [2], os resultados dos testes na janela [Test Results]
Vamos provocar um erro para ver como ele é sinalizado na página de resultados:
Linha 13: a asserção causará um erro, já que o valor de Csgrds é 3,49 (linha 8). A execução da classe de testes apresenta os seguintes resultados:
![]() |
- a página de resultados [1] agora mostra que houve testes que falharam.
- Em [2], um resumo da exceção que fez com que o teste falhasse. Nele consta o número da linha do código Java onde a exceção ocorreu.
5.10. A camada [metier] do aplicativo [PAM]
Agora que a camada [DAO] foi escrita, passamos ao estudo da camada de negócios [2]:
![]() |
5.10.1. A interface Java [IMetier]
Esta foi descrita no parágrafo 5.7. Apresentamos um resumo abaixo:
A implementação da camada [metier] será feita em um pacote [metier]:
![]() |
O pacote [metier] incluirá, além da interface [IMetier] e sua implementação [Metier], duas outras classes: [FeuilleSalaire] e [ElementsSalaire]. A classe [FeuilleSalaire] foi brevemente apresentada no parágrafo 5.7. Voltaremos a ela agora.
5.10.2. A classe [FeuilleSalaire]
O método [calculerFeuilleSalaire] da interface [IMetier] retorna um objeto do tipo [FeuilleSalaire] que representa os diferentes elementos de uma folha de pagamento. Sua definição é a seguinte:
- linha 7: a classe implementa a interface Serializable porque suas instâncias podem ser trocadas pela rede.
- linha 9: o funcionário ao qual se refere a folha de pagamento
- linha 10: as diferentes alíquotas de contribuição
- linha 11: os diversos subsídios vinculados ao índice do funcionário
- linha 12: os componentes de seu salário
- linhas 14-22: os dois construtores da classe
- linhas 25-27: método [toString] que identifica um objeto [FeuilleSalaire] específico
- linhas 29 e seguintes: os acessores públicos aos campos privados da classe
A classe [ElementsSalaire], referenciada na linha 11 da classe [FeuilleSalaire] acima, reúne os elementos que constituem um holerite. Sua definição é a seguinte:
- linha 3: a classe implementa a interface Serializable porque é um componente da classe FeuilleSalaire, que deve ser serializável.
- linha 6: o salário-base
- linha 7: as contribuições sociais pagas sobre esse salário-base
- linha 8: os auxílios diários para manutenção do filho
- linha 9: os auxílios diários para alimentação da criança
- linha 10: o salário líquido a ser pago à babá
- linhas 12-24: os construtores da classe
- linhas 27-31: método [toString] que identifica um objeto específico [ElementsSalaire]
- linhas 34 e seguintes: os acessadores públicos aos campos privados da classe
5.10.3. A classe de implementação [Metier] da camada [metier]
A classe de implementação [Metier] da camada [metier] poderia ser a seguinte:
- linha 5: a anotação Spring @Transactional faz com que cada método da classe seja executado dentro de uma transação.
- linhas 9-10: as referências nas camadas [DAO] às entidades [Cotisation, Employe, Indemnite]
- linhas 14-17: o método [calculerFeuilleSalaire]
- linhas 20-22: o método [findAllEmployes]
- linha 24 e seguintes: os acessadores públicos dos campos privados da classe
Questão: escreva o código do método [findAllEmployes].
Questão: escreva o código do método [calculerFeuilleSalaire].
Observe os seguintes pontos:
- o modo de cálculo do salário foi explicado no parágrafo 5.2.
- se o parâmetro [SS] não corresponder a nenhum funcionário (a camada [DAO] retornou um ponteiro null), o método lançará uma exceção do tipo [PamException] com um código de erro apropriado.
5.10.4. Testes da camada [metier]
Criamos dois programas de teste:
![]() |
As classes de teste [3] são criadas no pacote [metier] [2] do ramo [Test Packages] [1] do projeto.
A classe [JUnitMetier_1] poderia ser a seguinte:
Não há nenhuma asserção Assert.assertCondition na classe. O objetivo é simplesmente calcular alguns salários para, em seguida, verificá-los manualmente. A exibição na tela obtida pela execução da classe anterior é a seguinte:
- linha 4: a folha de pagamento de Justine Laverti
- linha 5: a folha de pagamento de Marie Jouveinal
- linha 6: a exceção devido ao fato de que o funcionário com o nº SS 'xx' não existe.
Pergunta: a linha 17 de [JUnitMetier_1] utiliza o bean Spring denominado metier. Indique a definição desse bean no arquivo [spring-config-metier-dao.xml].
A classe [JUnitMetier_2] poderia ser a seguinte:
A classe [JUnitMetier_2] é uma cópia da classe [JUnitMetier_1], na qual, desta vez, foram inseridas asserções no método test01.
Tarefa: escreva o método test01.
Ao executar a classe [JUnitMetier_2], obtêm-se os seguintes resultados, se tudo correr bem:

5.11. A camada [ui] do aplicativo [PAM] – versão console
Agora que a camada [metier] foi gravada, resta-nos gravar a camada [ui] [1]:
![]() |
Criaremos duas implementações diferentes da camada [ui]: uma versão console e uma versão gráfica swing:
![]() |
5.11.1. A classe [ui.console.Main]
Primeiramente, vamos nos concentrar no aplicativo de console implementado pela classe [ui.console.Main] acima. Seu funcionamento foi descrito no parágrafo 5.3. A estrutura básica da classe [Main] poderia ser a seguinte:
Questão: complete o código acima.
5.11.2. Execução
Para executar a classe [ui.console.Main], proceda da seguinte maneira:
![]() |
- em [1], selecione as propriedades do projeto,
- em [2], selecione a propriedade [Run] do projeto,
- utilize o botão [3] para indicar a classe (conhecida como classe principal) a ser executada,
- selecione a classe [4],
- a classe aparece em [5]. Ela precisa de três argumentos para ser executada (n.º SS, número de horas trabalhadas, número de dias trabalhados). Esses argumentos são inseridos em [6],
- feito isso, é possível executar o projeto [7]. A configuração anterior faz com que seja a classe [ui.console.Main] que será executada.
Os resultados da execução são exibidos na janela [output]:
![]() | ![]() |
5.12. A camada [ui] do aplicativo [PAM] – versão gráfica
Agora implementamos a camada [ui] com uma interface gráfica:
![]() |
![]() |
- em [1], a classe [PamJFrame] da interface gráfica
- em [2]: a interface gráfica
5.12.1. Um tutorial rápido
Para criar a interface gráfica, pode-se proceder da seguinte maneira:
![]() |
- [1]: cria-se um novo arquivo com o botão [1] [New File...]
- [2]: seleciona-se a categoria do arquivo [Swing GUI Forms], c.a.d. formulários gráficos
- [3]: selecione o tipo [JFrame Form], um tipo de formulário vazio
![]() |
- [5]: atribui-se um nome ao formulário, que também será uma classe
- [6]: insere-se o formulário em um pacote
- [8]: o formulário é adicionado à árvore de estrutura do projeto
- [9]: o formulário pode ser acessado por meio de duas perspectivas: [Design] e [9], que permitem desenhar os diferentes componentes do formulário, e [Source] e [10 ci-dessous], que dão acesso ao código Java do formulário. No fim das contas, um formulário é uma classe Java como qualquer outra. A perspectiva [Design] facilita o desenho do formulário. A cada adição de um componente no modo [Design], código Java é adicionado na perspectiva [Source] para incorporá-lo.
![]() |
- [11]: a lista de componentes Swing disponíveis para um formulário pode ser encontrada na janela [Palette].
- [12]: a janela [Inspector] apresenta a árvore de componentes do formulário. Os componentes com representação visual estarão no ramo [JFrame], e os demais, no ramo [Other Components].
![]() |
- no [13], selecionamos um componente [JLabel] com um simples clique
- em [14], o colocamos no formulário no modo [Design]
- em [15], definimos as propriedades do JLabel (texto, fonte).
![]() |
- em [16], o resultado obtido.
- em [17], solicitamos a visualização prévia do formulário
- em [18], o resultado
- em [19], o rótulo [JLabel1] foi adicionado à árvore de componentes na janela [Inspector]
![]() |
- em [20] e [21]: na perspectiva [Source] do formulário, foi adicionado código Java para gerenciar o JLabel adicionado.
Um tutorial sobre a criação de formulários com o NetBeans está disponível no endereço [http://www.netbeans.org/kb/trails/matisse.html].
5.12.2. A interface gráfica [PamJFrame]
Vamos criar a seguinte interface gráfica:
![]() |
- em [1], a interface gráfica
- em [2], a árvore de seus componentes: um JLabel e seis contêineres JPanel
JLabel1
![]() |
JPanel1
![]() | ![]() |
JPanel2
![]() | ![]() |
JPanel3
![]() | ![]() |
JPanel4
![]() | ![]() |
JPanel5
![]() | ![]() |
Exercício prático: construir a interface gráfica anterior com a ajuda do tutorial [http://www.netbeans.org/kb/trails/matisse.html].
5.12.3. Os eventos da interface gráfica
Leituras recomendadas: capítulo [Interfaces graphiques] de [ref2].
Vamos tratar o clique no botão [jButtonSalaire]. Para criar o método de tratamento desse evento, podemos proceder da seguinte forma:
![]() |
O manipulador do clique no botão [JButtonSalaire] é gerado:
O código Java que associa o método anterior ao clique no botão [JButtonSalaire] também é gerado:
São as linhas 2 a 5 que indicam que o clique (eventualmente do tipo ActionPerformed) no botão [jButtonSalaire] (linha 2) deve ser gerenciado pelo método [jButtonSalaireActionPerformed] (linha 4).
Também trataremos do evento [caretUpdate] (deslocamento do cursor de entrada) no campo de entrada [jTextFieldHT]. Para criar o manipulador desse evento, procedemos da mesma forma que anteriormente:
![]() |
O manipulador do evento [caretUpdate] no campo de entrada [jTextFieldHT] é gerado:
O código Java que associa o método anterior ao evento [caretUpdate] no campo de entrada [jTextFieldHT] também é gerado:
As linhas 1 a 4 indicam que o evento [caretUpdate] (linha 2) no botão [jTextFieldHT] (linha 1) deve ser tratado pelo método [ jTextFieldHTCaretUpdate] (linha 3).
5.12.4. Inicialização da interface gráfica
Voltemos à arquitetura do nosso aplicativo:
![]() |
A camada [ui] precisa de uma referência à camada [metier]. Vamos relembrar como essa referência foi obtida na aplicação console:
O método é o mesmo na aplicação gráfica. É necessário que, quando esta for inicializada, a referência [IMetier metier] da linha 3 acima também seja inicializada. O código gerado para a interface gráfica é, por enquanto, o seguinte:
- linhas 29-35: o método estático [main] que inicia o aplicativo
- linha 32: uma instância da interface gráfica [PamJFrame] é criada e tornada visível.
- linhas 7-9: o construtor da interface gráfica.
- linha 8: chamada ao método [initComponents] definido na linha 17. Esse método é gerado automaticamente a partir do trabalho realizado no modo [Design]. Não se deve alterá-lo.
- linha 21: o método que irá gerenciar o deslocamento do cursor de entrada no campo [jTextFieldHT]
- linha 25: o método que irá gerenciar o clique no botão [jButtonSalaire]
Para adicionar nossas próprias inicializações ao código anterior, podemos proceder da seguinte forma:
- linha 4: chamamos um método proprietário para realizar nossas próprias inicializações. Estas são definidas pelo código das linhas 10 a 42
Pergunta: com a ajuda dos comentários, complete o código da procedimento [doMyInit].
5.12.5. Manipuladores de eventos
Questão: escreva o método [jTextFieldHTCaretUpdate]. Esse método deve garantir que, se o dado presente no campo [jTextFieldHT] não for um número real >=0, o botão [jButtonSalaire] fique desativado.
Questão: escreva o método [jButtonSalaireActionPerformed], que deve exibir a folha de pagamento do funcionário selecionado em [jComboBoxEmployes].
5.12.6. Execução da interface gráfica
Para executar a interface gráfica, modificaremos a configuração [Run] do projeto:
![]() |
- para [1], definindo a classe da interface gráfica
O projeto deve estar completo com seus arquivos de configuração (persistence.xml, spring-config-metier-dao.xml) e a classe da interface gráfica. Inicie o alvo SGBD antes de executar o projeto.
5.13. Implementação da camada JPA com EclipseLink
Estamos interessados na seguinte arquitetura, na qual a camada JPA agora é implementada por EclipseLink:
![]() |
5.13.1. O projeto NetBeans
O novo projeto do NetBeans é obtido por meio da cópia do projeto anterior:
![]() |
- em [1]: após clicar com o botão direito do mouse no projeto Hibernate, selecione Copy
- usando o botão [2], selecione a pasta pai do novo projeto. O nome da pasta aparece em [3].
- em [4], nomeie o novo projeto
- em [5], o nome da pasta do projeto
![]() |
- em [1], o novo projeto foi criado. Ele tem o mesmo nome que o original,
- em [2] e [3], renomeia-se como [mv-pam-spring-eclipselink].
O projeto deve ser alterado em dois pontos para se adaptar à nova camada JPA / EclipseLink:
- no [4], os arquivos de configuração do Spring devem ser alterados. Neles está, de fato, a configuração da camada JPA.
- no [5], as bibliotecas do projeto devem ser modificadas: as do Hibernate devem ser substituídas pelas do EclipseLink.
Vamos começar por este último ponto. O arquivo [pom.xml] para o novo projeto será este:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-pam-spring-eclipselink</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-pam-spring-eclipselink</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>swing-layout</id>
<layout>default</layout>
<name>Repository for library Library[swing-layout]</name>
</repository>
<repository>
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library Library[eclipselink]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
- linhas 73-82: as dependências para a implementação JPA EclipseLink,
- linhas 19-24: o repositório Maven para EclipseLink.
Os arquivos de configuração do Spring devem ser alterados para indicar que a implementação JPA foi alterada. Nos dois arquivos, apenas a seção que configura a camada JPA é alterada. Por exemplo, em [spring-config-metier-dao.xml], temos:
As linhas 19 a 36 configuram a camada JPA. A implementação JPA utilizada é o Hibernate (linha 22). Além disso, o banco de dados de destino é [dbpam_hibernate] (linha 41).
Para mudar para uma implementação JPA / EclipseLink, as linhas 19 a 35 acima são substituídas pelas linhas a seguir:
- linha 5: a implementação JPA utilizada é EclipseLink
- linha 9: a propriedade databasePlatform define o SGBD como destino, neste caso, MySQL
- linha 11: para gerar as tabelas do banco de dados quando a camada JPA é instanciada. Aqui, a propriedade está comentada.
- linha 7: para visualizar no console os comandos SQL emitidos pela camada JPA. Aqui, a propriedade está comentada.
Além disso, o banco de dados de destino passa a ser [dbpam_eclipselink] (linha 4 abaixo):
5.13.2. Execução dos testes
Antes de testar a aplicação inteira, é recomendável verificar se os testes JUnit são aprovados com a nova implementação JPA. Antes de executá-los, começaremos por excluir as tabelas do banco de dados. Para isso, na guia [Runtime] do NetBeans, se necessário, criaremos uma conexão com o banco de dados dbpam_eclipselink / MySQL5. Uma vez conectado ao banco de dados dbpam_eclipselink / MySQL5, será possível prosseguir com a exclusão das tabelas, conforme mostrado abaixo:
- [1]: antes da exclusão
- [2]: após a exclusão
![]() |
Feito isso, é possível executar o primeiro teste na camada [DAO]: InitDB, que preenche o banco de dados. Para que as tabelas excluídas anteriormente sejam recriadas pelo aplicativo, é preciso garantir que, na configuração JPA / EclipseLink do Spring, a linha:
exista e não esteja comentada.
Compilamos o projeto (Build) e, em seguida, executamos o teste [JUnitInitDB] :
![]() |
- em [1], o teste InitDB é executado.
- No [2], ele falha. A exceção é lançada pelo Spring e não por um teste que teria falhado.
Causado por: org.springframework.beans.factory.BeanCreationException: Erro ao criar o bean com o nome 'entityManagerFactory' definido no recurso do caminho de classe [spring-config-DAO.xml]: Falha na invocação do método init; a exceção aninhada é java.lang.IllegalStateException: É necessário iniciar o agente Java para usar InstrumentationLoadTimeWeaver. Consulte a documentação do Spring.
O Spring indica que há um problema de configuração. A mensagem não é clara. O motivo da exceção foi explicado no parágrafo 3.1.9 de [ref1]. Para que a configuração do Spring / EclipseLink funcione, o JVM que executa a aplicação deve ser iniciado com um parâmetro específico: um agente Java. O formato desse parâmetro é o seguinte:
[spring-agent.jar] é o agente Java necessário para que o JVM gerencie a configuração Spring / EclipseLink.
Ao executar um projeto, é possível passar argumentos para o JVM:
![]() |
- no [1], acessa-se as propriedades do projeto
- em [2], as propriedades do Run
- em [3], passa-se o parâmetro -javaagent para o JVM
5.13.3. InitDB
Agora, estamos prontos para testar novamente o [InitDB]. Desta vez, os resultados obtidos são os seguintes:
![]() |
- em [1], o teste foi bem-sucedido
- em [2], na aba [Services], atualizamos a conexão do NetBeans com o banco de dados [dbpam_eclipselink]
- em [3], foram criadas quatro tabelas
![]() |
- em [5], visualiza-se o conteúdo da tabela [employes]
- em [6], o resultado.
5.13.4. JUnitDao
A execução da classe de testes [JUnitDao] pode falhar, mesmo que, com a implementação JPA / Hibernate, ela tenha sido bem-sucedida. Para entender o motivo, vamos analisar um exemplo.
O método testado é o seguinte método IndemniteDao.create:
- linhas 15-22: o método testado
O método de teste é o seguinte:
package dao;
...
public class JUnitDao {
// camadas DAO
static private IEmployeDao employeDao;
static private IIndemniteDao indemniteDao;
static private ICotisationDao cotisationDao;
@BeforeClass
public static void init() {
// log
log("init");
// configuração do aplicativo
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-DAO.xml");
// camadas DAO
employeDao = (IEmployeDao) ctx.getBean("employeDao");
indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao");
cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao");
}
@Before()
public void clean() {
// esvaziar o banco de dados
for (Employe employe : employeDao.findAll()) {
employeDao.destroy(employe);
}
for (Cotisation cotisation : cotisationDao.findAll()) {
cotisationDao.destroy(cotisation);
}
for (Indemnite indemnite : indemniteDao.findAll()) {
indemniteDao.destroy(indemnite);
}
}
// logs
private static void log(String message) {
System.out.println("----------- " + message);
}
// testes
….
@Test
public void test05() {
log("test05");
// criam-se duas indenizações com o mesmo índice
// viola a restrição de exclusividade do índice
boolean erreur = true;
Indemnite indemnite1 = null;
Indemnite indemnite2 = null;
Throwable th = null;
try {
indemnite1 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12));
indemnite2 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12));
erreur = false;
} catch (PamException ex) {
th = ex;
// verificações
Assert.assertEquals(31, ex.getCode());
} catch (Throwable th1) {
th = th1;
}
// verificações
Assert.assertTrue(erreur);
// sequência de exceções
System.out.println("Chaîne des exceptions --------------------------------------");
System.out.println(th.getClass().getName());
while (th.getCause() != null) {
th = th.getCause();
System.out.println(th.getClass().getName());
}
// a primeira indenização precisou ser mantida
Indemnite indemnite = indemniteDao.find(indemnite1.getId());
// verificação
Assert.assertNotNull(indemnite);
Assert.assertEquals(1, indemnite.getIndice());
Assert.assertEquals(1.93, indemnite.getBaseHeure(), 1e-6);
Assert.assertEquals(2, indemnite.getEntretienJour(), 1e-6);
Assert.assertEquals(3, indemnite.getRepasJour(), 1e-6);
Assert.assertEquals(12, indemnite.getIndemnitesCP(), 1e-6);
// a segunda indenização não deveria ter sido mantida
List<Indemnite> indemnites = indemniteDao.findAll();
int nbIndemnites = indemnites.size();
Assert.assertEquals(nbIndemnites, 1);
}
...
}
Pergunta: explique o que o teste test05 faz e indique os resultados esperados.
Os resultados obtidos com uma camada JPA / Hibernate são os seguintes:
O teste é aprovado, c.a.d. As asserções são verificadas e não há nenhuma exceção lançada pelo método de teste.
Pergunta: explique o que aconteceu.
Os resultados obtidos com a camada JPA / EclipseLink são os seguintes:
Assim como anteriormente com o Hibernate, o teste é aprovado, c.a.d. As asserções foram verificadas e não houve nenhuma exceção lançada pelo método de teste.
Pergunta: explique o que aconteceu.
Pergunta: a partir desses dois exemplos, o que se pode concluir sobre a intercambiabilidade das implementações JPA? Ela é total neste caso?
5.13.5. Os outros testes
Uma vez que a camada [DAO] tenha sido testada e considerada correta, poderemos passar para os testes da camada [metier] e para os do próprio projeto em sua versão de console ou gráfica. A mudança na implementação JPA não afeta de forma alguma as camadas [metier] e [ui] e, portanto, se essas camadas funcionavam com o Hibernate, elas funcionarão com o EclipseLink, com algumas exceções: o exemplo anterior mostra, de fato, que as exceções lançadas pelas camadas [DAO] podem ser diferentes. Assim, no caso do teste, Spring / JPA / Hibernate lança uma exceção do tipo [PamException], uma exceção específica da aplicação [pam], enquanto Spring / JPA / EclipseLink, por sua vez, lança uma exceção do tipo [TransactionSystemException], uma exceção do framework Spring. Se, no caso de uso do teste, a camada [ui] espera uma exceção do tipo [PamException] porque foi construída com o Hibernate, ela deixará de funcionar quando passarmos para EclipseLink.
5.13.6. Tarefa a ser realizada
Trabalho prático: refazer os testes das aplicações console e swing com diferentes SGBD: MySQL5, Oracle XE, SQL Server.





















































































