Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf ·...

58
Hibernate Migration Migrating from Legacy Hibernate Session/HBM descriptors to JPA Revision: v2013-08-19 Built on: 2014-03-07 00:16 EST Copyright © 2014 jim stafford ([email protected]) This presentation provides information for developers to migrate a legacy application from Hibernate/HBM descriptor files to the Java Persistence API. It contains coverage of how to construct Maven poms for the build environment, how to layout the project, and how to leverage schema generation plugins within the build.

Transcript of Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf ·...

Page 1: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Hibernate Migration

Migrating from Legacy Hibernate

Session/HBM descriptors to JPA

Revision: v2013-08-19Built on: 2014-03-07 00:16 EST

Copyright © 2014 jim stafford ([email protected])

This presentation provides information for developers to migrate a legacy application from

Hibernate/HBM descriptor files to the Java Persistence API. It contains coverage of how to

construct Maven poms for the build environment, how to layout the project, and how to leverage

schema generation plugins within the build.

Page 2: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and
Page 3: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

iii

Purpose ............................................................................................................................ v

1. Hibernate Legacy Project ............................................................................................ 1

1.1. Class Model ....................................................................................................... 1

1.2. Hibernate Configuration ....................................................................................... 2

1.3. Hibernate Session API ........................................................................................ 7

1.4. Test Case .......................................................................................................... 9

1.5. Maven Build ..................................................................................................... 11

1.6. Generating Schema from HBM files ................................................................... 14

1.7. Summary .......................................................................................................... 16

2. JPA Persistence Unit Using ORM.xml Mapping Files ................................................ 17

2.1. Going from HBM to ORM mapping files ............................................................. 17

2.2. JPA EntityManager API ..................................................................................... 21

2.3. Obtaining HIbernate Session from JPA .............................................................. 23

2.4. Mixing HBM and JPA definitions ........................................................................ 23

2.5. Generating Schema for JPA files ....................................................................... 24

2.6. Summary .......................................................................................................... 26

3. Adding Metadata thru Class Annotations .................................................................. 27

3.1. Entity Class Annotations .................................................................................... 27

3.2. Using Annotated Classes with Hibernate Configuration ........................................ 29

3.3. Accessing Annotated Classes with Hibernate Session ......................................... 30

3.4. Adding Overrides thru Custom Configuration ...................................................... 30

3.5. Adding Overrides thru Custom NamingStrategy .................................................. 33

3.6. Generating Schema from Class Annotations and NamingStrategy ........................ 36

3.7. Summary .......................................................................................................... 38

4. JPA-based Project ..................................................................................................... 39

4.1. JPA Project Structure ........................................................................................ 39

4.2. Generating Schema from JPA Persistence Unit .................................................. 42

4.3. Summary .......................................................................................................... 45

5. Hibernate Migration Errors ........................................................................................ 47

5.1. Missing hibernate-entitymanager ........................................................................ 47

5.1.1. Symptom: NoClassDefFoundError: org.hibernate.cfg.Configuration ............ 47

5.1.2. Cause: Missing dependency on hibernate-entitymanager .......................... 47

5.1.3. Solution: Declare plugin dependency on hibernate-entitymanager .............. 48

5.2. Mixed Hibernate Versions with plugin and JPA ................................................... 48

5.2.1. Symptom: NoSuchMethodException:

org.hibernate.validator.ClassValidator.<init>(...) .................................................. 48

5.2.2. Cause: Using JPAConfiguration and version mis-matches ......................... 48

5.2.3. Solution: Declare plugin dependency on hibernate-entitymanager .............. 49

5.3. slf4j version mis-matches with later versions of Hibernate .................................... 49

5.3.1. Symptom: NoClassDefFoundError: org.slf4j.helpers.NOPLoggerFactory .... 49

5.3.2. Cause: plugin dependency on hibernate-entitymanager causes slf4j-api

version mis-match ............................................................................................ 50

5.3.3. Solution: Exclude slf4j-api from plugin dependency ................................... 50

5.4. Summary .......................................................................................................... 51

Page 4: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

iv

Page 5: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

v

Purpose

Many other topics on this site are centered on how to work with the modern APIs. This topic is

centered on how best to work with the legacy form of a Hibernate project and migrate it gradually

to modern JPA constructs. The Hibernate Session will still be in place -- under the hood as acting

as the provider -- and accessible through a legal JPA call when needed. However, we will start

benefiting from using a standard API (and Hibernate extensions) to express mappings.

During this topic we will...

• Demonstrate options to upgrade a legacy Hibernate project to include

• Migrate from from Hibernate Session to JPA EntityManager by replacing descriptor files

• Mix use of Hibernate Session with JPA EntityManager

• Migrate away from Hibernate HBM files to Java annotated classes

• Override Java annotated classes within Hibernate Session

• Fully migrate to JPA Entity Manager, class annotations, and ORM.xml overrides

• Demonstrate how to automatically generate database schema for a project based on

• Hibernate Session and 2.x version of hibernate3-maven-plugin

• JPA EntityManager and 2.x version of hibernate3-maven-plugin

• Hibernate Session and 3.x version of hibernate3-maven-plugin

• JPA EntityManager and 3.x version of hibernate3-maven-plugin

Page 6: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

vi

Page 7: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1.

1

Hibernate Legacy ProjectThis chapter describes a starting point for our sample legacy Hibernate project. It is based on an

un-annotated class model, HBM files, a hibernate.cfg.xml configuration, and a Hibernate Session

at runtime.

1.1. Class Model

The example legacy model consists of four classes (Person, Clerk, Customer, Sale) and an enum

(CustomerLevel). Each of the classes is meant to be used with FIELD access and each overrides

hashCode/equals to provide a stable and unique identity for use within Sets. Since these aspects

are not central to the migration -- they have been left out of this write-up but are available in the

source code repository.

1.

src/main

|-- java

| `-- ejava

| `-- jpa

| `-- hibernatemigration

| `-- legacyhbm

| |-- Clerk.java

| |-- Customer.java

| |-- CustomerLevel.java

| |-- Person.java

| `-- Sale.java

2. One of the simpler classes is Person. It is a base class to Customer and Clerk. It supplies an

id and name property for its derived classes.

package ejava.jpa.hibernatemigration.legacyhbm;

...

public class Person {

private int id;

private String name;

3. Clerk extends Person and adds a few temporal attributes for when they were hired and

terminated. Clerk also contains a Set of Sales which is part of a Many-to-Many collection with

Sale where Clerk will be the inverse side.

package ejava.jpa.hibernatemigration.legacyhbm;

...

public class Clerk extends Person {

private Set<Sale> sales;

private Date hireDate;

private Date termDate;

Page 8: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

2

4. Customer extends Person and adds an enum and String string attribute. It also contains a Set

of Sales which is part of a One-to-Many relation where Customer will be the inverse side.

package ejava.jpa.hibernatemigration.legacyhbm;

...

public class Customer extends Person {

private Set<Sale> purchases;

private String email;

private CustomerLevel level=CustomerLevel.BRONZE;

5. Sale is identified by a business-assigned primary key value -- implemented here as a UUID

String. Sale provides tracking of floating point information, a timestamp. Sale also has a Many-

to-One relation with Customer and a Many-to-Many relation with Clerk. In both cases -- Sale

will be the owning side.

public class Sale {

private String id;

private BigDecimal amount;

private Date dateTime;

private Set<Clerk> salesClerks;

private Customer customer;

public Sale() {

this(UUID.randomUUID().toString());

}

public Sale(String id) {

this.id = id;

}

That completes the key aspects of the example class model used in the application. Next we will

describe the Hibernate setup.

1.2. Hibernate Configuration

The following files make-up the Hibernate configuration. It consists of individual Hibernate

Mapping (HBM) files, a Hibernate Session configuration XML (hibernate.cfg.xml) and properties

(hibernate.properties) file. Although all can be placed in either the XML or properties file, the

structure of the session is expressed in the XML file in the src/main branch and the database

connection properties were expressed in the properties file in the src/test branch. That permits

the Session definition to be distributed without concern of the runtime properties being embedded

from the test environment.

1.

src

|-- main

| |-- java

| `-- resources

| |-- hibernate

Page 9: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Hibernate Configuration

3

| | |-- Clerk.hbm.xml

| | |-- Customer.hbm.xml

| | `-- Sale.hbm.xml

| `-- hibernate.cfg.xml

`-- test

`-- resources

