View
13
Download
0
Category
Preview:
Citation preview
Java Persistence API:
Entity Manager Exercise
An Introduction to JPA and JPA/Maven Modules
Revision: v2018-10-04Built on: 2019-08-22 07:09 EST
Copyright © 2019 jim stafford (jim.stafford@jhu.edu)
This document contains an introductory exercise for building a Maven-based project for modules
that make use of a database and JPA. It will provide step-by-step instructions and explanations of
what is being added and why. Many of these steps will become boilerplate for what we need to do
for most JPA Maven modules. You go through this in detail here to provide you with exposure of
what is required and why certain things are in place in the examples. It is also another chance to
gain experience building a Maven project from scratch before we start leveraging templates and
parent projects to hide much of the details from us.
iii
Purpose ............................................................................................................................ v
1. Goals .................................................................................................................... v
2. Objectives ............................................................................................................. v
1. Setup and Start Database ............................................................................................ 1
1.1. Summary ............................................................................................................ 2
2. Create Core Project POM ............................................................................................ 3
2.1. Summary ............................................................................................................ 9
3. Setup Database Schema ............................................................................................ 11
3.1. Summary .......................................................................................................... 15
4. Add SQL Tuning ........................................................................................................ 17
4.1. Summary .......................................................................................................... 18
5. JPA Persistence Unit Setup ...................................................................................... 19
5.1. Summary .......................................................................................................... 23
6. Setup JPA TestCase .................................................................................................. 25
6.1. Summary .......................................................................................................... 30
7. Ready Project for Import into Eclipse ....................................................................... 33
7.1. Summary .......................................................................................................... 34
8. Test EntityManager Methods ..................................................................................... 35
8.1. Summary .......................................................................................................... 40
9. Automatically Generate Database Schema ................................................................ 41
9.1. Summary .......................................................................................................... 44
10. Create JPA Parent POM ........................................................................................... 45
10.1. Summary ........................................................................................................ 56
iv
v
Purpose
1. Goals• Provide one more chance to build a Maven module using step-by-step instructions
• Provide step-by-step instructions for setting up a Maven module for JPA
• Provide an introduction to JPA EntityManager CRUD methods
2. Objectives
At the completion of this topic, the student shall be able to:
• Maven/General project setup
• Setup a database for use with module development
• Create a Maven project for use with a JPA module
• Manage module-specific schema within the database
• Add custom SQL tuning properties to their module schema definition
• Import a Maven/JPA project into IDE
• Automatically generate database schema for project
• Refector single Maven/JPA module into a re-suable parent and individual child modules
• JPA
• Setup a JPA Persistence Unit
• Setup a JPA Unit test
• Implement tests of JPA EntityManager methods
vi
Chapter 1.
1
Setup and Start DatabaseThis chapter will cover setup required to start the development database in server-mode. The
database has at least three (3) modes.
• In-memory mode
• File-based mode
• Server-based mode
The in-memory option manages all tables within the memory footprint of the JVM. This is very fast
and requires no server-process setup -- which makes it a good option for automated unit tests.
However, since the DB instance is created and destroyed with each JVM execution it makes a bad
choice for modules relying on multiple tools to pre-populate the database prior to executing tests.
The file-based option stores all information in the filesystem. It is useful for multi-JVM (sequential)
setup and post-mortem analysis. However only a single client may access the files at one time.
I have seen this used effectively when simulating external databases -- where an external setup
script populates the database and the JVM running the unit tests just queries the information as
they would do in production. We will use this as an option to server-based mode since we are
using separate plugins to initialize the database. We also want to treat our database schema as a
first-class artifact for our application -- and not rely on test capabilities to instantiate the database
for each test.
The server-based option requires a separate process activated but allows concurrent connections
from database user interface while the JVM is under test. This chapter will focus on the setup
required to run the database in server mode.
1. Prepare your environment to run the database in server mode for this exercise by following the
instructions defined in Development Environment Setup.
2. Start the database and web server server in a directory where you wish to create database files.
Your h2.jar file source be located in M2_REPO/com/h2database/h2/*/h2*.jar to name at least
one location. Another location is JBOSS_HOME/modules/com/h2database/h2/main/h2*.jar
cd /tmp
java -jar h2.jar
This should result in a database server process and access to a web-based database UI
through the following local URL: http://localhost:8082/login.jsp
3. Connect to the database server from the web-based UI.
Driver Class: org.h2.Driver
JDBC URL: jdbc:h2:tcp://localhost:9092/./h2db/ejava</jdbc.url>
User Name: sa
Password:
Look in the directory where you started the server. After connecting with a relative URL using
the UI, there should be a new "h2db" directory with one or more files called "ejava*". You want
Chapter 1. Setup and Start Da...
2
to make sure you use the same URL in the UI and application so you are seeing the same
instance of the database.
If you use file-based mode, the connection information would look like the following where "./
h2db/ejava" must point to the exact same path your JVM under test uses. This can be a relative
or fully-qualified path.
Driver Class: org.h2.Driver
JDBC URL: jdbc:h2:./target/h2db/ejava
User Name: sa
Password:
1.1. Summary
In this chapter you...
• located a copy of the Java archive required to run the server
• located a scratch area to run the server
• started the server
• launched the web-based UI
• connected to the server using the web-based UI
This is the only database mode that requires administrative setup. You cannot connect to the
database running in-memory but you can connect to the database using file-mode once all other
JVMs have released their exclusive lock on the database storage files.
Chapter 2.
3
Create Core Project POMThis chapter will put in place a couple of core constructs that will allow Maven and the IDE
recognize the elements of your source tree.
1. Create a root directory for your project and populate with a pom.xml file
<?xml version="1.0"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>myorg.jpa</groupId>
<artifactId>entityMgrEx</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Entity Manager Exercise</name>
</project>
2. Define a remote repository to use to download hibernate artifacts
<!-- needed to resolve some hibernate dependencies -->
<repositories>
<repository>
<id>jboss-nexus</id>
<name>JBoss Nexus Repository</name>
<url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
</repository>
</repositories>
3. Add property definitions for versions of dependencies and plugins we will be using.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source.version>1.8</java.source.version>
<java.target.version>1.8</java.target.version>
<jboss.host>localhost</jboss.host>
<db.host>${jboss.host}</db.host>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<maven-jar-plugin.version>3.1.0</maven-jar-plugin.version>
<maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version>
<sql-maven-plugin.version>1.5</sql-maven-plugin.version>
<h2db.version>1.4.197</h2db.version>
<javax.persistence-api.version>2.2</javax.persistence-api.version>
<hibernate-entitymanager.version>5.3.1.Final</hibernate-entitymanager.version>
<junit.version>4.12</junit.version>
Chapter 2. Create Core Projec...
4
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.25</slf4j.version>
<ejava.version>5.0.0-SNAPSHOT</ejava.version>
</properties>
4. Add a dependencyManagement section to define and configure the dependencies we will be
using to work with JPA. These are passive definitions -- meaning they don't actually add any
dependencies to your project. They just define the version and possible exclusions for artifacts
so all child/leaf projects stay consistent.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>${javax.persistence-api.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate-entitymanager.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
Note
Knowing this exercise will always be a single module -- we could do this
simpler. However, it is assumed that you will soon take the information you
learn here to a real enterprise project and that will have many modules and
could benefit from reusing a standard parent configuration. All definitions will be
housed in a single module during the conduct of this exercise but the properties,
5
dependencyManagement, and pluginManagement sections we will build below
can be lifted and moved to a parent pom in a multi-module project.
5. Add pluginManagement definitions for certain plugins we will use in this module. Like above --
these are passive definitions that define the configuration for certain plugins when the child/leaf
projects chose to use them. Lets start with a simple example and add a few more complex ones
later. In this example, we are making sure all uses of the jar plugin are of a specific version.
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
6. Add the src/main dependencies. This represents what your code depends upon at compile time
and runtime.
• scope=compile is used when your src/main code depends on the artifact to compile and you
wish the design of transitive dependency to automatically bring this dependency with the
module.
• scope=provided is used when your src/main code depends on the artifact to compile but you
do not wish this automatically be brought along when used with downstream clients. Normally
this type of artifact is an API and the downstream client will be providing their own version
of the API packaged with their provider.
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<scope>provided</scope>
</dependency>
...
</dependencies>
Chapter 2. Create Core Projec...
6
Note
Notice how the definitions above are lacking a version element. The dependency
declaration actively brings the dependency into the project and inherits the
definition specified by the dependencyManagement section above.
7. Add the src/test standard dependencies.
• scope=test is used for anything that your src/test code depends upon (but not your src/main)
or what your unit tests need at runtime to operate the test. For example, a module may
declare a scope=test dependency on h2 database (later) to do some local unit testing and
then be ultimately deployed to a postgres server in a downstream client. In this case we are
picking JUnit4 as the testing framework, log4j as the logging implementation for commons-
logging, and hibernate as the JPA implementation for the jpa-api. We are also adding an
extra dependency to allow hibernate slf4j logging to be integrated with log4j.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
</dependency>
8. Add a testResources definition to the build section to get src/test/resource files filtered when
copied into the target tree. We do this so we have a chance to replace the variables in the
persistence.xml and hibernate.properties file.
<build>
<!-- filtering will replace URLs, credentials, etc in the
files copied to the target directory and used during testing.
-->
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
7
</testResources>
<pluginManagement>
...
</build>
9. Add a compiler specification to control the source and target java versions. In this case we are
picking up the specific value from property variables set above and can be overridden in the
developer's settings.xml or on the command line using system properties.
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.source.version}</source>
<target>${java.target.version}</target>
</configuration>
</plugin>
...
</plugins>
</pluginManagement>
</build>
10.Add a definition for the maven-surefire-plugin so we can set properties needed for testing.
<build>
<pluginManagement>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>${surefire.argLine}</argLine>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
Note
At this point, we are just allowing the argLine defined elsewhere to be optionally
specified (for debugging). We do not yet have a need for system properties, but
if we did the example shows how -Dname=value would be specified within the
Chapter 2. Create Core Projec...
8
plugin configuration. The plugin (not pluginManagement) definition in the child
pom will include any necessary system properties to be passed to the unit test.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<name1>value1</name1>
<name2>value2</name2>
</systemPropertyVariables>
</configuration>
</plugin>
11.Add a set of profiles that define H2 and Hibernate as our database and persistence provider. In
the example below we are adding two database definitions (that happen to both be the same
vendor). One requires the server to be running and the other uses an embedded server and
a local filesystem. The embedded version can be easier to test with. The server version can
be easier to debug. Activate which one you want with either your settings.xml#activeProfile
settings or using a -Pprofile-name argument on the command line. If you already have a
settings.xml#activeProfile setting, you can turn it off using -P\!deactivated-profile-name ((bash)
or -P!deactivated-profile-name (dos)) and follow it up with -Pactivated-profile-name.
<profiles>
<profile> <!-- H2 server-based DB -->
<id>h2srv</id>
<properties>
<jdbc.driver>org.h2.Driver</jdbc.driver>
<jdbc.url>jdbc:h2:tcp://${db.host}:9092/./h2db/ejava</jdbc.url>
<jdbc.user>sa</jdbc.user>
<jdbc.password/>
<hibernate.dialect>org.hibernate.dialect.H2Dialect</hibernate.dialect>
</properties>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile> <!-- H2 file-based DB -->
<id>h2db</id>
<activation>
<property>
<name>!jdbcdb</name>
</property>
</activation>
<properties>
<jdbc.driver>org.h2.Driver</jdbc.driver>
Summary
9
<jdbc.url>jdbc:h2:${basedir}/target/h2db/ejava</jdbc.url>
<jdbc.user>sa</jdbc.user>
<jdbc.password/>
<hibernate.dialect>org.hibernate.dialect.H2Dialect</hibernate.dialect>
</properties>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
Note
Profiles can be used to control which options are enabled at build time to make
the module more portable. I also use them to help identify which dependencies
are brought in for what reason -- especially for profiles that are configure to
always activate.
12.Perform a test of your pom.xml by issuing a sample build command. All should complete even
though there is nothing yet in your source tree.
$ mvn clean test
2.1. Summary
In this chapter you created a core Maven project definition with the ability to resolve dependencies,
build, and potentially test a JPA module. At this point you have an empty but valid Maven project/
module. In the following chapters we will add more artifacts to make to build something useful.
10
Chapter 3.
11
Setup Database SchemaThis chapter will take you through steps that will populate your database with a (simple) database
schema. A database schema is required by any module that directly interacts with a RDMBS. The
JPA provider can automatically generate a database schema but that is generally restricted to
early development and quick prototypes. A module within the data tier will ultimately be responsible
for providing a separate artifact the create and/or migrate the schema from version-to-version.
That is typically finalized by humans knowledgable about particular databases and can be aided
by tool(s) we introduce in this exercise.
1. Create a set of ddl scripts in src/main/resources/ddl to handle creating the schema, deleting
rows in the schema, and dropping tables in the schema. Make sure each script has the word
"create", "delete", or "drop" in its file name to match some search strings we'll use later. Have
the database generate a value for the primary key. That value should not be allowed to be null.
`-- src
|-- main
| |-- java
| `-- resources
| |-- ddl
| | |-- emauto_create.ddl
| | |-- emauto_delete.ddl
| | `-- emauto_drop.ddl
`-- test
|-- java
`-- resources
Note
We could actually skip this step and have the persistence provider create the
table for us. That approach is great for quick Java-first prototypes. However,
creating the schema outside of the persistence provider is a more realistic
scenario for larger developments.
# src/main/resources/ddl/emauto_create.ddl
CREATE TABLE EM_AUTO (
ID BIGINT generated by default as identity (start with 1) not null,
MAKE VARCHAR(32),
MODEL VARCHAR(32),
COLOR VARCHAR(32),
MILEAGE INT,
CONSTRAINT em_autoPK PRIMARY KEY(ID)
)
# src/main/resources/ddl/emauto_delete.ddl
DELETE FROM EM_AUTO;
# src/main/resources/ddl/emauto_drop.ddl
Chapter 3. Setup Database Schema
12
DROP TABLE EM_AUTO if EXISTS;
2. You can perform a sanity check of the above scripts by pasting them into the DB UI SQL area
and executing.
3. Add the standard database setup and teardown scripts. This allows us to create a legacy
database schema and write classes that map to that schema. We will later have the persistence
provider create the schema for us when we are in quick prototype mode. First create the
reusable portion of the definition in the pluginManagement section. This will define the version,
database dependencies, and property information for all to inherit.
<build>
<pluginManagement>
<plugins>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<version>${sql-maven-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
</dependency>
</dependencies>
<configuration>
<username>${jdbc.user}</username>
<password>${jdbc.password}</password>
<driver>${jdbc.driver}</driver>
<url>${jdbc.url}</url>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
4. Next add the potentially project-specific portion to a build-plugins-plugin section that would
normally be in the child module. However, when you add this to the module -- do so within a
profile that is wired to always run except when the system property -DskipTests is defined. This
is a standard maven system property that builders use to build the module and bypass both
unit and integration testing. By honoring the property here -- our module will only attempt to
work with the database if we ware not skipping tests. Note the !bang-not character means "the
absence of this system property".
<profiles>
...
<profile>
<id>testing</id>
13
<activation>
<property>
<name>!skipTests</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<!-- runs schema against the DB -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<executions>
<!-- place execution elements here -->
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
5. Configure the sql-maven-plugin executions element to run any drop scripts in the source tree
before running tests.
<execution>
<id>drop-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*drop*.ddl</include>
</includes>
</fileset>
<onError>continue</onError>
</configuration>
</execution>
Note
Note that we are controlling when the scripts are executed using the phase
element. This is naming a well known Maven lifecycle phase for the build.
6. Configure the sql-maven-plugin executions element to run any scripts from the source tree to
create schema before running tests.
Chapter 3. Setup Database Schema
14
<execution>
<id>create-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*create*.ddl</include>
</includes>
</fileset>
<print>true</print>
</configuration>
</execution>
7. Configure the sql-maven-plugin executions element to run any populate scripts from the source
tree to add rows to the database before running tests.
<execution>
<id>populate-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>test/resources/ddl/**/*populate*.ddl</include>
</includes>
</fileset>
</configuration>
</execution>
8. Configure the sql-maven-plugin executions element to run any drop scripts after testing. You
may want to comment this out if you want to view database changes in a GUI after the test.
<execution>
<id>drop-db-after-test</id>
<phase>test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
Summary
15
<include>main/resources/ddl/**/*drop*.ddl</include>
</includes>
</fileset>
</configuration>
</execution>
9. Build and run the tests. The schema should show up in the DB UI.
$mvn clean test -P\!h2db -Ph2srv
Note
Remember to turn off (-P!profile-name) the embedded profile (h2db) if active by
default and turn on the server profile (h2srv) if you wish to use the server and DB
UI while the unit test is running. The DB UI can only inspect the embedded file
once all active clients close the file. The backslash is only needed for commands
from the bash shell.
3.1. Summary
In this chapter you added a (trivial) schema definition for your module. This schema definition
was used to manage (create, delete, and drop) the schema within the database. Although we will
show that schema can be generated automatically by the JPA persistence provider and managed
at runtime -- this feature is only feasible for functional unit testing and quick prototypes. Any real
application will require a separate database schema artifact finalized by developers to be tuned
appropriately for the target database.
16
Chapter 4.
17
Add SQL TuningIn this chapter we are going to add tuning aspects to the schema put in place above. Examples of
this include any indexes we believe would enhance the query performance. This example is still
quite simple and lacks enough context to determine what would and would not be a helpful index.
Simply treat this exercise as a tutorial in putting an index in place when properly identified. Adding
the physical files mentioned here could be considered optional if all schema is hand-crafted. You
control the contents of each file in a 100% hand-crafted DDL solution. However, for those cases
where auto-generated schema files are created, you may want a separate set of files designated
for "tuning" the schema that was auto-generated. We will demonstrate using two extra files to
create/drop database indexes.
1. Add a file to add database indexes for your schema
# src/main/resources/ddl/emauto_tuningadd.ddl
CREATE INDEX EM_AUTO_MAKEMODEL ON EM_AUTO(MAKE, MODEL);
2. Wire this new file into your SQL plugin definition for creating schema. Have it run after your table
creates. Add an "orderFile" [https://www.mojohaus.org/sql-maven-plugin/execute-mojo.html]
element to the configuration to specify to the plugin that you wish the files be executed in a
specific order. Otherwise the order will be non-deterministic and the tuning may get executed
before the schema is created.
<execution>
<id>create-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<orderFile>ascending</orderFile>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*create*.ddl</include>
<include>main/resources/ddl/**/*tuningadd*.ddl</include>
</includes>
</fileset>
<print>true</print>
</configuration>
</execution>
3. Add a file to augment the drop script and remove indexes from your schema
# src/main/resources/ddl/emauto_tuningremove.ddl
DROP INDEX EM_AUTO_MAKEMODEL if exists;
Chapter 4. Add SQL Tuning
18
4. Wire this new file into your SQL plugin definition for dropping schema. Have it run before
your table drops. Add an "orderFile" [https://www.mojohaus.org/sql-maven-plugin/execute-
mojo.html] element to the configuration to specify to the plugin that you wish the files be
executed in a specific order.
<configuration>
<autocommit>true</autocommit>
<orderFile>decending</orderFile>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*tuningremove*.ddl</include>
<include>main/resources/ddl/**/*drop*.ddl</include>
</includes>
</fileset>
<onError>continue</onError>
</configuration>
5. Build the schema for your module
$ mvn clean process-test-classes
...
[INFO] --- sql-maven-plugin:1.4:execute (drop-db-before-test) @ entityMgrEx ---
[INFO] Executing file: .../src/main/resources/ddl/emauto_drop.ddl
[INFO] Executing file: .../src/main/resources/ddl/emauto_tuningremove.ddl
[INFO] 2 of 2 SQL statements executed successfully
[INFO]
[INFO] --- sql-maven-plugin:1.4:execute (create-db-before-test) @ entityMgrEx ---
[INFO] Executing file: .../src/main/resources/ddl/emauto_create.ddl
[INFO] Executing file: .../entityMgrEx/src/main/resources/ddl/emauto_tuningadd.ddl
[INFO] 2 of 2 SQL statements executed successfully
4.1. Summary
In this chapter you added additional schema files to the overall build process by assuming core
schema files may be generated by the persistence provider and additional tweaks may need to
be added within the development environment to complete testing.
Chapter 5.
19
JPA Persistence Unit SetupThis chapter will add an entity class, the persistence.xml, and associated property file to define
the persistence unit.
The entity class represents one or more tables in the database and each instance of the entity
class represents a specific row in those tables.
The persistence.xml defines a JPA persistence unit (along with other related XML files and entity
class annotations). Instances of a persistence unit is called a persistence context. Instances of
the persistence unit are accessed through an EntityManager.
`-- src
|-- main
| |-- java
| | `-- myorg
| | `-- entitymgrex
| | `-- Auto.java
| `-- resources
| `-- META-INF
| `-- persistence.xml
`-- test
|-- java
`-- resources
`-- hibernate.properties
1. Create a (Plain Old Java Object (POJO)) class to represent an automobile. Use class
annotations to provide the following:
# src/main/java/myorg/entitymgrex/Auto.java
package myorg.entitymgrex;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GenerationType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity @Table(name="EM_AUTO")
public class Auto implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String make;
private String model;
private String color;
private int mileage;
public Auto(){}
public Auto(int id) { this.id=id; }
Chapter 5. JPA Persistence Un...
20
public long getId() { return id; }
//more getter/setters go here
}
Note
@Entity, @Id, and a default constructor are the only requirements on an entity
class. The implementation of java.io.Serializable is not a JPA requirement but
will be leveraged by a later example within this exercise.
2. Add the remaining setter/getter methods to the class. If you are using Eclipse to author the
class -- right click->Source->Generate Getters and Setters will generate all of this for you. Since
we are using generated primary key values, there is no immediate need for a constructor to set
the id. If you add this later, remember to also add a default constructor, which was removed by
the compiler when you manually add the first constructor.
public void setMake(String make) {
this.make = make;
}
public int getMileage() { return mileage; }
public void setMileage(int mileage) {
this.mileage = mileage;
}
public String getModel() { return model; }
public void setModel(String model) {
this.model = model;
}
public String getColor() { return color; }
public void setColor(String color) {
this.color = color;
}
3. You may also want to add a public toString():String method to conveniently print your Auto
objects. Eclipse can also generate that on demand and configurable.
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder
.append("id=").append(id)
.append(", make=").append(make)
.append(", model=").append(model)
.append(", color=").append(color)
.append(", mileage=").append(mileage);
return builder.toString();
}
4. Create a META-INF/persistence.xml file to define the persistence unit for our jar file.
21
• persistence-unit name: must match what we place in our JUnit test
• provider: specify that this persistence unit is defined for the
org.hibernate.jpa.HibernatePersistenceProvider provider.
• define provider-specific properties that tell the provider how to obtain a connection to the
database as well as some other configuration properties.
Note
The technique to add the provider-specific properties includes somewhat
sensitive information like user credentials. If we place them in the
persistence.xml file within the src/main tree, these properties will become part
of our deployed artifact. To avoid this, we will define them in a separate
hibernate.properties file placed in the src/test tree.
# src/main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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" version="2.0">
<persistence-unit name="entityMgrEx">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- defined in src/test/resources/hibernate.properties -->
</properties>
</persistence-unit>
</persistence>
5. Create a hibernate.properties file in src/test/resources to hold information we want to support
testing, but may not want to be part of the deployed artifact. Leave the volatile values as
variables so they can be expanded into the target tree during compile time.
• the variables will be filled in during the build process using "filtering" and the resources plugin.
• the show and format_sql options are only turned on during early development and debug.
• the jdbc.batch_size property set to 0 is also used during debug. Setting it to this value will
eliminate any batching of SQL commands, allowing errors about the commands to be better
reported to the developer.
# src/test/resources/hibernate.properties
hibernate.connection.url=${jdbc.url}
hibernate.connection.driver_class=${jdbc.driver}
hibernate.connection.username=${jdbc.user}
hibernate.connection.password=${jdbc.password}
hibernate.dialect=${hibernate.dialect}
#hibernate.hbm2ddl.auto=create
hibernate.hbm2ddl.import_files=/ddl/emauto-tuningdrop.ddl,/ddl/emauto-tuning.ddl
hibernate.show_sql=true
hibernate.format_sql=true
Chapter 5. JPA Persistence Un...
22
#hibernate.jdbc.batch_size=0
6. Make sure your project still builds. Your area should look something like the following after the
build.
$ mvn clean install -P\!h2db -Ph2srv
|-- pom.xml
|-- src
| |-- main
| | |-- java
| | | `-- myorg
| | | `-- entitymgrex
| | | `-- Auto.java
| | `-- resources
| | |-- ddl
| | | |-- emauto_create.ddl
| | | |-- emauto_delete.ddl
| | | |-- emauto_drop.ddl
| | | |-- emauto_tuningadd.ddl
| | | `-- emauto_tuningremove.ddl
| | `-- META-INF
| | `-- persistence.xml
| `-- test
| `-- resources
| `-- hibernate.properties
`-- target
|-- classes
| |-- ddl
| | |-- emauto_create.ddl
| | |-- emauto_delete.ddl
| | |-- emauto_drop.ddl
| | |-- emauto_tuningadd.ddl
| | `-- emauto_tuningremove.ddl
| |-- META-INF
| | `-- persistence.xml
| `-- myorg
| `-- entitymgrex
| `-- Auto.class
...
`-- test-classes
`-- hibernate.properties
7. You should also check that your hibernate.properties file was filtered by your
build.testResources definition provided earlier. The variable definitions within your src/test/
resources source file(s) should be replaced with property values from your environment.
$ more src/test/resources/hibernate.properties target/test-classes/hibernate.properties
::::::::::::::
src/test/resources/hibernate.properties
::::::::::::::
hibernate.dialect=${hibernate.dialect}
Summary
23
hibernate.connection.url=${jdbc.url}
hibernate.connection.driver_class=${jdbc.driver}
hibernate.connection.password=${jdbc.password}
hibernate.connection.username=${jdbc.user}
#hibernate.hbm2ddl.auto=create
hibernate.show_sql=true
hibernate.format_sql=true
#hibernate.jdbc.batch_size=0
::::::::::::::
target/test-classes/hibernate.properties
::::::::::::::
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.connection.url=jdbc:h2:tcp://127.0.0.1:9092/./h2db/ejava
hibernate.connection.driver_class=org.h2.Driver
hibernate.connection.password=
hibernate.connection.username=sa
#hibernate.hbm2ddl.auto=create
#hibernate.hbm2ddl.import_files=/ddl/emauto-tuningdrop.ddl,/ddl/emauto-tuning.ddl
hibernate.show_sql=true
hibernate.format_sql=true
#hibernate.jdbc.batch_size=0
5.1. Summary
In this chapter you defined a JPA entity class, persistence unit, and properties specific to your
database instance. With these tackled, you have the core building blocks in place to begin
instantiating the JPA EntityManager in the next chapter. You will notice that the specification
of your database properties came from your Maven pom properties instead of hard-coded into
individual files. This leveraged the filtering feature of resource files during the build. In the last
chapter of this exercise you will be asked to re-factor the Maven database properties into a parent
Maven project to be re-used across several projects.
24
Chapter 6.
25
Setup JPA TestCaseThis chapter will create a JUnit test case that will instantiate an EntityManager, perform some
basic operations, and log information about the tests.
`-- src
`-- test
|-- java
| `-- myorg
| `-- entitymgrex
| `-- EntityMgrTest.java
`-- resources
`-- log4j.xml
1. Create a JUnit test case to hold your test code. The following is an example of a 4.x JUnit test
case that uses @Annotations.
# src/test/java/myorg/entitymgrex/EntityMgrTest.java
package myorg.entitymgrex;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class EntityMgrTest {
private static final Logger log = LoggerFactory.getLogger(EntityMgrTest.class);
@Test
public void testTemplate() {
logger.info("testTemplate");
}
}
2. Provide a setUpClass() method that runs once before all tests that can create the entity
manager. This method must be static.
Chapter 6. Setup JPA TestCase
26
private static final String PERSISTENCE_UNIT = "entityMgrEx";
private static EntityManagerFactory emf;
@BeforeClass
public static void setUpClass() {
log.debug("creating entity manager factory");
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
}
3. Provide a setUp() method that will be called before each testMethod is executed. Have this
method create an entity manager for the tests to use.
private EntityManager em;
@Before
public void setUp() throws Exception {
log.debug("creating entity manager");
em = emf.createEntityManager();
//cleanup();
}
4. Provide a tearDown() method that will be called after each testMethod. Have this flush all
remaining items in the persistence context to the database and close the entity manager.
@After
public void tearDown() throws Exception {
if (em!=null) {
logger.debug("tearDown() started, em={}", em);
em.getTransaction().begin();
em.flush();
//logAutos();
em.getTransaction().commit();
em.close();
logger.debug("tearDown() complete, em={}", em);
em=null;
}
}
5. Provide a tearDownClass() method that will be called after all testMethods have completed.
This method must be static and should close the entity manager factory.
@AfterClass
public static void tearDownClass() {
if (emf!=null) {
logger.debug("closing entity manager factory");
emf.close();
emf=null;
}
}
27
6. Add in a logAutos() method to query and print all autos in the database. Do this after flushing
the entity manager in the tearDown() method so you can see the changes from the previous
test. The following example uses the entity manager to create an ad-hoc EJB-QL statement.
@After
public void tearDown() throws Exception {
...
em.flush();
logAutos();
em.getTransaction().commit();
...
}
public void logAutos() {
Query query = em.createQuery("select a from Auto as a");
for (Object o: query.getResultList()) {
logger.info("EM_AUTO: {}", o);
}
}
7. You might also want to add a cleanup() to clear out the Auto table between tests. The example
below uses the entity manager to create a native SQL statement.
@Before
public void setUp() throws Exception {
...
em = emf.createEntityManager();
cleanup();
}
public void cleanup() {
em.getTransaction().begin();
Query query = em.createNativeQuery("delete from EM_AUTO");
int rows = query.executeUpdate();
em.getTransaction().commit();
logger.info("removed {} rows", rows);
}
8. Add a log4j.xml file to src/test/resources that has your desired settings. The one below produces
less timestamp information at the console and more details in the logfile.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration
xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="(%F:%M:%L) -%m%n"/>
Chapter 6. Setup JPA TestCase
28
</layout>
</appender>
<appender name="logfile" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="target/log4j-out.txt"/>
<param name="Append" value="false"/>
<param name="MaxFileSize" value="100KB"/>
<param name="MaxBackupIndex" value="1"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%-5p %d{dd-MM HH:mm:ss,SSS} [%c] (%F:%M:%L) -%m%n"/>
</layout>
</appender>
<logger name="myorg">
<level value="debug"/>
<appender-ref ref="logfile"/>
</logger>
<root>
<priority value="fatal"/>
<appender-ref ref="CONSOLE"/>
</root>
</log4j:configuration>
Note
Although it might be a bit entertaining to set the priority of the root appender to
debug to see everything the persistence provider has to say, it is quite noisy.
Consider changing to root priority to fatal so that a majority of the log statements
are yours.
9. You should be able to build and test your module at this time.
$ mvn clean test
Running myorg.entitymgrex.EntityMgrTest
(EntityMgrTest.java:setUpClass:25) -creating entity manager factory
(EntityMgrTest.java:setUp:31) -creating entity manager
Hibernate:
delete
from
EM_AUTO
(EntityMgrTest.java:cleanup:58) -removed 0 rows
(EntityMgrTest.java:testTemplate:75) -testTemplate
(EntityMgrTest.java:tearDown:39) -tearDown() started, em=org.hibernate.ejb.EntityManagerImpl@3e52a475
Hibernate:
select
auto0_.id as id0_,
auto0_.color as color0_,
auto0_.make as make0_,
auto0_.mileage as mileage0_,
29
auto0_.model as model0_
from
EM_AUTO auto0_
(EntityMgrTest.java:tearDown:45) -tearDown() complete, em=org.hibernate.ejb.EntityManagerImpl@3e52a475
(EntityMgrTest.java:tearDownClass:69) -closing entity manager factory
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.337 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] BUILD SUCCESS
Raise org.hibernate verbosity to help debug errors
If you tests failed and there is no key information printed, try raising the verbosity
for all org.hibernate classes to DEBUG.
# src/test/resources/log4j.xml
<logger name="org.hibernate">
<level value="trace"/>
<appender-ref ref="logfile"/>
</logger>
10.Check that you have the following artifacts in your project tree.
|-- pom.xml
|-- src
| |-- main
| | |-- java
| | | `-- myorg
| | | `-- entitymgrex
| | | `-- Auto.java
| | `-- resources
| | |-- ddl
| | | |-- emauto_create.ddl
| | | |-- emauto_delete.ddl
| | | |-- emauto_drop.ddl
| | | |-- emauto_tuningadd.ddl
| | | `-- emauto_tuningremove.ddl
| | `-- META-INF
| | `-- persistence.xml
| `-- test
| |-- java
| | `-- myorg
| | `-- entitymgrex
| | `-- EntityMgrTest.java
| `-- resources
| |-- hibernate.properties
| `-- log4j.xml
`-- target
|-- antrun
| `-- build-main.xml
Chapter 6. Setup JPA TestCase
30
|-- classes
| |-- ddl
| | |-- emauto_create.ddl
| | |-- emauto_delete.ddl
| | |-- emauto_drop.ddl
| | |-- emauto_tuningadd.ddl
| | `-- emauto_tuningremove.ddl
| |-- META-INF
| | `-- persistence.xml
| `-- myorg
| `-- entitymgrex
| `-- Auto.class
|-- generated-sources
| `-- annotations
|-- generated-test-sources
| `-- test-annotations
|-- h2db
| `-- ejava.h2.db
|-- log4j-out.txt
|-- maven-status
| `-- maven-compiler-plugin
| |-- compile
| | `-- default-compile
| | |-- createdFiles.lst
| | `-- inputFiles.lst
| `-- testCompile
| `-- default-testCompile
| |-- createdFiles.lst
| `-- inputFiles.lst
|-- surefire-reports
| |-- myorg.entitymgrex.EntityMgrTest.txt
| `-- TEST-myorg.entitymgrex.EntityMgrTest.xml
`-- test-classes
|-- hibernate.properties
|-- log4j.xml
`-- myorg
`-- entitymgrex
`-- EntityMgrTest.class
6.1. Summary
In this chapter you created an EntityManager, managed the transaction, and closed the
EntityManager within the JUnit framework set of callbacks. JUnit will create a new instance of
the class and execute @Before, @Test, @After in that order for each @Test. @BeforeClass is
run before the first @Before/@Test and @AfterClass is run after the last @Test/@After and must
be static.
There are numerous ways to setup the transaction properties. Some make it a practice to rollback
the transaction as a part of each @After (after an em.flush() of course). There is nothing wrong with
managing the transaction within the @Test for certain tests but make it a point *NOT* to attempt to
manage the transaction within code placed in the src/main tree without some careful consideration.
JavaEE is a framework and transaction management is a key part of that framework. You would be
Summary
31
outside the framework if you attempt to directly manage a tranaction in your src/main production
code. Much more on that later.
32
Chapter 7.
33
Ready Project for Import into EclipseOver the years/versions, Eclipse has progressed from being ignorant of Maven (with all integration
coming from the Maven side) to being very much integrated with Maven. In that later/integrated
mode, Eclipse will try really hard to do the right thing within Eclipse for what was defined to be done
outside of Eclipse. For example, Eclipse will turn Maven dependencies directly into an Eclipse
build path. There exists, however, some plugins that Eclipse has yet to learn about and will alert
you to that fact. Many of these have no role within Eclipse and you simply need to explicitly give
Eclipse instruction to ignore the plugin. Luckily these cases get fewer and fewer each year and
Eclipse will update your pom.xml with the necessary configuration when it is needed.
• Import the project into Eclipse using "Existing Maven Projects" option. You should see an error
for the maven-sql-plugin. Ignore the error and continue with the import. We will address the
error in the next step.
• Add the following profile to your pom.xml. The profile is activated when the m2e.version property
is defined -- which is a property m2e (Maven To Eclipse) sets within Eclipse.
<profiles>
...
<!-- tell Eclipse what to do with some of the plugins -->
<profile>
<id>m2e</id>
<activation>
<property>
<name>m2e.version</name>
</property>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<versionRange>[1.0.0,)</versionRange>
<goals>
<goal>execute</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
Chapter 7. Ready Project for ...
34
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
...
</profiles>
The red error marks should have cleared from the pom.xml editor. The above provide is
activated when the "m2e.version" is present within Eclipse and tells Eclipse to ignore the sql-
maven-plugin.
7.1. Summary
In this chapter you imported your JPA project into Eclipse and took care of any issues getting
Eclipse to understand what to do with plugins that it did not understand or need to understand.
Chapter 8.
35
Test EntityManager MethodsThis chapter will demonstrate various methods to perform create, read, update, and delete
(CRUD) operations on the database using the EntityManager, the persistence unit/context, and
the entity class.
Note
The following changes are all made to the EntityMgrTest.java JUnit test class.
Everything is being done within this file to keep things simple. This test case is
playing the role of the business and persistence (Data Access Object (DAO)) logic.
1. add a testCreate() method to test the functionality of EntityManager.create(). This will add an
object to the database once associated with a transaction.
@Test
public void testCreate() {
logger.info("testCreate");
Auto car = new Auto();
car.setMake("Chrysler");
car.setModel("Gold Duster");
car.setColor("Gold");
car.setMileage(60*1000);
logger.info("creating auto: {}", car);
em.persist(car);
}
-testCreate
-creating auto:myorg.entitymgrex.Auto@140984b, id=0, make=Chrysler, model=Gold Duster, color=Gold,
mileage=60000
-tearDown() started, em=org.hibernate.ejb.EntityManagerImpl@3ac93e
-EM_AUTO:myorg.entitymgrex.Auto@140984b, id=1, make=Chrysler, model=Gold Duster, color=Gold,
mileage=60000
-removed 1 rows
2. add a testMultiCreate() to test creating several objects. This should also help verify that unique
primary keys are being generated.
@Test
public void testMultiCreate() {
logger.info("testMultiCreate");
for(int i=0; i<5; i++) {
Auto car = new Auto();
car.setMake("Plymouth " + i);
car.setModel("Grand Prix");
car.setColor("Green");
car.setMileage(80*1000);
logger.info("creating auto: {}", car);
Chapter 8. Test EntityManager...
36
em.persist(car);
}
}
-testMultiCreate
-creating auto:myorg.entitymgrex.Auto@c3e9e9, id=0, make=Plymouth 0, model=Grand Prix, color=Green,
mileage=80000
-creating auto:myorg.entitymgrex.Auto@31f2a7, id=0, make=Plymouth 1, model=Grand Prix, color=Green,
mileage=80000
-creating auto:myorg.entitymgrex.Auto@131c89c, id=0, make=Plymouth 2, model=Grand Prix, color=Green,
mileage=80000
-creating auto:myorg.entitymgrex.Auto@1697b67, id=0, make=Plymouth 3, model=Grand Prix, color=Green,
mileage=80000
-creating auto:myorg.entitymgrex.Auto@24c4a3, id=0, make=Plymouth 4, model=Grand Prix, color=Green,
mileage=80000
-tearDown() started, em=org.hibernate.ejb.EntityManagerImpl@1e9c82e
-EM_AUTO:myorg.entitymgrex.Auto@c3e9e9, id=2, make=Plymouth 0, model=Grand Prix, color=Green,
mileage=80000
-EM_AUTO:myorg.entitymgrex.Auto@31f2a7, id=3, make=Plymouth 1, model=Grand Prix, color=Green,
mileage=80000
-EM_AUTO:myorg.entitymgrex.Auto@131c89c, id=4, make=Plymouth 2, model=Grand Prix, color=Green,
mileage=80000
-EM_AUTO:myorg.entitymgrex.Auto@1697b67, id=5, make=Plymouth 3, model=Grand Prix, color=Green,
mileage=80000
-EM_AUTO:myorg.entitymgrex.Auto@24c4a3, id=6, make=Plymouth 4, model=Grand Prix, color=Green,
mileage=80000
3. add a testFind() to test the ability to find an object by its primary key value.
@Test
public void testFind() {
logger.info("testFind");
Auto car = new Auto();
car.setMake("Ford");
car.setModel("Bronco II");
car.setColor("Red");
car.setMileage(0*1000);
logger.info("creating auto: {}", car);
em.persist(car);
//we need to associate the em with a transaction to get a
//primary key generated and assigned to the auto
em.getTransaction().begin();
em.getTransaction().commit();
Auto car2 = em.find(Auto.class, car.getId());
assertNotNull("car not found:" + car.getId(), car2);
logger.info("found car: {}", car2);
}
-testFind
-creating auto:myorg.entitymgrex.Auto@aae86e, id=0, make=Ford, model=Bronco II, color=Red, mileage=0
-found car:myorg.entitymgrex.Auto@aae86e, id=7, make=Ford, model=Bronco II, color=Red, mileage=0
37
-tearDown() started, em=org.hibernate.ejb.EntityManagerImpl@97d026
-EM_AUTO:myorg.entitymgrex.Auto@aae86e, id=7, make=Ford, model=Bronco II, color=Red, mileage=0
4. add a getReference() to test the ability to get a reference to an object. With such a shallow
object, this will act much like find().
@Test
public void testGetReference() {
logger.info("testGetReference");
Auto car = new Auto();
car.setMake("Ford");
car.setModel("Escort");
car.setColor("Red");
car.setMileage(0*1000);
logger.info("creating auto: {}", car);
em.persist(car);
//we need to associate the em with a transaction to get a
//primary key generated and assigned to the auto
em.getTransaction().begin();
em.getTransaction().commit();
Auto car2 = em.getReference(Auto.class, car.getId());
assertNotNull("car not found:" + car.getId(), car2);
logger.info("found car: {}", car2);
}
-testGetReference
-creating auto:myorg.entitymgrex.Auto@608760, id=0, make=Ford, model=Escort, color=Red, mileage=0
-found car:myorg.entitymgrex.Auto@608760, id=8, make=Ford, model=Escort, color=Red, mileage=0
-tearDown() started, em=org.hibernate.ejb.EntityManagerImpl@157ea4a
-EM_AUTO:myorg.entitymgrex.Auto@608760, id=8, make=Ford, model=Escort, color=Red, mileage=0
5. add a testUpdate() method to test the ability to have the setter() of a managed ubject update
the database.
@Test
public void testUpdate() {
logger.info("testUpdate");
Auto car = new Auto();
car.setMake("Pontiac");
car.setModel("Gran Am");
car.setColor("Red");
car.setMileage(0*1000);
logger.info("creating auto: {}", car);
em.persist(car);
//we need to associate the em with a transaction to get a
//primary key generated and assigned to the auto
em.getTransaction().begin();
em.getTransaction().commit();
Chapter 8. Test EntityManager...
38
for(int mileage=car.getMileage(); mileage<(100*1000); mileage+=20000) {
//here's where the update is done
car.setMileage(mileage);
//commit the update to the database for query
em.getTransaction().begin();
em.getTransaction().commit();
//inspect database for value
int value = getMileage(car.getId());
assertTrue("unexpected mileage:" + value, value == mileage);
logger.info("found mileage: {}", value);
}
}
private int getMileage(long id) {
Query query =
em.createQuery("select a.mileage from Auto as a where a.id=:pk");
query.setParameter("pk", id);
return (Integer)query.getSingleResult();
}
-testUpdate
-creating auto:myorg.entitymgrex.Auto@6a3960, id=0, make=Pontiac, model=Gran Am, color=Red, mileage=0
-found mileage:0
-found mileage:20000
-found mileage:40000
-found mileage:60000
-found mileage:80000
-EM_AUTO:myorg.entitymgrex.Auto@6a3960, id=9, make=Pontiac, model=Gran Am, color=Red, mileage=80000
6. add a testMerge() method to test the ability to perform updates based on the current values of
a detached object. Note that we are using Java serialization to simulate sending a copy of the
object to/from a remote process and then performing the merge based on the updated object.
@Test
public void testMerge() throws Exception {
logger.info("testMerge");
Auto car = new Auto();
car.setMake("Chrystler");
car.setModel("Concord");
car.setColor("Red");
car.setMileage(0*1000);
logger.info("creating auto: {}", car);
car = em.merge(car); //using merge to persist new
//we need to associate the em with a transaction to get a
//primary key generated and assigned to the auto
em.getTransaction().begin();
em.getTransaction().commit();
for(int mileage=(10*1000); mileage<(100*1000); mileage+=20000) {
39
//simulate sending to remote system for update
Auto car2 = updateMileage(car, mileage);
//verify the object is not being managed by the EM
assertFalse("object was managed", em.contains(car2));
assertTrue("object wasn't managed", em.contains(car));
assertTrue("mileage was same",
car.getMileage() != car2.getMileage());
//commit the update to the database for query
em.merge(car2);
assertTrue("car1 not merged:" + car.getMileage(),
car.getMileage() == mileage);
em.getTransaction().begin();
em.getTransaction().commit();
//inspect database for value
int value = getMileage(car.getId());
assertTrue("unexpected mileage:" + value, value == mileage);
logger.info("found mileage:" + value);
}
}
private Auto updateMileage(Auto car, int mileage) throws Exception {
//simulate sending the object to a remote system
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(car);
oos.close();
//simulate receiving an update to the object from remote system
ByteArrayInputStream bis =
new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Auto car2 = (Auto)ois.readObject();
ois.close();
//here's what they would have changed in remote process
car2.setMileage(mileage);
return car2;
}
-testMerge
-creating auto:myorg.entitymgrex.Auto@147358f, id=0, make=Chrystler, model=Concord, color=Red, mileage=0
-found mileage:10000
-found mileage:30000
-found mileage:50000
-found mileage:70000
-found mileage:90000
-tearDown() started, em=org.hibernate.ejb.EntityManagerImpl@1b4c1d7
-EM_AUTO:myorg.entitymgrex.Auto@147358f, id=10, make=Chrystler, model=Concord, color=Red, mileage=90000
7. add a testRemove() method to verify that we can delete objects from the database.
Chapter 8. Test EntityManager...
40
@Test
public void testRemove() {
logger.info("testRemove");
Auto car = new Auto();
car.setMake("Jeep");
car.setModel("Cherokee");
car.setColor("Green");
car.setMileage(30*1000);
logger.info("creating auto: {}", car);
em.persist(car);
//we need to associate the em with a transaction to get a
//primary key generated and assigned to the auto
em.getTransaction().begin();
em.getTransaction().commit();
Auto car2 = em.find(Auto.class, car.getId());
assertNotNull("car not found:" + car.getId(), car2);
logger.info("found car: {}", car2);
//now remove the car
logger.info("removing car: {}", car);
em.remove(car);
//we need to associate the em with a transaction to
//physically remove from database
em.getTransaction().begin();
em.getTransaction().commit();
Auto car3 = em.find(Auto.class, car.getId());
assertNull("car found", car3);
}
-testRemove
-creating auto:myorg.entitymgrex.Auto@28305d, id=0, make=Jeep, model=Cherokee, color=Green, mileage=30000
-found car:myorg.entitymgrex.Auto@28305d, id=11, make=Jeep, model=Cherokee, color=Green, mileage=30000
-removing car:myorg.entitymgrex.Auto@28305d, id=11, make=Jeep, model=Cherokee, color=Green, mileage=30000
8.1. Summary
In this chapter you worked with several of the EntityManager CRUD methods witin the context of
a JUnit test case and Maven project. This should have given you a decent taste of what can be
done with the JPA EntityManager and how to bring this all together within a Maven module. In the
next chapter we will tack on a few more useful features to know.
Chapter 9.
41
Automatically Generate Database
SchemaIn a previous chapter, you manually created a set of DDL files to create schema, delete rows from
the schema in the database, and drop the schema from the database. Since your persistence
provider knows how to work with schema, you can optionally get it to create schema for you rather
than generating it manually. Even if you are working with legacy schema (and won't be changing
the database), it is extremely helpful to see the persistence providers version of the schema to
be able to more quickly determine a mis-match in the mapping rather than waiting until runtime
testing. In order to add schema generation to your projects you can add one of the following;
runtime schema generation or compile-time schema generation. Runtime schema generation is
fine for examples and small prototypes, but compile-time generation is suitable for more realistic
development scenarios.
1. runtime schema generation can be added to your project by adding the following property to
your persistence-unit or hibernate.properties. Coldstart your database, comment out your SQL
plugin, and re-run your tests if you want to verify the above will create the database at runtime.
#persistence.xml
<property name="hibernate.hbm2ddl.auto" value="create"/>
#hibernate.properties
hibernate.hbm2ddl.auto=create
2. A set of files for schema can be generated by adding a standard set of properties to the
persistence.xml properties element.
<properties>
<property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create"/>
<property name="javax.persistence.schema-generation.scripts.create-target" value="target/classes/ddl/
entityMgrEx-JPAcreate.ddl"/>
<property name="javax.persistence.schema-generation.scripts.drop-target" value="target/classes/ddl/entityMgrEx-
JPAdrop.ddl"/>
</properties>
With the above configuration in place, the persistence unit will create two files in the target/
classes/ddl directory that represent the JPA provider's view of the mapping.
target/classes/ddl/
|-- emauto_create.ddl
|-- emauto_delete.ddl
|-- emauto_drop.ddl
|-- emauto_tuningadd.ddl
|-- emauto_tuningremove.ddl
|-- entityMgrEx-JPAcreate.ddl <== generated
`-- entityMgrEx-JPAdrop.ddl <== by the persistence unit
Chapter 9. Automatically Gene...
42
The primary downfall in this approach is that the schema is generated too late for us to use the
maven plugin to populate schema and it will execute this behavior all the way into production.
3. compile-time schema generation can be moved forward in the build cycle by instantiating the
persistence unit twice; once in a small program designed only to generate schema and once
for our unit tests. I have wrapped that small program in a Maven plugin which we can install
in our pom. It can be configured some. However, since I wrote it for use with this course -- it
pretty much does what we want without much configuration.
Add the following plugin definition to the pluginManagement section of your pom.xml. This will
define the core behavor of the jpa-schemagen-maven-plugin to execute the generate goal. By
default it executes during the process-test-classes phase.
<build>
<pluginManagement>
<plugins>
...
<plugin>
<groupId>info.ejava.utils.jpa</groupId>
<artifactId>jpa-schemagen-maven-plugin</artifactId>
<version>${ejava.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</pluginManagement>
</build>
4. Add the following active declaration to you pom to activate the plugin and fill in the module-
specifics. We could optionally add it to the database profiles.
...
</pluginManagement>
<plugins>
<plugin>
<artifactId>jpa-schemagen-maven-plugin</artifactId>
<groupId>info.ejava.utils.jpa</groupId>
<configuration>
<persistenceUnit>entityMgrEx</persistenceUnit>
</configuration>
</plugin>
</plugins>
</build>
43
5. Build your module and notice the generated JPA.ddl files
$ mvn clean process-test-classes
...
[INFO] --- jpa-schemagen-maven-plugin:5.0.0-SNAPSHOT:generate (default) @ entityMgrEx ---
[INFO] Generating database schema for: entityMgrEx
[INFO] removing existing target file:/Users/jim/proj/784/entityMgrEx/target/classes/ddl/entityMgrEx-drop.ddl
[INFO] removing existing target file:/Users/jim/proj/784/entityMgrEx/target/classes/ddl/entityMgrEx-create.ddl
Aug 14, 2018 10:28:50 PM org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
name: entityMgrEx
...]
Aug 14, 2018 10:28:50 PM org.hibernate.Version logVersion
...
INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@10850d17'
...
---
---
target/classes/ddl/
|-- emauto_create.ddl
|-- emauto_delete.ddl
|-- emauto_drop.ddl
|-- emauto_tuningadd.ddl
|-- emauto_tuningremove.ddl
|-- entityMgrEx-JPAcreate.ddl <== generated thru
|-- entityMgrEx-JPAdrop.ddl <===== configuration in persistence.xml
|-- entityMgrEx-create.ddl <== generated thru
`-- entityMgrEx-drop.ddl <===== plugin we just added
6. (Optionally) update your SQL plugin defintion added in previous chapter to reference the
dynamically generated schema in the target tree.
7. (Optionally) update your persistence.xml to turn off schema generation from within all uses of
the persistence unit.
8. Eclipse will again report a plugin error within the pom.xml editor. Add the following definition to
the lifecycle-mapping plugin to have the error ignored.
<pluginExecution>
<pluginExecutionFilter>
<groupId>info.ejava.utils.jpa</groupId>
<artifactId>jpa-schemagen-maven-plugin</artifactId>
<versionRange>[5.0.0-SNAPSHOT,)</versionRange>
<goals>
<goal>generate</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
Chapter 9. Automatically Gene...
44
9.1. Summary
In this chapter you configured a Maven project to create a set of file artifacts as a part of the
build that represent what the persistence provider believes the database should look like. You
can optionally directly use this as a part of your module's database schema population, use it as
a starting reference to manually create schema, or *most important* gain insight into what the
persistence provider thinks your persistence unit is defined. This will save you some ignorant bliss
that is usually followed by hours of debugging an incorrect mapping.
Chapter 10.
45
Create JPA Parent POMSince you will likely have many JPA modules in your enterprise application, lets take a moment to
break the current module into a parent and child before you quit. That way you can better visualize
which parts are specific to the child module and which are reusable from a common parent.
1. Create a sibling module called ../jpa-parent
$ mkdir ../jpa-parent
2. Add the module definition (../jpa-parent/pom.xml)
<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>myorg.jpa</groupId>
<artifactId>jpa-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JPA Parent POM</name>
<description>
This parent pom is intended to provide common and re-usable
definitions and constructs across JPA projects.
</description>
</project>
3. Add the following parent declaration to your existing module. The relativePath is only useful if
you find yourself changing the parent pom on a frequent basis. Otherwise the parent module
can be found in the localRepository once it has been installed.
<parent>
<groupId>myorg.jpa</groupId>
<artifactId>jpa-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../jpa-parent</relativePath>
</parent>
<groupId>myorg.jpa</groupId>
<artifactId>entityMgrEx-child</artifactId>
<name>Entity Manager Exercise</name>
4. Verify your project still builds. This will verify your relativePath is correct.
$mvn clean verify
...
[INFO] BUILD SUCCESS
Chapter 10. Create JPA Parent POM
46
5. Move the following constructs from the entityMgrEx module to the jpa-parent module. These
represent the *passive* definitions that will not directly impact the child module until the child
requests that feature. Your child module should still have the same build and test functionality
except now it should look a little smaller. One could also make a case for moving some of
the SQL/DDL script execution definitions also to the parent -- which would make this module
almost of trivial size.
• properties
• repositories
• dependencyManagement
• pluginManagement
• select profiles
<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>myorg.jpa</groupId>
<artifactId>jpa-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JPA Parent POM</name>
<description>
This parent pom is intended to provide common and re-usable
definitions and constructs across JPA projects.
</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source.version>1.8</java.source.version>
<java.target.version>1.8</java.target.version>
<jboss.host>localhost</jboss.host>
<db.host>${jboss.host}</db.host>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<maven-jar-plugin.version>3.1.0</maven-jar-plugin.version>
<maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version>
<sql-maven-plugin.version>1.5</sql-maven-plugin.version>
<h2db.version>1.4.197</h2db.version>
<javax.persistence-api.version>2.2</javax.persistence-api.version>
<hibernate-entitymanager.version>5.3.1.Final</hibernate-entitymanager.version>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.25</slf4j.version>
<ejava.version>5.0.0-SNAPSHOT</ejava.version>
</properties>
<dependencyManagement>
<dependencies>
47
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>${javax.persistence-api.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate-entitymanager.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.source.version}</source>
<target>${java.target.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
Chapter 10. Create JPA Parent POM
48
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>${surefire.argLine}</argLine>
<systemPropertyVariables>
<property.name>value</property.name>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>info.ejava.utils.jpa</groupId>
<artifactId>jpa-schemagen-maven-plugin</artifactId>
<version>${ejava.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<version>${sql-maven-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
</dependency>
</dependencies>
<configuration>
<username>${jdbc.user}</username>
<password>${jdbc.password}</password>
<driver>${jdbc.driver}</driver>
<url>${jdbc.url}</url>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile> <!-- H2 server-based DB -->
<id>h2srv</id>
<properties>
<jdbc.driver>org.h2.Driver</jdbc.driver>
<jdbc.url>jdbc:h2:tcp://${db.host}:9092/./h2db/ejava</jdbc.url>
<jdbc.user>sa</jdbc.user>
<jdbc.password/>
<hibernate.dialect>org.hibernate.dialect.H2Dialect</hibernate.dialect>
49
</properties>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile> <!-- H2 file-based DB -->
<id>h2db</id>
<activation>
<property>
<name>!jdbcdb</name>
</property>
</activation>
<properties>
<jdbc.driver>org.h2.Driver</jdbc.driver>
<jdbc.url>jdbc:h2:${basedir}/target/h2db/ejava</jdbc.url>
<jdbc.user>sa</jdbc.user>
<jdbc.password/>
<hibernate.dialect>org.hibernate.dialect.H2Dialect</hibernate.dialect>
</properties>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2db.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>testing</id>
<activation>
<property>
<name>!skipTests</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<!-- runs schema against the DB -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<executions>
<!-- place execution elements here -->
<execution>
<id>drop-db-before-test</id>
Chapter 10. Create JPA Parent POM
50
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<orderFile>decending</orderFile>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*tuningremove*.ddl</include>
<include>main/resources/ddl/**/*drop*.ddl</include>
</includes>
</fileset>
<onError>continue</onError>
</configuration>
</execution>
<execution>
<id>create-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<orderFile>ascending</orderFile>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*create*.ddl</include>
<include>main/resources/ddl/**/*tuningadd*.ddl</include>
</includes>
</fileset>
<print>true</print>
</configuration>
</execution>
<execution>
<id>populate-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>test/resources/ddl/**/*populate*.ddl</include>
</includes>
</fileset>
</configuration>
</execution>
51
<!--
<execution>
<id>drop-db-after-test</id>
<phase>test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*drop*.ddl</include>
</includes>
</fileset>
</configuration>
</execution>
-->
</executions>
</plugin>
</plugins>
</build>
</profile>
<!-- tell Eclipse what to do with some of the plugins -->
<profile>
<id>m2e</id>
<activation>
<property>
<name>m2e.version</name>
</property>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<versionRange>[1.0.0,)</versionRange>
<goals>
<goal>execute</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
Chapter 10. Create JPA Parent POM
52
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project>
6. Leave the following the child project. This is a collection of *active* project constructs.
• plugins
• dependencies
• module-specific properties
• profiles that declare plugins and dependencies
<?xml version="1.0"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>myorg.jpa</groupId>
<artifactId>jpa-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../jpa-parent</relativePath>
</parent>
<groupId>myorg.jpa</groupId>
<artifactId>entityMgrEx</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Entity Manager Exercise</name>
<build>
<!-- filtering will replace URLs, credentials, etc in the
files copied to the target directory and used during testing.
-->
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
<plugins>
<plugin>
53
<artifactId>jpa-schemagen-maven-plugin</artifactId>
<groupId>info.ejava.utils.jpa</groupId>
<configuration>
<persistenceUnit>entityMgrEx</persistenceUnit>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>testing</id>
<activation>
<property>
<name>!skipTests</name>
</property>
</activation>
<build>
Chapter 10. Create JPA Parent POM
54
<plugins>
<plugin>
<!-- runs schema against the DB -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<executions>
<!-- place execution elements here -->
<execution>
<id>drop-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<orderFile>decending</orderFile>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*tuningremove*.ddl</include>
<include>main/resources/ddl/**/*drop*.ddl</include>
</includes>
</fileset>
<onError>continue</onError>
</configuration>
</execution>
<execution>
<id>create-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<orderFile>ascending</orderFile>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*create*.ddl</include>
<include>main/resources/ddl/**/*tuningadd*.ddl</include>
</includes>
</fileset>
<print>true</print>
</configuration>
</execution>
<execution>
<id>populate-db-before-test</id>
<phase>process-test-classes</phase>
<goals>
<goal>execute</goal>
</goals>
55
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>test/resources/ddl/**/*populate*.ddl</include>
</includes>
</fileset>
</configuration>
</execution>
<!--
<execution>
<id>drop-db-after-test</id>
<phase>test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<autocommit>true</autocommit>
<fileset>
<basedir>${basedir}/src</basedir>
<includes>
<include>main/resources/ddl/**/*drop*.ddl</include>
</includes>
</fileset>
</configuration>
</execution>
-->
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
7. Verify your project still builds. This will verify your relativePath is correct.
$mvn clean verify
...
[INFO] BUILD SUCCESS
8. Optionally change your jpa-parent dependency to the class examples base parent project.
<parent>
<groupId>info.ejava.examples.build</groupId>
<artifactId>dependencies</artifactId>
<version>x.x.x-SNAPSHOT</version>
<relativePath>build/dependencies/pom.xml</relativePath>
</parent>
Chapter 10. Create JPA Parent POM
56
Note
Replace x.x.x-SNAPSHOT with the correct version for class.
Note
It is never a good idea to declare *active* POM constructs in a parent of a multi-
module project unless *ALL* child modules are of the same purpose. Strive for
parent Maven projects to define standards to follow without inserting unecessary
dependencies or other constructs.
10.1. Summary
In this chapter you re-factored your module into a reusable jpa-parent module (that could be
replaced by the class example) and child module. The child is then freed of defining version#s
and other constructs that can be shared across several modules -- allowing smaller child POMs to
be created through templates that produce modules that extend the jpa-parent. Cool. You didn't
want to do that again.
You have now finished all chapters of the Maven and JPA/EntityManager exercise that was geared
at getting you started developing modules that implement the data tier.
Recommended