8. Spring Data JPA OpenJpa
8.1. Introduzione
Stiamo riutilizzando l'architettura precedente, che ora stiamo implementando con un livello JPA/OpenJpa.
8.2. Configurazione dell'ambiente di sviluppo
Utilizzando STS, scaricare il progetto [myql-config-jpa-eclipselink] [1-4]:
quindi importare il progetto [mysl-config-jpa-openjpa] [5] che si trova nella cartella [<examples>/spring-database-config/mysql/eclipse] [6]:
Una volta fatto ciò, aggiorna l'ambiente Maven (Alt-F5) per tutti i progetti in [Package Explorer]:
Quindi, per verificare l'ambiente di lavoro, esegui la configurazione di build denominata [spring-jpa-generic-JUnitTestDao-openjpa]:
Questa configurazione esegue il test [JUnitTestDao]. Il test dovrebbe avere esito positivo:
8.3. Il progetto di configurazione del livello JPA
Lo scopo di questo progetto è configurare il livello JPA dell'architettura illustrata di seguito:
8.3.1. Configurazione Maven
Il progetto è un progetto Maven configurato dal seguente file [pom.xml]:
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>configuration mysql openjpa</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- dépendances variables ********************************************** -->
<!-- JPA provider -->
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>2.3.0</version>
</dependency>
<!-- dépendances constantes ********************************************** -->
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- configuration JDBC inherited -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- righe 5–7: l'artefatto Maven generato da questo progetto. È lo stesso del progetto [mysql-config-jpa-hibernate]. Ciò significa che solo uno di questi progetti può essere attivo in un dato momento;
- righe 10–14: il progetto Maven principale che imposta la versione della maggior parte delle dipendenze richieste dal progetto;
- righe 19–23: la libreria OpenJpa;
- righe 26–29: la libreria Spring Data;
- righe 32–34: il progetto di configurazione del livello JPA si basa sul progetto di configurazione del livello JDBC, che definisce, tra le altre cose, il driver JDBC per il DBMS in uso e i dettagli di connessione al database;
- righe 35–40: il progetto di configurazione del livello JDBC include la libreria [Spring JDBC], che qui viene sostituita dalla libreria [Spring Data JPA]. Pertanto, specifichiamo di non includerla nelle dipendenze del progetto. Tuttavia, se rimane, ciò non causa alcun errore;
Alla fine, le dipendenze sono le seguenti:
8.3.2. Configurazione di Spring
La classe [ConfigJpa] configura il progetto Spring come segue:
package generic.jpa.config;
import generic.jdbc.config.ConfigJdbc;
import javax.persistence.EntityManagerFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@Import({ConfigJdbc.class})
public class ConfigJpa {
// the provider JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter();
openJpaVendorAdapter.setShowSql(false);
openJpaVendorAdapter.setDatabase(Database.MYSQL);
openJpaVendorAdapter.setGenerateDdl(true);
return openJpaVendorAdapter;
}
// JPA entity packages
public final static String[] ENTITIES_PACKAGES = { "generic.jpa.entities.dbproduitscategories" };
// data source
@Bean
public DataSource dataSource() {
// data source TomcatJdbc
DataSource dataSource = new DataSource();
// configuration access JDBC
dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
dataSource.setUsername(ConfigJdbc.USER_DBPRODUITSCATEGORIES);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
// initially open connections
dataSource.setInitialSize(5);
// result
return dataSource;
}
// EntityManagerFactory
@Bean
public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan(ENTITIES_PACKAGES);
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
// Transaction manager
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
Questa configurazione è simile a quella descritta nella Sezione 6.3.2 per l'implementazione JPA di Hibernate. Ci limiteremo a illustrare le differenze:
- righe 23–30: il bean [jpaVendorAdapter] è ora implementato utilizzando OpenJpa;
8.4. Il file [persistence.xml]
 | | |
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
<persistence-unit name="generic-jpa-entities-dbproduitscategories" transaction-type="RESOURCE_LOCAL">
<!-- entities JPA -->
<class>generic.jpa.entities.dbproduitscategories.Categorie</class>
<class>generic.jpa.entities.dbproduitscategories.Produit</class>
<class>generic.jpa.entities.dbproduitscategories.User</class>
<class>generic.jpa.entities.dbproduitscategories.Role</class>
<class>generic.jpa.entities.dbproduitscategories.UserRole</class>
<exclude-unlisted-classes />
</persistence-unit>
<persistence-unit name="generic-jpa-entities-dbproduits" transaction-type="RESOURCE_LOCAL">
<!-- entities JPA -->
<class>generic.jpa.entities.dbproduits.Produit</class>
<exclude-unlisted-classes />
</persistence-unit>
</persistence>
- riga 4: l'unità di persistenza collegata al database [dbproduitscategories]. Può avere qualsiasi nome (attributo name);
- righe 6–10: le cinque entità JPA da gestire;
- righe 13–17: un'altra unità di persistenza collegata al database [dbproduitscategories]. OpenJpa consente di avere un file di persistenza contenente più unità di persistenza. EclipseLink no. Ricordiamo che con Hibernate non abbiamo utilizzato un file di persistenza;
8.5. Il livello di test
I test sopra riportati sono identici a quelli relativi alle implementazioni Spring JDBC, Spring JPA Hibernate e Spring JPA EclipseLink. Se necessario, consultare le pagine seguenti:
Le configurazioni di esecuzione da utilizzare sono le seguenti:
Affinché le entità JPA possano essere integrate da OpenJpa, le applicazioni Java o i test JUnit devono essere avviati con un agente Java. Questo agente integrerà le entità JPA prima che vengano caricate dalla JVM [http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html]. Esaminiamo, ad esempio, la configurazione del test [JUnitTestDao]:
- Nella scheda [Arguments] [1], forniamo il riferimento all'agente Java alla JVM utilizzando la sintassi [2]. L'agente Java è l'archivio [openjpa-<versione>.jar] che si trova nella cartella [<m2-repo>/org/apache/openjpa/<versione>]. La cartella <m2-repo> è specificata nella configurazione di Eclipse ([3] di seguito):
In [2] sopra, abbiamo utilizzato una variabile definita dall'utente [M2_REPO]:
Abbiamo assegnato il valore mostrato in [3] alla variabile [M2_REPO].
I risultati ottenuti per i test [JUnitTestDao] e [JUnitTestPushTheLimits] sono i seguenti:
- in [1], [JUnitTestPushTheLimits-EclipseLink]: 70,583 s;
- in [2], [JUnitTestPushTheLimits-Hibernate]: 78,945 s;
- in [3], [JUnitTestPushTheLimits-JDBC]: 36,09 s;
- in [4], [JUnitTestPushTheLimits-OpenJpa]: 80,394 s;
I risultati della console ottenuti per il test [JUnitTestProxies] sono i seguenti:
| Vidage de la base de données --------------------------------
doNothing
Vidage de la base de données --------------------------------
getShortCategoriesByName1 --------------------------------
Catégorie de type : PROXY
Catégorie :
Exception : java.lang.NullPointerException, Message : null
Vidage de la base de données --------------------------------
getLongCategoriesByName1 --------------------------------
Catégorie de type : POJO
Catégorie :
1
Vidage de la base de données --------------------------------
getShortProduitsByName1 --------------------------------
Produit de type : PROXY
Nom de la catégorie du produit :
Exception : java.lang.NullPointerException, Message : null
Vidage de la base de données --------------------------------
getLongProduitsByName1 --------------------------------
Produit de type : POJO
Nom de la catégorie du produit :
categorie[0]
|
Qui vediamo che quando si accede al campo [Categorie.produits] di una categoria di tipo PROXY e al campo [Produit.categorie] di un prodotto di tipo PROXY, in entrambi i casi abbiamo un puntatore nullo (righe 7 e 17).