`-- hibernate.properties

2. The Clerk mapping maps the entity to the HMIG_CLERK table and assigns a strategy

of IDENTITY for database assigned primary keys. It provides column size and optional

specifications for individual properties and expresses the granularity of the java.lang.Date-s

being persisted to be down to the DATE level. It defines a Many-to-Many relationship with Sale

where the Customer is the inverse side.

src/main/resources/hibernate/Clerk.hbm.xml

::::::::::::::

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="ejava.jpa.hibernatemigration.legacyhbm">

<class name="Clerk" table="HMIG_CLERK">

<id name="id" access="field">

<generator class="identity"/>

</id>

<property name="name"

access="field"

not-null="true"

length="32"/>

<property name="hireDate"

access="field"

not-null="true"

type="date"

column="HIRE_DATE"/>

<property name="termDate"

access="field"

type="date"

column="TERM_DATE"/>

<set name="sales" table="HMIG_SALE_CLERK" access="field" inverse="true">

<key column="CLERK_ID"/>

<many-to-many column="SALE_ID" class="Sale"/>

</set>

</class>

3. Customer assigns the table name and database identity generation the same as Clerk. Also

of note -- both Customer and Clerk have modeled their relationship with Person as a table per

concrete class. Customer uses a very odd syntax to map the String name of the enum to the

database [http://stackoverflow.com/questions/1896666/adding-an-enum-as-a-class-property-

in-hbm] table. The value 12 is the value for VARCHAR in the java.sql.Types [http://

docs.oracle.com/javase/1.5.0/docs/api/constant-values.html#java.sql.Types.VARCHAR] class

Page 10: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

4

(JPA has a much cleaner syntax for expressing enums). The Customer maps the inverse side

of a one-to-many with the Sale.

src/main/resources/hibernate/Customer.hbm.xml

::::::::::::::

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="ejava.jpa.hibernatemigration.legacyhbm">

<class name="Customer" table="HMIG_CUSTOMER">

<id name="id" access="field">

<generator class="identity"/>

</id>

<property name="name"

access="field"

not-null="true"

length="32"/>

<property name="email"

access="field"

length="32"/>

<property name="level"

access="field"

length="8">

<type name="org.hibernate.type.EnumType">

<param name="enumClass">ejava.jpa.hibernatemigration.legacyhbm.CustomerLevel</param>

<param name="type">12</param>

</type>

</property>

<set name="purchases" access="field" inverse="true">

<key column="CUSTOMER_ID"/>

<one-to-many class="Sale"/>

</set>

</class>

</hibernate-mapping>

4. The Sale mapping defines a manually assigned primary key, a decimal amount for the price, and

a temporal granularity of TIMESTAMP for the dateTime of sale. It defines the two relationships

with Custome and Clerk -- where Sale is the owner of both.

src/main/resources/hibernate/Sale.hbm.xml

::::::::::::::

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="ejava.jpa.hibernatemigration.legacyhbm">

<class name="Sale" table="HMIG_SALE">

<id name="id" access="field" length="36"/>

Page 11: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Hibernate Configuration

5

<property name="amount"

access="field"

not-null="true"

precision="7"

scale="2"/>

<property name="dateTime"

access="field"

not-null="true"

type="timestamp"

column="SALE_TIME"/>

<many-to-one name="customer" access="field"

class="Customer"

column="CUSTOMER_ID"

not-null="true"/>

<set name="salesClerks" access="field" table="HMIG_SALE_CLERK">

<key column="SALE_ID"/>

<many-to-many column="CLERK_ID" class="Clerk"/>

</set>

</class>

</hibernate-mapping>

5. Although the HBM files can be placed anywhere in the application, the default location for the

hibernate.cfg.xml file is in the root directory. Although not required -- this document can define

the runtime properties with the database (commented out below) and may reference the HBM

or other mapping constructs. If they are not listed here, they can be manually added through

calls to the Hibernate Configuration object constructed by the application.

src/main/resources/hibernate.cfg.xml

::::::::::::::

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<!-- defined in src/test/resources/hibernate.properties

<property name="connection.driver_class">org.h2.Driver</property>

<property name="connection.url">jdbc:h2:/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-

hibernate/hibernate-migration/hibernate-migration-docbook/target/h2db/ejava</property>

<property name="connection.username">sa</property>

<property name="connection.password"></property>

<property name="connection.pool_size">1</property>

<property name="dialect">org.hibernate.dialect.H2Dialect</property>

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create</property>

<property name="current_session_context_class">thread</property>

<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

-->

Page 12: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

6

<mapping resource="hibernate/Clerk.hbm.xml"/>

<mapping resource="hibernate/Sale.hbm.xml"/>

<mapping resource="hibernate/Customer.hbm.xml"/>

</session-factory>

</hibernate-configuration>

6. The above resulted in the following database schema using the

org.hibernate.dialect.HSQLDialect hibernate dialect.

create table HMIG_CLERK (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

HIRE_DATE date not null,

TERM_DATE date,

primary key (id)

);

create table HMIG_CUSTOMER (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

email varchar(32),

level varchar(8),

primary key (id)

);

create table HMIG_SALE (

id varchar(36) not null,

amount numeric not null,

SALE_TIME timestamp not null,

CUSTOMER_ID integer not null,

primary key (id)

);

create table HMIG_SALE_CLERK (

CLERK_ID integer not null,

SALE_ID varchar(36) not null,

primary key (SALE_ID, CLERK_ID)

);

alter table HMIG_SALE

add constraint FK862A2223AE3F6B6

foreign key (CUSTOMER_ID)

references HMIG_CUSTOMER;

alter table HMIG_SALE_CLERK

add constraint FK33F2DF19B837D55E

foreign key (CLERK_ID)

references HMIG_CLERK;

alter table HMIG_SALE_CLERK

add constraint FK33F2DF198C45F016

foreign key (SALE_ID)

references HMIG_SALE;

Page 13: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Hibernate Session API

7

7. We placed the runtime properties of the Session in hibernate.properties within the src/test

branch of the tree so it would not be part of the delivered artifact with out test properties. You will

notice the file leverages filtering -- where each ${variable} can be replaced during compilation

$ cat src/test/resources/hibernate.properties

hibernate.dialect=org.hibernate.dialect.H2Dialect

hibernate.connection.url=jdbc:h2:/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/

hibernate-migration/hibernate-migration-docbook/target/h2db/ejava

hibernate.connection.driver_class=org.h2.Driver

hibernate.connection.password=

hibernate.connection.username=sa

hibernate.connection.pool_size=1

hibernate.hbm2ddl.auto=create

hibernate.show_sql=true

hibernate.format_sql=true

#hibernate.jdbc.batch_size=0

hibernate.current_session_context_class=thread

hibernate.cache.provider_class=org.hibernate.cache.NoCacheProvider

8. This is what a listing of the final archive(s) look like with the above src/main information.

ddl/hibernate-hbm-createHBM.ddl

ddl/hibernate-hbm-dropHBM.ddl

ejava/jpa/hibernatemigration/legacyhbm/Clerk.class

ejava/jpa/hibernatemigration/legacyhbm/Customer.class

ejava/jpa/hibernatemigration/legacyhbm/CustomerLevel.class

ejava/jpa/hibernatemigration/legacyhbm/Person.class

ejava/jpa/hibernatemigration/legacyhbm/Sale.class

hibernate.cfg.xml

hibernate/Clerk.hbm.xml

hibernate/Customer.hbm.xml

hibernate/Sale.hbm.xml

You have finished taking a look at the descriptor files behind most legacy Hibernate approaches.

Next we will look at how the application can obtain references to Hibernate API -- which will use

the above information to access the database.

1.3. Hibernate Session API

We will demonstrate access to the Hibernate API through sub-classes of a JUnit testcase that we

will detail in the next section. Here we will show the extension points and how they where used

to obtain access to and use the Hibernate API. This same framework will be used in all of the

testcases demonstrated in this migration topic.

1. The methods below are all callback methods for JUnit or concrete implementations for abstract

methods defined in the base testcase.

src/test/java/ejava/jpa/hibernatemigration/legacyhbm/LegacyHBMTest.java

Page 14: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

8

::::::::::::::

package ejava.jpa.hibernatemigration.legacyhbm;

...

public class LegacyHBMTest extends BaseMigrationTest {

2. Access to the Hibernate API is through a Session. A Session is obtained from a SessionFactory

and a SessionFactory is built from a Configuration. The Configuration processes the information

we covered in the XML files above. You can see we have access to the Configuration object

during the following calls but have chosen to just let it have its default values from the supplied

XML files.

private static SessionFactory sessionFactory;

@BeforeClass

public static void setUpClass() {

log.debug("creating sessionFactory");

sessionFactory=new Configuration().configure().buildSessionFactory();

}

3. The Session is generally created to support a single transaction but may support many

depending on how long-lived you can make it. In the following setUp for each testMethod we

create a Session from the SessionFactory and start a transaction.

private Session session;

@Before

public void setUp() {

log.debug("creating session");

session = sessionFactory.getCurrentSession();

session.beginTransaction();

}

4. At the end of each testMethod the Session's transaction is committed and the session is closed.

@After

public void tearDown() {

if (session != null) {

if (session.getTransaction().isActive()) {

session.getTransaction().commit();

}

}

}

5. At the termination of the testcase, the SessionFactory is closed.

@AfterClass

public static void tearDownClass() {

Page 15: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Test Case

9

if (sessionFactory!=null) {

sessionFactory.close();

}

}

6. The remaining methods defined support the abstract methods of the base testcase covered

next. You will notice that all actions are based off the Session and the SessionFactory is nice

enough to let us know which is the current session.

@Override

protected void save(Object entity) { session.save(entity); }

@Override

protected void flush() { session.flush(); }

@Override

protected void clear() { session.clear(); }

@Override

@SuppressWarnings("unchecked")

protected <T> T get(Class<T> clazz, Serializable pk) { return (T)session.get(clazz, pk); }

@Override

protected void beginTransaction() { sessionFactory.getCurrentSession().beginTransaction(); }

@Override

protected void commitTransaction() { sessionFactory.getCurrentSession().getTransaction().commit(); }

You have finished looking at extending the testcase for Hibernate-specific implementation. Next

we will look at the base testcase that makes calls to what was just covered above.

1.4. Test Case

In this section will take the only look at the base testcase that is used across all this and

downstream examples. The testcase builds a sample instance of the class model described

above, persists the model, and retrieves it back from the database.

1. The calls in the testcase are encapsulated in the following JUnit test.

public abstract class BaseMigrationTest {

private final Log log = LogFactory.getLog(getClass());

protected abstract void save(Object entity);

protected abstract void flush();

protected abstract void clear();

protected abstract <T> T get(Class<T> clazz, Serializable pk);

protected abstract void beginTransaction();

protected abstract void commitTransaction();

@Test

public void testPersist() {

log.info("*** testPersist ***");

...

}

}

Page 16: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

10

2. First the test wires up instances in the class model using standard POJO calls.

//create our customer

Customer customer = new Customer();

customer.setName("joe");

customer.setEmail("[email protected]");

customer.setLevel(CustomerLevel.SILVER);

//create two clerks for the sale

Clerk clerk1 = new Clerk();

clerk1.setName("tom");

clerk1.setHireDate(new GregorianCalendar(2010, Calendar.JANUARY, 1).getTime());

//create the sale

Clerk clerk2 = new Clerk();

clerk2.setName("mary");

clerk2.setHireDate(new GregorianCalendar(2012, Calendar.JULY, 1).getTime());

Sale sale = new Sale();

sale.setDateTime(new Date());

sale.setAmount(new BigDecimal(100.12));

//associate the entities

sale.setCustomer(customer);

customer.setPurchaes(sale);

sale.addSalesClerk(clerk1);

clerk1.addSale(sale);

sale.addSalesClerk(clerk2);

clerk2.addSale(sale);

3. In the following segment, the instances are persisted in the session and later flushed to the

database to simulate a transaction ending.

//persist objects

log.info("(prior to persist) sale=" + sale);

save(customer);

save(clerk1);

save(clerk2);

save(sale);

//flushing session to database

flush();

log.info("(after flush) sale=" + sale);

4. Next we obtain new instances of the objects through their relationships -- starting with the Sale.

//get a new instance of sale

clear();

Sale sale2 = get(Sale.class, sale.getId());

Page 17: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Maven Build

11

//verify state and relationships were persisted

assertNotNull("could not locate sale", sale2);

assertEquals("unexpected amount", sale.getAmount().intValue(), sale2.getAmount().intValue());

5. We perform a sanity check of the other instances after finishing up with the sale.

//test the sale/customer many-to-one collection mapping from owning side

assertNotNull("no customer found", sale2.getCustomer());

assertEquals("unexpected sale.customer.id", sale.getCustomer().getId(), sale2.getCustomer().getId());

assertEquals("unexpected customer name", customer.getName(), sale2.getCustomer().getName());

//test the sale/customer many-to-one collection mapping from inverse side

assertEquals("unexpected purchase count", 1, sale2.getCustomer().getPurchases().size());

assertEquals("unexpected saleId", sale.getId(), sale2.getCustomer().getPurchases().iterator().next().getId());

//test the sale/clerk many-to-many collection from owning side

assertEquals("unexpected number of clerks", 2, sale2.getSalesClerks().size());

assertTrue("could not locate clerk1", sale2.getSalesClerks().contains(clerk1));

assertTrue("could not locate clerk2", sale2.getSalesClerks().contains(clerk2));

//test the sale/clerk many-to-many collection from inverse side

for (Clerk clerk: sale2.getSalesClerks()) {

assertEquals("unexpected number if sales", 1, clerk.getSales().size());

assertTrue("sale not found in clerk", clerk.getSales().contains(sale));

}

You have finished looking at the generic portion of the testcase that is used for this and all

downstream examples. It is a simple test -- but one that will help achieve our primary goal of

demonstrating the migration.

1.5. Maven Build

The above artifacts go nowhere without being built. To do that, they have been assembled into

a single Maven project. To make sure the demonstration was as realistic as possible, an old

version of Hibernate (dating back to 2007) was used for the legacy versions of the project. The real

example used multiple modules to build the solutions in order to optimize re-use across solution

approaches. We will show the solution here as a single logical module for simplicity.

1. Our legacy example uses a version of Hibernate that dates back to ~2007. This should provide

some comfort to users with older versions of Hibernate that what is demonstrated is not beyond

your reach.

<properties>

<!-- hibernate 3.2.0.ga was released to ibiblio 2007-01-04 -->

<legacy-hibernate.version>3.2.0.ga</legacy-hibernate.version>

...

</properties>

2. Besides the standard dependencies like commons-logging, junit, and log4j, the only

dependency required for this first example is the hibernate-entity-manager. It brings in several

Page 18: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

12

other dependencies (like hibernate-annotations) and makes it simple to obtain all that we need.

We are currently marking it as scope=provided to prevent a specific dependency on hibernate

when most of the hibernate dependency is within the tests and runtime and not the component

itself.

<dependencies>

...

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${legacy-hibernate.version}</version>

<scope>provided</scope>

</dependency>

...

</dependencies>

3. You saw earlier where the hibernate.cfg.xml and hibernate.properties files in the src/main and

src/test trees leveraged property filtering. The following build construct sets up the filtering when

copying from the src to target tree. Filtering replaces ${variable} references with values from

the build. The values come from a System property or property within the pom.

<build>

<resources>

<resource>

<directory>src/main/resources</directory>

<excludes>

<exclude>hibernate.cfg.xml</exclude>

</excludes>

</resource>

<resource>

<directory>src/main/resources</directory>

<includes>

<include>hibernate.cfg.xml</include>

</includes>

<filtering>true</filtering>

</resource>

</resources>

<testResources>

<testResource>

<directory>src/test/resources</directory>

<excludes>

<exclude>hibernate.properties</exclude>

</excludes>

</testResource>

<testResource>

<directory>src/test/resources</directory>

<includes>

<include>hibernate.properties</include>

</includes>

<filtering>true</filtering>

Page 19: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Maven Build

13

</testResource>

</testResources>

...

</build>

4. The properties for the specific database information is provided through a default profile. An

embedded file version of Hypersonic is being used for this demonstration. That means there is

no database to start/stop/manage as part of running these tests.

<profiles>

<profile>

<id>hsqldb</id> <!-- Hypersonic server-based DB -->

<activation>

<property>

<name>!jdbcdb</name>

</property>

</activation>

<properties>

<jdbc.driver>org.hsqldb.jdbcDriver</jdbc.driver>

<jdbc.url>jdbc:hsqldb:/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/

hibernate-migration/hibernate-migration-docbook/target/hsqldb/ejava</jdbc.url>

<jdbc.user>sa</jdbc.user>

<jdbc.password/>

<hibernate.dialect>

org.hibernate.dialect.HSQLDialect

</hibernate.dialect>

</properties>

<dependencies>

<dependency>

<groupId>org.hsqldb</groupId>

<artifactId>hsqldb</artifactId>

<scope>test</scope>

</dependency>

</dependencies>

</profile>

...

<profiles>

Using Alternate Profiles

You can switch to an alternate profile by turning off the default profile (-P\!

hsqldb) and turning on an alternate profile (-Phsqlsrv) -- also supplied in the

pom. Alternate profiles can be used to switch between embedded and server

versions of the same database as well as switching between different types of

databases.

5. You can look at the contents of the database after the test runs by executing the following jar

file and entering the file based URL specified above and the Standalone option.

$ java -jar ~/.m2/repository/org/hsqldb/hsqldb/2.2.8/hsqldb-2.2.8.jar

Page 20: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

14

6. You can build the model using standard Maven commands or import into your IDE and run.

$ mvn clean test

...

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

You have finished looking at the key maven aspects for building the legacy project. Most of the

remaining details are standard Maven constructs. However, there is one more important thing to

cover before we leave the legacy application and start migrating. We want to generate schema

from the supplied mappings so we can validate we have mapped things correctly.

1.6. Generating Schema from HBM files

Whether you use auto-generated schema to directly defined your database schema or simply use

it as a guide, it is quite valuable to generate schema form the entity mappings of your Hibernate

and JPA applications so you can sanity check the results and more easily spot errors that can go

undetected until late into runtime. We are going to leverage Hibernate's ability to generate schema

through the hibernate3-maven-plugin. Unfortunately the plugin went through a major release

change (from 2.2 to 3.0) sometime around 2012 and the configurations changed drastically. You

are going to be shown the legacy 2.x syntax here to stick with our theme of "older versions" for

the legacy project We will use the 3.x syntax in follow-on projects to get practice with both styles.

There is no reason to use the older version of the plugin if you have access to the new version.

The definition for the plugin can be quite lengthy in total, but can be reduced when factored into

a pluginManagement section in a shared parent pom.

1. Define the hibernate3-maven-plugin core structure. The version used in this case is 2.2.

<build>

<plugins>

<!-- generates a DDL file for persistence unit using an older version of the plugin -->

<plugin>

<artifactId>hibernate3-maven-plugin</artifactId>

<groupId>org.codehaus.mojo</groupId>

<version>${legacy-hibernate3-maven-plugin.version}</version>

<dependencies>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${legacy-hibernate.version}</version>

</dependency>

</dependencies>

<configuration>

<components>

<component>

<name>hbm2ddl</name>

<outputDirectory>target/classes/ddl</outputDirectory>

Page 21: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Generating Schema from HBM files

15

</component>

</components>

<componentProperties>

<configurationfile>target/classes/hibernate.cfg.xml</configurationfile>

<export>false</export>

<format>true</format>

</componentProperties>

</configuration>

<executions>

...

</executions>

</plugin>

</plugins>

</build>

2. Generate the drop and create scripts. Run them in a maven phase that assures you the classes

exist prior to running the plugin.

<execution>

<id>generate-drop-hbm</id>

<phase>process-test-resources</phase>

<goals>

<goal>hbm2ddl</goal>

</goals>

<configuration>

<componentProperties>

<outputfilename>hibernate-migration-docbook-dropHBM.ddl</outputfilename>

<drop>true</drop>

<create>false</create>

</componentProperties>

</configuration>

</execution>

<execution>

<id>generate-create-hbm</id>

<phase>process-test-resources</phase>

<goals>

<goal>hbm2ddl</goal>

</goals>

<configuration>

<componentProperties>

<outputfilename>hibernate-migration-docbook-createHBM.ddl</outputfilename>

<drop>false</drop>

<create>true</create>

</componentProperties>

</configuration>

</execution>

3. The above plugin generates the following two files that express creating the complete schema

and dropping it from the database.

Page 22: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 1. Hibernate Legacy P...

16

target/classes/ddl

|-- hibernate-hbm-createHBM.ddl

`-- hibernate-hbm-dropHBM.ddl

You have finished looking through how schema can be generated with the legacy hibernate3-

maven-plugin version using hibernate-specific techniques to define the session. In the next

chapter we will use the same version of the plugin to process a JPA persistence unit.

1.7. Summary

In this chapter we took a look at a legacy Hibernate application that we wish to migrate to JPA

through one of a couple possible first steps. This part of the example defined the class model, a

legacy database schema through the supplied mappings, a maven build and unit test, and schema

generation. I know of some projects that would be happy with just that!

Page 23: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 2.

17

JPA Persistence Unit Using

ORM.xml Mapping FilesThis chapter describes a stepping stone for those that wish to replace Hibernate-specific calls

with JPA standard calls or even use a mix of the two. We will show how the same legacy object

model is made available through JPA and also show how the legacy Hibernate Session can still

be obtained when needed.

2.1. Going from HBM to ORM mapping files

This may not be the sexiest part of the migration but it can get you one step closer to using a JPA

persistence unit when you cannot modify the source code of the entity classes being modeled.

We will create a set of JPA Object/Relational (ORM) XML files that will replace the HBM files.

1. The hibernate.cfg.xml file will get replaced by a META-INF/persistence.xml. The HBM files will

get replaced by orm.xml variants but notice there is an extra mapping file for the Person base

class. The hibernate.properties file we factored out in the legacy application can stay as defined

previously. Hibernate will pick that up.

src

|-- main

| |-- java

| `-- resources

| |-- jpa

| | |-- Clerk-orm.xml

| | |-- Customer-orm.xml

| | |-- Person-orm.xml

| | `-- Sale-orm.xml

| `-- META-INF

| `-- persistence.xml

`-- test

`-- resources

`-- hibernate.properties

2. With JPA we are able to create a reusable definition for the base Person class using a mapped-

superclass. This will end up being a table-per-concrete-class approach once complete. The

mapped-superclass is used to define reused properties as well as a primary key and generation

strategy.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm

http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

version="1.0">

Page 24: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 2. JPA Persistence Un...

18

<mapped-superclass class="ejava.jpa.hibernatemigration.legacyhbm.Person"

access="FIELD">

<attributes>

<id name="id">

<generated-value strategy="IDENTITY"/>

</id>

<basic name="name" optional="false">

<column length="32"/>

</basic>

</attributes>

</mapped-superclass>

</entity-mappings>

3. The Customer mapping extends the Person mapping and details the remaining properties. One

thing to note that is different between HBM and ORM.xml files -- unmapped properties in HBM

are @Transitent by default. Unmapped properties in JPA take on the default mapping properties

of the JPA Spec [http://download.oracle.com/otndocs/jcp/persistence-2.0-fr-eval-oth-JSpec/].

You must specifically annotate a property as transient in order for it to be ignored by JPA.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm

http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

version="1.0">

<!-- id and name mapped in superclass ORM -->

<entity class="ejava.jpa.hibernatemigration.legacyhbm.Customer"

access="FIELD">

<table name="HMIG_CUSTOMER"/>

<attributes>

<basic name="email">

<column length="32"/>

</basic>

<basic name="level">

<column length="8"/>

<enumerated>STRING</enumerated>

</basic>

<one-to-many name="purchases" mapped-by="customer"/>

</attributes>

</entity>

</entity-mappings>

Another key point is demonstrated above with respect to enums. Remember the cryptic syntax

used by Hibernate to map the STRING representation of the CustomerLevel enum to the

database? JPA uses a more straight forward enumerated element to do the same thing. (Of

course once we switch to annotations -- this argument becomes a very minor point)

Page 25: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Going from HBM to ORM mapping files

19

4. Clerk also extends the mapping of Person and adds some specifics of its own to match the HBM

mappings. Note the "mapped-by" denotes the "inverse" side in JPA. The mapped-by element

references the property in the owning entity that defines the mapping to the database.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm

http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

version="1.0">

<!-- id and name mapped in superclass ORM -->

<entity class="ejava.jpa.hibernatemigration.legacyhbm.Clerk"

access="FIELD">

<table name="HMIG_CLERK"/>

<attributes>

<basic name="hireDate" optional="false">

<column name="HIRE_DATE"/>

<temporal>DATE</temporal>

</basic>

<basic name="termDate">

<column name="TERM_DATE"/>

<temporal>DATE</temporal>

</basic>

<many-to-many name="sales" mapped-by="salesClerks"/>

</attributes>

</entity>

</entity-mappings>

5. Sale is demonstrating a few extra constructs offered by JPA where we can assigned the default

package and access type for entities within that package. This can be helpful if you package

multiple entities from the same package in to the same file.

src/main/resources/jpa/Sale-orm.xml

::::::::::::::

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm

http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

version="1.0">

<package>ejava.jpa.hibernatemigration.legacyhbm</package>

<access>FIELD</access>

<entity class="Sale">

<table name="HMIG_SALE"/>

<attributes>

<id name="id">

<column length="36"/>

</id>

<basic name="amount" optional="false">

Page 26: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 2. JPA Persistence Un...

20

<column precision="7" scale="2"/>

</basic>

<basic name="dateTime" optional="false">

<column name="SALE_TIME"/>

<temporal>TIMESTAMP</temporal>

</basic>

<many-to-one name="customer" optional="false">

<join-column name="CUSTOMER_ID"/>

</many-to-one>

<many-to-many name="salesClerks">

<join-table name="HMIG_SALE_CLERK">

<join-column name="SALE_ID"/>

<inverse-join-column name="CLERK_ID"/>

</join-table>

</many-to-many>

</attributes>

</entity>

</entity-mappings>

6. Like with the hibernare.cfg.xml, the persistence unit can defined properties of the connection

and vendor-specific properties about the provider (all commented out) as well as references

to the mapping files. One additional thing to point out with JPA -- mapping files augment and

override classes and their annotations. With HBM files -- it is one or the other. With JPA,

ORM files can augment and override the class annotations as well as instruct the provider to

ignore the class annotations (with "metadata-complete"). This allows the developer to place

core mappings within the Java class and leverage filterable ORM.xml files to change what is

volatile.

<?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_1_0.xsd"

version="1.0">

<persistence-unit name="hibernate-migration-sales">

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<mapping-file>jpa/Person-orm.xml</mapping-file>

<mapping-file>jpa/Customer-orm.xml</mapping-file>

<mapping-file>jpa/Clerk-orm.xml</mapping-file>

<mapping-file>jpa/Sale-orm.xml</mapping-file>

<properties>

<!-- properties moved to hibernate.properties file

<property name="connection.driver_class" value="org.h2.Driver"/>

<property name="connection.url" value="jdbc:h2:/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-

providers/jpa-hibernate/hibernate-migration/hibernate-migration-docbook/target/h2db/ejava"/>

<property name="connection.username" value="sa"/>

<property name="connection.password" value=""/>

<property name="connection.pool_size" value="1"/>

Page 27: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

JPA EntityManager API

21

<property name="dialect" value="org.hibernate.dialect.H2Dialect"/>

<property name="show_sql" value="true"/>

<property name="hbm2ddl.auto" value="create"/>

<property name="current_session_context_class" value="thread"/>

<property name="cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>

-->

</properties>

</persistence-unit>

</persistence>

You have finished looking at the migration from HBM to ORM.xml files. As stated, we can form

a JPA persistence unit with ORM.xml mappings (and class mappings later on). With Hibernate,

all unmapped properties are transient. With JPA -- all unmapped properties have their JPA Spec-

defined mappings defaults applied and must be specifically marked as transient to be ignored.

ORM.xml mappings can augment or replace metadata expressed in the entity classes. With

straight Hibernate, you can only use the HBM file or the entity class annotations -- not both.

2.2. JPA EntityManager API

In this section we will demonstrate how to access the JPA API for interacting with the persistence

unit. As done previously, we will show access to the API through extension points in our testcase.

1. The persistence unit is represented at runtime by a persistence context and accessed through

the EntityManager. An EntityManager is created from an EntityManagerFactory and the

EntityManagerFactory is configured through the persistence unit we put together above. Each

persistence.xml can define multiple persistence units -- so we must pass in the name of the

persistence unit when creating the EntityManagerFactory.

public class ORMMappingTest extends BaseMigrationTest {

private static final String PERSISTENCE_UNIT_NAME = "hibernate-migration-sales";

private static EntityManagerFactory emf;

@BeforeClass

public static void setUpClass() {

log.debug("creating entityManagerFactory");

emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);

}

2. EntityManagers are usually associated with a single transaction or many transactions

for applications that can keep them open long enough. In the testcase, we create the

EntityManager and start a transaction prior to each testcase.

private EntityManager em;

@Before

public void setUp() {

log.debug("creating session");

em = emf.createEntityManager();

Page 28: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 2. JPA Persistence Un...

22

em.getTransaction().begin();

}

3. The transaction is committed and the EntityManager is closed at the end of the testcase.

@After

public void tearDown() {

if (em != null) {

if (em.getTransaction().isActive()) {

em.getTransaction().commit();

}

em.close();

}

}

4. The EntityManagerFactory is closed at the end of the testcase.

@AfterClass

public static void tearDownClass() {

if (emf!=null) {

emf.close();

}

}

5. The rest of the concrete methods that map the abstract testcase calls to the JPA API are shown

below. Note there is a near one-to-one mapping between what Hibernate and JPA need to do

in each case.

@Override

protected void save(Object entity) { em.persist(entity); }

@Override

protected void flush() { em.flush(); }

@Override

protected void clear() { em.clear(); }

@Override

protected <T> T get(Class<T> clazz, Serializable pk) { return em.find(clazz, pk); }

@Override

protected void beginTransaction() {

if (em!=null) { em.getTransaction().begin(); }

}

@Override

protected void commitTransaction() {

if (em!=null) { em.getTransaction().commit(); }

}

Page 29: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Obtaining HIbernate Session from JPA

23

2.3. Obtaining HIbernate Session from JPA

JPA is a standard that freely admits it may not cover all cases addressed by the providers that

implement it. There are official back doors added for SQL database access and access to the raw

provider implementation behind the EntityManager (i.e., the Hibernate Session).

1. The following call can be made to obtain a Hibernate Session object from behind the JPA

EntityManager facade.

protected Session getCurrentSession() { return (em==null)?null : (Session)em.getDelegate(); }

In this quick section we showed how a client can gain access to the legacy/raw Hibernate Session

object after migrating to a JPA interface. This can be useful to make provider-specific calls. In the

next section we will show a more hybrid approach where some of the entities were mapped with

JPA and some with Hibernate.

2.4. Mixing HBM and JPA definitions

In this section we will backup a half step and show how to model a partion of the class model in

HBM files and the remaining portion in ORM files and have the Hibernate-specific mapped entities

be used with the JPA-mapped entities. The only requirement is that the Hibernate Session used

must be the one obtained from the EntityManager.getDelegate() call.

1. Our mapping tree looks like the following. We are using a Hibernate-specific HBM file for

both Clerk and Customer and a JPA orm.xml file for Sale. Both a hibernate.cfg.xml and

persistence.xml are also supplied.

src

|-- main

| |-- java

| `-- resources

| |-- hibernate

| | |-- Clerk.hbm.xml

| | `-- Customer.hbm.xml

| |-- hibernate.cfg.xml

| |-- jpa

| | `-- Sale-orm.xml

| `-- META-INF

| `-- persistence.xml

`-- test

`-- resources

`-- hibernate.properties

2. The hibernate.cfg.xml file references the two HBM files used to map the Clerk and Customer.

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

Page 30: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 2. JPA Persistence Un...

24

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<mapping resource="hibernate/Clerk.hbm.xml"/>

<mapping resource="hibernate/Customer.hbm.xml"/>

<!-- mapped with JPA

<mapping resource="hibernate/Sale.hbm.xml"/>

-->

</session-factory>

3. The persistence.xml provides a file reference to the ORM.xml file used to map the Sale.

<?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_1_0.xsd"

version="1.0">

<persistence-unit name="hibernate-migration-sales">

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<!-- mapped with HBM

<mapping-file>jpa/Person-orm.xml</mapping-file>

<mapping-file>jpa/Customer-orm.xml</mapping-file>

<mapping-file>jpa/Clerk-orm.xml</mapping-file>

-->

<mapping-file>jpa/Sale-orm.xml</mapping-file>

</persistence-unit>

</persistence>

In this section we took a half step back and opened the door for a partial migration from Hibernate

HBM to JPA ORM.xml files. This option option is obviously appealing for modules with highly

complex HBM file declarations. With this approach, one can use the JPA API while retaining

their 100% Hibernate defined mapping to the database. We will finish up in the next section by

generating schema using the JPA instead of HBM files.

2.5. Generating Schema for JPA files

In this section we will describe the only part of the Maven build that is unique to this section --

generating schema from a JPA persistence unit versus a Hibernate Session configuration.

1. To keep the document short, we are only going to show the important differences between the

HBM schema generation and the JPA approach. Notice how -- in the configuration section of the

plugin -- a jpaconfiguration implementation is being expressed and the componentProperties

section provides the name of the persistence unit. That is all we should need to process the

ORM.xml files.

Page 31: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Generating Schema for JPA files

25

<configuration>

<components>

<component>

<name>hbm2ddl</name>

<implementation>jpaconfiguration</implementation>

<outputDirectory>target/classes/ddl</outputDirectory>

</component>

</components>

<componentProperties>

<persistenceunit>hibernate-migration-sales</persistenceunit>

<export>false</export>

<format>true</format>

</componentProperties>

</configuration>

2. The following is an example of what was produced.

create table HMIG_CLERK (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

HIRE_DATE date not null,

TERM_DATE date,

primary key (id)

);

create table HMIG_CUSTOMER (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

email varchar(32),

level varchar(8),

primary key (id)

);

create table HMIG_SALE (

id varchar(36) not null,

amount numeric not null,

SALE_TIME timestamp not null,

CUSTOMER_ID integer not null,

primary key (id)

);

create table HMIG_SALE_CLERK (

SALE_ID varchar(36) not null,

CLERK_ID integer not null,

primary key (SALE_ID, CLERK_ID)

);

alter table HMIG_SALE

add constraint FK862A2223AE3F6B6

foreign key (CUSTOMER_ID)

references HMIG_CUSTOMER;

Page 32: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 2. JPA Persistence Un...

26

alter table HMIG_SALE_CLERK

add constraint FK33F2DF198C45F016

foreign key (SALE_ID)

references HMIG_SALE;

alter table HMIG_SALE_CLERK

add constraint FK33F2DF19B837D55E

foreign key (CLERK_ID)

references HMIG_CLERK;

You have finished looking at how to generate database schema from a JPA persistence unit

definition and the 2.x version of the hibernate3-maven-plugin. In the follow-on sections we will

switch to using the newer 3.x implementation/configuration.

2.6. Summary

In this chapter we took an approach that would get us to a JPA API without changing the

Java classes. We showed and approach where the hibernate.cfg.xml was replaced by the

persistence.xml to express the persistence unit. We saw where the hbm.xml mappings were

replaced by the orm.xml mappings to allow for vendor neutral mappings and a definition usuable by

the persistence unit. We also kept the door open to raw Hibernate usage. At one level we showed

how we can simply ask for the Hibernate Session from the JPA EntityManager. At another level we

showed how -- since the provider in this case is Hibernate -- we can leave hibernate.cfg.xml and

hbm.xml files in place to reference entities we have not yet migrated. They can be stored/accessed

by the outer JPA EntityManager and the Hibernate Session configured from the EntityManager.

Page 33: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 3.

27

Adding Metadata thru Class

AnnotationsThis chapter describes a migration increment where we get rid of verbose Hibernate HBM files

and replace the XML source of metadata with class annotations. This permits class design details

to be expressed in one location and not in separate Java class and XML files. However -- there

is a catch. There is debate whether some of the metadata annotations are appropriate to place

in a Java class. JPA solves this issue by allowing the entity class to express a default and that

can be augmented or replaced by metadata within the ORM.xml. Hibernate Session -- however

is one or the other. You can use annotations or HBM files and not a combination of both. This

section will demonstrate how the entity annotations processed by the Hibernate Session can be

overridden by the application.

3.1. Entity Class Annotations

In this section we will migrate the HBM or ORM mappings from their XML files to the entity classes.

This makes for a very concise and readable design.

1. To get started we are going to create a new version of the example class model and leave

the older one in tact.

src/main

|-- java

| `-- ejava

| `-- jpa

| `-- hibernatemigration

| |-- annotated

| | |-- Clerk.java

| | |-- Customer.java

| | |-- CustomerLevel.java

| | |-- Person.java

| | `-- Sale.java

2. The annotations we see below should map 1:1 with the annotations we defined within the

ORM.xml files in the previous section. In this case the Person entity class is declaring it as an

@MappedSuperclass and defining a few properties inherited by Clerk and Customer.

@MappedSuperclass

public class Person {

@Id @GeneratedValue(strategy=GenerationType.IDENTITY)

private int id;

@Basic(optional=false)

@Column(length=32)

private String name;

Page 34: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 3. Adding Metadata th...

28

3. One nice aspect of using class annotations is that most information is type-safe. In defining the

DATE granularity for the hire/termDate, the developer cannot make a spelling or syntax error

without the compiler complaining.

@Entity

@Table(name="HMIG_CLERK")

public class Clerk extends Person {

@ManyToMany(mappedBy="salesClerks")

private Set<Sale> sales;

@Basic(optional=false)

@Temporal(TemporalType.DATE)

@Column(name="HIRE_DATE")

private Date hireDate;

@Temporal(TemporalType.DATE)

@Column(name="TERM_DATE")

private Date termDate;

4. Even though we have spent a decent amount of effort on defining VARCHAR lengths and

dateTime accuracy for the database schema -- realize that most of the mapping information

is not used beyond optional schema creation or validation. A JPA provider will not check that

a String attribute is within @Column.length constraints. These types of checks are left to the

database to enforce.

@Entity

@Table(name="HMIG_CUSTOMER")

public class Customer extends Person {

@OneToMany(mappedBy="customer")

private Set<Sale> purchases;

@Column(length=32)

private String email;

@Enumerated(EnumType.STRING)

@Column(length=8)

private CustomerLevel level=CustomerLevel.BRONZE;

5. Sale was left un-mapped by entity class annotations and will be required to use an HBM

mapping. This will provide an example of a hybrid approach that mixes class annotations and

HBM file(s).

public class Sale {

private String id;

private BigDecimal amount;

private Date dateTime;

private Set<Clerk> salesClerks;

private Customer customer;

You have finished migrating from HBM mappings to entity class annotations. Hopefully you can

see the benefits and risks associated with placing information directly within the class mapped to

Page 35: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Using Annotated Classes with Hibernate Configuration

29

the database. Next we will look at a few remaining mapping issues before our application is ready

to run with this new approach.

3.2. Using Annotated Classes with Hibernate

Configuration

In this section we will look at what is required to use the annotated classes with Hibernate. For the

most part, the main difference is using a different Configuration class -- that is Annotation-aware.

This class is provided in the hibernate-annotations artifact.

1. Remember we left one of the classes unmapped from class annotations so we have to provide a

mapping for that class in an HBM file. This is the same mapping you have seen for Sale earlier.

src/main

|-- java

`-- resources

|-- hibernate

| `-- Sale.hbm.xml

`-- hibernate.cfg.xml

2. The hibernate.xfg.xml looks a little different from what you last saw -- but I am sure the changes

present here are very obvious. We have traded an HBM reference for a class reference for the

Clerk and Customer classes. Sale still uses a reference to an HBM mapping definition.

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<!-- some of the classes use annotations -->

<mapping class="ejava.jpa.hibernatemigration.annotated.Clerk"/>

<mapping class="ejava.jpa.hibernatemigration.annotated.Customer"/>

<!-- this class does not use annotations -->

<mapping resource="hibernate/Sale.hbm.xml"/>

</session-factory>

</hibernate-configuration>

You have finished going through the Hibernate configuration for using annotated classes. The

main difference is that the classes are listed versus the HBM mappings in the hibernate.cfg.xml

(or API equivalent of the Configuration). Next we will look at the API changes we need at start-up.

Page 36: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 3. Adding Metadata th...

30

3.3. Accessing Annotated Classes with Hibernate

Session

There is only a single change required when adding annotated classes to the Hibernate Session.

Instead of instantiating a normal Configuration class when building a SessionFactory -- we

instantiate an AnnotationConfiguration instead. The rest is identical to what you saw in the legacy

HBM approach.

1.

@BeforeClass

public static void setUpClass() {

log.debug("creating sessionFactory");

sessionFactory=new AnnotationConfiguration().configure().buildSessionFactory();

}

You have finished going through the few changes necessary to access the annotated classes

as entities using Hibernate Session. At this point we may be feeling good -- especially since we

can mix annotated and non-annotated approaches. However, the mixture is at the Session level

and not within a specific entity class. Hibernate provides no means to override the entity class

annotations using the HBM file. In fact, some of the elements in the HBM schema are required

and cannot be left out. The next section shows a first option in achieving an annotation override.

3.4. Adding Overrides thru Custom Configuration

For all the great things one can do with entity class annotations and all the good one can optionally

do with JPA mapping file overrides, Hibernate does not offer that override capability through its

HBM mapping construct. You must do so through API calls. We will start with the first of two

options -- custom Configuration

1. Lets start with the end of the story and work backwards since it is easier to see that way. Notice

what we have done to the Configuration. We have replaced the Hibernate call with a version

we have customized for our own use.

public class CustomizedConfigurationHBMTest extends BaseAnnotatedMigrationTest {

...

@BeforeClass

public static void setUpClass() {

log.debug("creating sessionFactory");

sessionFactory=new CustomizedConfiguration().configure().buildSessionFactory();

}

2. The class is implemented as an extension of the AnnotationConfiguration class since it is the

entity class annotations we primarily wish to override.

public class CustomizedConfiguration extends AnnotationConfiguration {

Page 37: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Adding Overrides thru Custom Configuration

31

private static final long serialVersionUID = 1L;

private static final Log log = LogFactory.getLog(CustomizedConfiguration.class);

private Map<String, String> tableMap = new HashMap<String, String>();

...

}

3. The overridden call to configure() calls the parent and then looks for any properties in the

session configuration that expresses a table name override for a particular class. Once found

-- it is stored in a map for a follow-on callback.

@Override

public Configuration configure() throws HibernateException {

log.info(AnnotationConfiguration.class.getResource("AnnotationConfiguration.class"));

Configuration config = super.configure();

for (Object o : getProperties().keySet()) {

String key = (String)o;

log.info("checking " + o);

//different versions of hibernate express key property differently

if (key.matches("(hibernate.)*(tableName:)+.*")) {

String[] tokens = key.split(":");

if (tokens.length != 2) {

log.warn("bad tableName key:" + key);

continue;

}

String className = tokens[1];

String tableName = getProperty(key);

if (tableName==null || tableName.length()==0) {

log.warn("empty tableName value:" + key);

continue;

}

tableMap.put(className, tableName);

}

}

return config;

}

4. After the secondPassCompile callback is complete, all classes have a PersistClass associated

with them and this class houses the class' mapping to the database. If we can find the call we

are configured to use -- we have succeeded in overriding at least one type of annotation.

@Override

protected void secondPassCompile() throws MappingException {

super.secondPassCompile();

for (Entry<String, String> e: tableMap.entrySet()) {

String className = e.getKey();

String tableName = e.getValue();

PersistentClass pc = getClassMapping(className);

if (pc == null) {

log.warn("entity class " + tableName + " not found in session configuration");

continue;

}

Page 38: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 3. Adding Metadata th...

32

String oldName = pc.getTable().getName();

if (!tableName.equals(oldName)) {

pc.getTable().setName(tableName);

log.info(String.format("changed %s tableName from %s to %s",

className, oldName, tableName));

}

}

}

5. To demonstrate our override we add a tableName property to the hibernate.cfg.xml properties.

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<!-- custom overrides for annotated classes -->

<property name="tableName:ejava.jpa.hibernatemigration.annotated.Clerk">HMIG_CLERK_OVERRIDE</

property>

<!-- some of the classes use annotations -->

<mapping class="ejava.jpa.hibernatemigration.annotated.Clerk"/>

<mapping class="ejava.jpa.hibernatemigration.annotated.Customer"/>

<!-- this class does not use annotations -->

<mapping resource="hibernate/Sale.hbm.xml"/>

</session-factory>

</hibernate-configuration>

6. During the configure step our class recognizes the pattern, parses the tokens and stores the

className and tableName for after the secondPassCompile.

-checking tableName:ejava.jpa.hibernatemigration.annotated.Clerk

7. After the secondPassCompile you can see Hibernate print what it determined was going to be

the proper table mappings and then our override comes in at the end.

-Mapping class: ejava.jpa.hibernatemigration.annotated.Sale -> HMIG_SALE

-Mapping collection: ejava.jpa.hibernatemigration.annotated.Sale.salesClerks -> HMIG_SALE_CLERK

-Binding entity from annotated class: ejava.jpa.hibernatemigration.annotated.Clerk

-Bind entity ejava.jpa.hibernatemigration.annotated.Clerk on table HMIG_CLERK

-Binding entity from annotated class: ejava.jpa.hibernatemigration.annotated.Customer

-Bind entity ejava.jpa.hibernatemigration.annotated.Customer on table HMIG_CUSTOMER

-Mapping collection: ejava.jpa.hibernatemigration.annotated.Customer.purchases -> HMIG_SALE

-changed ejava.jpa.hibernatemigration.annotated.Clerk tableName from HMIG_CLERK to

HMIG_CLERK_OVERRIDE

Page 39: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Adding Overrides thru Custom NamingStrategy

33

8. In the debug output for the SQL exchanges with the database, we can verify the tableName

has been changed.

Hibernate:

insert

into

HMIG_CLERK_OVERRIDE

(id, name, HIRE_DATE, TERM_DATE)

values

(null, ?, ?, ?)

9. Life is good! right? Not so fast. Although the Hibernate Session authors may have been open to

us extending Configuration and changing things -- the tool writers didn't get that same memo.

When generating schema the generation configuration does not allow us to express anything

beyind a straight configuration, annotationconfiguration, or one of the other classes supported

by Hibernate. For that reason, we get the followingun-customized mapping generated during

database schema generation.

create table HMIG_CLERK (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

HIRE_DATE date not null,

TERM_DATE date,

primary key (id)

);

You are done looking at our first attempt at implementing a property override when using a

Hibernate Session and entity class annotations. We achieved most of our goal but fell short of tool

integration. Luckily there is another option that is supported by DDL generation. In the next section

we will take a look at providing the given Configuration a NamingStrategy rather than extending

the Configuration.

3.5. Adding Overrides thru Custom NamingStrategy

Hibernate provides the option of overriding its DefaultNamingStrategy with a few built-in versions

as well as your own custom override. This class is consulted for names for entities, tables,

columns, join-columns, etc. We can clearly jump in there. The only down-side is that it class does

not receive any callback with the configuration. You may have to cook something up yourself (like

we did here).

1. We start off by extending the default Hibernate naming strategy class. This class will accept a

configuration mapping from class-to-table and and override mapping from table-to-table. The

former is called when there is no table specification on the class. The later is called when there

is a table specification and gives us a chance to change it.

public class CustomizedNamingStrategy extends DefaultNamingStrategy {

Page 40: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 3. Adding Metadata th...

34

private static final Log log = LogFactory.getLog(CustomizedNamingStrategy.class);

//mapping from className to tableName

private Map<String, String> classTableMap = new HashMap<String, String>();

//tableName override

private Map<String, String> tableMap = new HashMap<String, String>();

...

}

2. We add some methods to allow our application to configure the overrides

public CustomizedNamingStrategy addClassTableMapping(String className, String tableName) {

classTableMap.put(className, tableName);

return this;

}

public CustomizedNamingStrategy addTableMapping(String oldName, String newName) {

tableMap.put(oldName, newName);

return this;

}

3. We implement the callback method when the provider is looking for a table name and there

was none provided in the class annotations.

@Override

public String classToTableName(String className) {

log.debug("classToTableName(" + className + ")");

String tableName = super.classToTableName(className);

String newName = classTableMap.get(className);

if (newName != null) {

log.info(String.format("updating %s from tableName: %s to %s",

className, tableName, newName));

tableName = newName;

}

return tableName;

}

4. We implement the callback method when the provider is looking for a table name and there

was one provided by the class annotation. In this case we are given a chance to override what

the configuration was going to use.

@Override

public String tableName(String tableName) {

log.debug("tableName(" + tableName + ")");

String newName = tableMap.get(tableName);

if (newName != null) {

log.info(String.format("updating tableName from %s to %s",

tableName, newName));

tableName = newName;

}

return tableName;

Page 41: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Adding Overrides thru Custom NamingStrategy

35

}

5. One more step is to create a class that sets the mapping values. Clearly this could be driven

off of anything and was hard-coded here for quick implementation.

public class ProjectNamingStrategy extends CustomizedNamingStrategy {

public ProjectNamingStrategy() {

//this is in case the class metadata did not specify a tableName

addClassTableMapping(Customer.class.getName(), "HMIG_CUSTOMER_STRATEGY");

//this is in case the class metadata did spec a tableName we want to override

addTableMapping("HMIG_CUSTOMER", "HMIG_CUSTOMER_STRATEGY");

}

}

6. We now set an instance of our NamingStrategy as the naming strategy for the configuration.

@BeforeClass

public static void setUpClass() {

log.debug("creating sessionFactory");

sessionFactory=new AnnotationConfiguration()

.setNamingStrategy(new ProjectNamingStrategy())

.configure().buildSessionFactory();

}

7. During the SessionFactory creation -- we can see our naming strategy get called and get a

chance to override our targeted table name.

-updating tableName from HMIG_CUSTOMER to HMIG_CUSTOMER_STRATEGY

-Bind entity ejava.jpa.hibernatemigration.annotated.Customer on table HMIG_CUSTOMER_STRATEGY

8. Like with the previous strategy, we can now see our override taking place.

Hibernate:

insert

into

HMIG_CUSTOMER_STRATEGY

(id, name, email, level)

values

(null, ?, ?, ?)

9. Unlike our previous strategy, we can also provide this NamingStrategy to the DLL generation

tool. This looks different from before since we have switched to the new hibernate3-maven-

plugin API in this example. More details later -- but notice we can provide a specification to

our NamingStrategy.

<hibernatetool>

Page 42: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 3. Adding Metadata th...

36

<annotationconfiguration configurationfile="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-

hibernate/hibernate-migration/hibernate-migration-docbook/target/classes/hibernate.cfg.xml"

namingstrategy="ejava.jpa.hibernatemigration.ProjectNamingStrategy"/>

<hbm2ddl export="false" create="true" drop="false" format="true"

outputfilename="hibernate-migration-docbook-createNAM.ddl"/>

</hibernatetool>

10.The following is an example of what is generated from the DDL plugin.

create table HMIG_CUSTOMER_STRATEGY (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

email varchar(32),

level varchar(8),

primary key (id)

);

...

alter table HMIG_SALE

add constraint FK862A2223AB739428

foreign key (CUSTOMER_ID)

references HMIG_CUSTOMER_STRATEGY;

We have finished looking at the custom NamingStrategy option. Both this and the custom

Configuration option gave us most of what we wanted, but this one added a small amount of tool

support that could be quite useful. Next we will look at the full hibernate3-maven-plugin that made

this happen since we are now switching to the new 3.x release.

3.6. Generating Schema from Class Annotations and

NamingStrategy

As a move forward, we are going to switch to the 3.x version of the hibernate3-maven-plugin.

This upgrade wraps Hibernate Ant tasks and just serves as a delegation wrapper. Most of

the documentation is at the Ant level [http://docs.jboss.org/tools/latest/en/hibernatetools/html/

ant.html].

1. My success varied with this plugin and versions required. You will notice it provides the ability to

declare a separate plugin dependency on the hibernate-entitymanager. I found this useful when

my application used hibernate4 and the plugin required hibernate3. However, if the application

uses hibernate3 -- then the version used by the plugin and pom dependency must be the

same. In that case, you can simply leave the dependency on hibernate-entitymanager out of

the plugin#dependencies.

<plugin>

<artifactId>hibernate3-maven-plugin</artifactId>

<groupId>org.codehaus.mojo</groupId>

<version>3.0</version>

<extensions>true</extensions>

<dependencies>

Page 43: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Generating Schema from Class Annotations and NamingStrategy

37

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${legacy-hibernate.version}</version>

</dependency>

</dependencies>

<configuration>

<hibernatetool destdir="target/classes/ddl">

<classpath>

<path location="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/

hibernate-migration/hibernate-migration-docbook/target/classes" />

<path location="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/

hibernate-migration/hibernate-migration-docbook/target/test-classes" />

</classpath>

</hibernatetool>

</configuration>

<executions>

...

</executions>

</plugin>

2. The configuration for the schema generation is defined by a Hibernate Configuration -- which is

represented by the annotationconfiguration element shown below. Note we have also assigned

the namingstrategy for the configuration to our customer strategy.

<execution>

<id>generate-drop-hbm</id>

<phase>process-test-resources</phase>

<goals>

<goal>run</goal>

</goals>

<configuration>

<hibernatetool>

<annotationconfiguration configurationfile="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/

jpa-hibernate/hibernate-migration/hibernate-migration-docbook/target/classes/hibernate.cfg.xml"

namingstrategy="ejava.jpa.hibernatemigration.ProjectNamingStrategy"/>

<hbm2ddl export="false" create="false" drop="true" format="true"

outputfilename="hibernate-migration-docbook-dropNAM.ddl"/>

</hibernatetool>

</configuration>

</execution>

3. We still need to create two executions; one to generate the drops and the other to generate

the creates.

<execution>

<id>generate-create-hbm</id>

<phase>process-test-resources</phase>

<goals>

<goal>run</goal>

</goals>

<configuration>

Page 44: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 3. Adding Metadata th...

38

<hibernatetool>

<annotationconfiguration configurationfile="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/

jpa-hibernate/hibernate-migration/hibernate-migration-docbook/target/classes/hibernate.cfg.xml"

namingstrategy="ejava.jpa.hibernatemigration.ProjectNamingStrategy"/>

<hbm2ddl export="false" create="true" drop="false" format="true"

outputfilename="hibernate-migration-docbook-createNAM.ddl"/>

</hibernatetool>

</configuration>

</execution>

Note

The above shows an example of using a hibernate.cfg.xml file. If you simply

have HBM files and, at most, a hibernate.properties file, you can add references

to those files using

<hibernatetool destdir="${build.dir}/generated">

<configuration propertyfile="target/test-classes/hibernate.properties">

<fileset dir="target/classes/hibernate">

<include name="**/*.hbm.xml"/>

</fileset>

</configuration>

You have finished looking at schema generation for a Hibernate Session that has overrides for

the annotated classes. This represents the last section within this topic.

3.7. Summary

In this chapter we replaced verbose XML mapping files with Java class annotations. This permits

us to place the information and mapping design in a single location. The down-side, however, is

that class annotations cannot be mixed with HBM files within the same class. No fear. We simply

needed to write a small set of classes to manipulate the Hibernate Configuration to supply the

overrides. This wraps up our coverage of incremental steps moving from Hibernate Sessions to

JPA. The next chapter shows the end state where we have fully migrated the application away

from HBM files, to class annotations, and to a JPA EntityManager API.

Page 45: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 4.

39

JPA-based ProjectThis chapter describes describes the final stage of the migration where the classes, descriptors,

and APIs have all been switched to JPA. If we do encounter apsects of the legacy mapping that is

not supported in JPA -- we know from the previous chapters that we can fully leverage the legacy

Hibernate mappings where necessary or desired.

4.1. JPA Project Structure

In this pass we are going to treat the annotated entity classes as an external library and provide

overrides for table names specific to our application. Note too that the Sales class does not provide

any Java annotations.

1. Our application supplies a persistence.xml that describes how this application will use the

persistence unit. For example, some applications use a direct database connection and others

borrow a connection from a javax.sql.DataSource. In this case, our persistence unit is defining

some application-specific overrides for the two of the entities (Clerk and Customer) and

providing the full mapping for the third entity (Sale) because that class does not supply any

metadata.

src

|-- main

| |-- java

| `-- resources

| |-- jpa

| | |-- Clerk-orm.xml

| | |-- Customer-orm.xml

| | `-- Sale-orm.xml

| `-- META-INF

| `-- persistence.xml

`-- test

`-- resources

`-- hibernate.properties

2. The Customer ORM entity definition overrides the table name used in the class annotations

to be something specific to this application. Although not needed for our simple mapping

requirements, we have also switched the schema references to JPA 2.0.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm

http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"

version="2.0">

<entity class="ejava.jpa.hibernatemigration.annotated.Customer">

Page 46: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 4. JPA-based Project

40

<table name="HMIG_CUSTOMER_ORM"/>

</entity>

3. The Clerk ORM provides the same type of table override and to simply things -- we could have

placed the simple overrides for Customer and Clerk into a single ORM file with multiple entity

elements.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm

http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"

version="2.0">

<entity class="ejava.jpa.hibernatemigration.annotated.Clerk">

<table name="HMIG_CLERK_ORM"/>

</entity>

</entity-mappings>

4. The Sale ORM file provides the full mapping to the database because the class does not provide

any mapping annotations and many of the mappings are too complicated for standard JPA

Entity defaults to solve.

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm

http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"

version="2.0">

<package>ejava.jpa.hibernatemigration.annotated</package>

<access>FIELD</access>

<entity class="Sale">

<table name="HMIG_SALE_ORM"/>

<attributes>

<id name="id">

<column length="36"/>

</id>

<basic name="amount" optional="false">

<column precision="7" scale="2"/>

</basic>

<basic name="dateTime" optional="false">

<column name="SALE_TIME"/>

<temporal>TIMESTAMP</temporal>

</basic>

<many-to-one name="customer" optional="false">

<join-column name="CUSTOMER_ID"/>

</many-to-one>

<many-to-many name="salesClerks">

<join-table name="HMIG_SALE_CLERK">

Page 47: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

JPA Project Structure

41

<join-column name="SALE_ID"/>

<inverse-join-column name="CLERK_ID"/>

</join-table>

</many-to-many>

</attributes>

</entity>

</entity-mappings>

5. The persistence unit is defined to use the Hibernate provider (in case there are multiple in the

classpath) and include our three ORM mapping files.

<?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="hibernate-migration-sales">

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<mapping-file>jpa/Customer-orm.xml</mapping-file>

<mapping-file>jpa/Clerk-orm.xml</mapping-file>

<mapping-file>jpa/Sale-orm.xml</mapping-file>

</persistence-unit>

</persistence>

6. The hibernate.properties file has not changed from the original legacy project. We optionally

separate it from the persistence.xml because it defines many runtime properties that may be

specific to our current environment (like database URL and credentials). If we do think that some

of the properties are better associated with the persistence unit (e.g., hibernate.cache.provider)

we can move that to the properties section of the persistence.xml.

hibernate.dialect=org.hibernate.dialect.H2Dialect

hibernate.connection.url=jdbc:h2:/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/

hibernate-migration/hibernate-migration-docbook/target/h2db/ejava

hibernate.connection.driver_class=org.h2.Driver

hibernate.connection.password=

hibernate.connection.username=sa

hibernate.connection.pool_size=1

hibernate.hbm2ddl.auto=create

hibernate.show_sql=true

hibernate.format_sql=true

#hibernate.jdbc.batch_size=0

hibernate.current_session_context_class=thread

hibernate.cache.provider_class=org.hibernate.cache.NoCacheProvider

Page 48: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 4. JPA-based Project

42

4.2. Generating Schema from JPA Persistence Unit

This section will cover database schema generation from a persistence unit. It is very similar to

what we did in the previous section when we used the 3.x version of the hibernate3-maven-plugin

except this time we will use a jpaconfiguration element.

1. We start off with a modest upgrade to our JPA library so that it supports JPA2 constructs.

Versions of Hibernate with JPA2 support have been in place since mid-2010. You should be

able to locate versions newer than what is used here.

<properties>

<!-- hibernate 3.5.0-Final was released to ibiblio April 2010 -->

<jpa2-hibernate.version>3.5.0-Final</jpa2-hibernate.version>

<hibernate-slf4j.version>1.5.8</hibernate-slf4j.version>

</properties>

2. A the project dependency needs to reference the newer hibernate-entitymanager version. Be

sure to also include a dependency on slf4j-log4j12 and make sure it is a compatible version

with the version used by hibernate.

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${jpa2-hibernate.version}</version>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>${hibernate-slf4j.version}</version>

<scope>test</scope>

</dependency>

3. We add a plugin dependency on hibernate-entitymanager to force the plugin to use the same

version of the entitymanager as the application. If the dependency above was scope=compile,

this would not have been necessary. The exclusion for the slf4j-api removes a conflicting

dependency later versions of the entitymanager have with the 3.x version of the hibernate3-

maven-plugin.

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>hibernate3-maven-plugin</artifactId>

<version>3.0</version>

<extensions>true</extensions>

<dependencies>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

Page 49: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Generating Schema from JPA Persistence Unit

43

<version>${jpa2-hibernate.version}</version>

<exclusions>

<exclusion>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

</exclusion>

</exclusions>

</dependency>

</dependencies>

<configuration>

<hibernatetool destdir="target/classes/ddl">

<classpath>

<path location="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/

hibernate-migration/hibernate-migration-docbook/target/classes" />

<path location="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/

hibernate-migration/hibernate-migration-docbook/target/test-classes" />

</classpath>

</hibernatetool>

</configuration>

<executions>

...

</executions>

</plugin>

4. A jpaconfiguration and persistenceunit name is used to locate the persistence unit definition

and define the entities to Hibernate.

<execution>

<id>generate-drop-hbm</id>

<phase>process-test-resources</phase>

<goals>

<goal>run</goal>

</goals>

<configuration>

<hibernatetool>

<jpaconfiguration persistenceunit="hibernate-migration-sales"

propertyfile="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/hibernate-

migration/hibernate-migration-docbook/target/test-classes/hibernate.properties" />

<hbm2ddl export="false" create="false" drop="true" format="true"

outputfilename="hibernate-migration-docbook-dropJPA.ddl"/>

</hibernatetool>

</configuration>

</execution>

5. The create script execution uses the same technique.

<execution>

<id>generate-create-hbm</id>

<phase>process-test-resources</phase>

<goals>

<goal>run</goal>

</goals>

Page 50: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 4. JPA-based Project

44

<configuration>

<hibernatetool>

<jpaconfiguration persistenceunit="hibernate-migration-sales"

propertyfile="/home/jenkins/workspace/javaee-class-deploy/jpa/jpa-providers/jpa-hibernate/hibernate-

migration/hibernate-migration-docbook/target/test-classes/hibernate.properties" />

<hbm2ddl export="false" create="true" drop="false" format="true"

outputfilename="hibernate-migration-docbook-createJPA.ddl"/>

</hibernatetool>

</configuration>

</execution>

6. The following lists the generated DDL from the hibernate3-maven-plugin, our configurations,

and definitions.

create table HMIG_CLERK_ORM (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

HIRE_DATE date not null,

TERM_DATE date,

primary key (id)

);

create table HMIG_CUSTOMER_ORM (

id integer generated by default as identity (start with 1),

name varchar(32) not null,

email varchar(32),

level varchar(8),

primary key (id)

);

create table HMIG_SALE_CLERK (

SALE_ID varchar(36) not null,

CLERK_ID integer not null,

primary key (SALE_ID, CLERK_ID)

);

create table HMIG_SALE_ORM (

id varchar(36) not null,

amount numeric not null,

SALE_TIME timestamp not null,

CUSTOMER_ID integer not null,

primary key (id)

);

alter table HMIG_SALE_CLERK

add constraint FK33F2DF1955121688

foreign key (SALE_ID)

references HMIG_SALE_ORM;

alter table HMIG_SALE_CLERK

add constraint FK33F2DF198F07D2C

foreign key (CLERK_ID)

references HMIG_CLERK_ORM;

Page 51: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Summary

45

alter table HMIG_SALE_ORM

add constraint FK4262BAAEAB739428

foreign key (CUSTOMER_ID)

references HMIG_CUSTOMER_ORM;

You have finished going through the specifics for setting up the hibernate3-maven-plugin to

generate database schema for our JPA module. That wraps up what is needed to cover the final

leg of the migration to the JPA-centric approach.

4.3. Summary

In this chapter we defined use of our application through the JPA interface -- the same interface

that was used during the ORM.xml file section. However, in this case we leveraged a mixture of

Java class annotations and ORM.xml files to define the persistence unit.

Page 52: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

46

Page 53: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 5.

47

Hibernate Migration ErrorsThis chapter describes describes a few of the problems, causes, and solutions found when

migrating from a legacy Hibernate project to a JPA project. Most of the issues involved integration

with hibernate3-maven-plugin.

5.1. Missing hibernate-entitymanager

This error occurs when using the hibernate3-maven-plugin and the project does not declare a

dependency on the hibernate-entitymanager.

5.1.1. Symptom: NoClassDefFoundError:

org.hibernate.cfg.Configuration

[ERROR] Failed to execute goal org.codehaus.mojo:hibernate3-maven-plugin:3.0:run (generate-drop-hbm) on project

hibernate-hbm2:

There was an error creating the AntRun task. An Ant BuildException has occured: java.lang.NoClassDefFoundError:

org/hibernate/cfg/Configuration: org.hibernate.cfg.Configuration

5.1.2. Cause: Missing dependency on hibernate-entitymanager

The error occurs because the plugin has no hibernate artifact to provide it the necessary classes.

That could be due to....

• The dependency on hibernate-entitymanager was either missing or declared scope=test or

provided.

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${legacy-hibernate.version}</version>

<scope>test</scope>

</dependency>

• The plugin did not declare a dependency on hibernate-entitymanager to compensate for the

module not having a scope=compile dependency.

<build>

<plugins>

<plugin>

<artifactId>hibernate3-maven-plugin</artifactId>

<groupId>org.codehaus.mojo</groupId>

<version>3.0</version>

<extensions>true</extensions>

<dependencies>

</dependencies>

...

Page 54: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 5. Hibernate Migratio...

48

5.1.3. Solution: Declare plugin dependency on hibernate-

entitymanager

Either the module or the module plugin must have a dependency on hibernate-entitymanager to

have the missing class(es) be resolved for DDL generation.

<plugin>

<artifactId>hibernate3-maven-plugin</artifactId>

<groupId>org.codehaus.mojo</groupId>

<version>3.0</version>

<extensions>true</extensions>

<dependencies>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${legacy-hibernate.version}</version>

</dependency>

</dependencies>

5.2. Mixed Hibernate Versions with plugin and JPA

This error is caused when there is a version of hibernate in your project that conflicts with the

default version used by the hibernate3-maven-plugin.

5.2.1. Symptom: NoSuchMethodException:

org.hibernate.validator.ClassValidator.<init>(...)

When using the JPA aspects of the plugin you may notice the following.

[INFO] --- hibernate3-maven-plugin:2.2:hbm2ddl (generate-drop-hbm) @ hibernate-orm ---

-Hibernate Annotations 3.4.0.GA

-Hibernate 3.3.1.GA

Failed to execute goal org.codehaus.mojo:hibernate3-maven-plugin:2.2:hbm2ddl (generate-drop-hbm) on project

hibernate-orm:

Execution generate-drop-hbm of goal org.codehaus.mojo:hibernate3-maven-plugin:2.2:hbm2ddl failed:

[PersistenceUnit: hibernate-migration-sales] Unable to configure EntityManagerFactory:

java.lang.NoSuchMethodException: org.hibernate.validator.ClassValidator.<init>(java.lang.Class,

java.util.ResourceBundle, org.hibernate.validator.MessageInterpolator, java.util.Map,

org.hibernate.annotations.common.reflection.ReflectionManager)

5.2.2. Cause: Using JPAConfiguration and version mis-matches

This error is caused by using the JPA configuration of the plugin and not having all dependencies

aligned.

Page 55: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Solution: Declare plugin dependency on hibernate-entitymanager

49

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${legacy-hibernate.version}</version>

<scope>provided</scope>

</dependency>

<plugin>

<artifactId>hibernate3-maven-plugin</artifactId>

<groupId>org.codehaus.mojo</groupId>

<version>${legacy-hibernate3-maven-plugin.version}</version>

<dependencies>

</dependencies>

5.2.3. Solution: Declare plugin dependency on hibernate-

entitymanager

Declare a dependency on hibernate-entitymanager within the plugin to control the version brought

in when using the JPA extentions.

<plugin>

<artifactId>hibernate3-maven-plugin</artifactId>

<groupId>org.codehaus.mojo</groupId>

<version>3.0</version>

<extensions>true</extensions>

<dependencies>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${legacy-hibernate.version}</version>

</dependency>

</dependencies>

5.3. slf4j version mis-matches with later versions of

Hibernate

Issues with hibernate and slf4j start appearing with some of the later versions of hibernate 3.x.

where they switched dependencies from slf4j1.5.8 to 1.6.1.

5.3.1. Symptom: NoClassDefFoundError:

org.slf4j.helpers.NOPLoggerFactory

[ERROR] Failed to execute goal org.codehaus.mojo:hibernate3-maven-plugin:3.0:run (generate-drop-hbm) on project

hibernate-annotated-jpa:

Page 56: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Chapter 5. Hibernate Migratio...

50

There was an error creating the AntRun task. An Ant BuildException has occured: java.lang.NoClassDefFoundError: org/

slf4j/helpers/NOPLoggerFactory:

org.slf4j.helpers.NOPLoggerFactory

5.3.2. Cause: plugin dependency on hibernate-entitymanager

causes slf4j-api version mis-match

In this case we have created a plugin dependency on hibernate-entitymanager for the plugin using

version 3.5.0-Final. This brought in slf4j-api version 1.5.8 which collided with logging done within

the plugin.

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>hibernate3-maven-plugin</artifactId>

<version>3.0</version>

<extensions>true</extensions>

<dependencies>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${jpa2-hibernate.version}</version>

</dependency>

</dependencies>

5.3.3. Solution: Exclude slf4j-api from plugin dependency

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>hibernate3-maven-plugin</artifactId>

<version>3.0</version>

<extensions>true</extensions>

<dependencies>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>${jpa2-hibernate.version}</version>

<exclusions>

<exclusion>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

</exclusion>

</exclusions>

</dependency>

</dependencies>

Page 57: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

Summary

51

5.4. Summary

In this chapter we listed some error messages, described the causes, and provided solutions.

When in doubt, you can also take a look at the source modules that were used to build the

examples provided.

Page 58: Hibernate Migration - webdev home pagewebdev.jhuep.com/.../content/pdf/hibernate-migration.pdf · 2014-03-07 · Chapter 1. Hibernate Legacy P... 2 4. Customer extends Person and

52