Java Persistence API (JPA)
Java Persistence API (JPA)
Elastic Path uses the Apache OpenJPA implementation of the Java Persistence API (JPA) to store and retrieve information from its databases.
- Supports a POJO (Plain Old Java Object) persistence model.
- Rich inheritance support.
- Support for annotations or XML based mapping files.
- Supports pluggable persistence providers.
- Is supported by Spring.
- Supports native SQL queries as well as the Java Persistence Query Language (JPQL).
Entities and Identities
Entity Characteristics
A JPA Entity is a class which represents persistent data stored in a database.
In most cases a concrete or an abstract class can be an Entity. Entity classes support mixed inheritance: the superclass and/or subclass of an Entity does not have to be an Entity.
Any application-defined object with the following characteristics can be an Entity:
- It can be made persistent,
- It has a persistent identity (i.e. a unique key),
- It is not a primitive, a primitive wrapper of build-in object.
Entity Identification
All Entities must have a persistent id (i.e. a database primary key). This is known as the entity identity (or persistence identity). JPA supports the following entity identities:
- Single field identity (like our long uidPk field).
- Composite primary keys (provided by an Identity class),
The Entity identity must be defined on the root entity or mapped superclass of the hierarchy. Elastic Path's domain model defines the uidPk on the Persistable interface. Persistable classes need to implement the getUidPk() method with the @Id annotation and setUidPk() method.
JPA supports several strategies for primary key generation:
- Auto - leave the decision up to the JPA implementation.
- Identity - the database will assign an identity value on insert.
- Sequence - use a datastore sequence to generate a value.
- Table - use a sequence table to generate a field value.
Elastic Path's domain model uses the Table generator. Table works with all databases, and offers the best overall performance. Elastic Path's persistence classes defines the generator table JPA_GENERATED_KEYS with the columns ID and LAST_VALUE, a name and primary key value matching the name of the database table that the class will be persisted to. JPA uses this table to keep track of the last identifier value used to ensure the next one used is unique. You can configure the number of values to allocate in memory for each trip to the database (the default is 50). Allocating values in memory allows the JPA runtime to avoid accessing the database for every sequence request.
The annotations on the getUidPk() method are as follows:
@Id @Column(name = "UIDPK") @GeneratedValue(strategy = GenerationType.TABLE, generator = TABLE_NAME) @TableGenerator(name = TABLE_NAME, table = "JPA_GENERATED_KEYS", pkColumnName = "ID", valueColumnName = "LAST_VALUE", pkColumnValue = TABLE_NAME)
Packaging
JPA requires one XML file, META-INF/jpa-persistence.xml, which does the following:
- Defines the name of the persistence unit,
- Defines the transaction strategy,
- Identifies the entities contained within a persistence unit,
- Defines persistence provider configuration.
It can also define the location of XML mapping files.
Below is a sample META-INF/jpa-persistence.xml file. It does the following:
- Defines a persistence unit named "myopenjpa",
- Identifies the provider as OpenJPA (org.apache.openjpa.persistence.PersistenceProviderImpl),
- Lists the classes contained in the persistence unit,
- Sets the connection factory to java:comp/env/jdbc/epjndi.
<?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="myopenjpa"> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> <class>com.elasticpath.domain.catalog.impl.AbstractPriceImpl</class> <class>com.elasticpath.domain.catalog.impl.BrandImpl</class> <class>com.elasticpath.domain.catalog.impl.CategoryImpl</class> <class>com.elasticpath.domain.catalog.impl.CategoryTypeImpl</class> <class>com.elasticpath.domain.catalog.impl.InventoryImpl</class> <class>com.elasticpath.domain.catalog.impl.ProductAssociationImpl</class> <class>com.elasticpath.domain.catalog.impl.ProductCategoryImpl</class> <class>com.elasticpath.domain.catalog.impl.ProductImpl</class> <class>com.elasticpath.domain.catalog.impl.ProductSkuImpl</class> <class>com.elasticpath.domain.catalog.impl.ProductTypeImpl</class> ... <properties> <property name="openjpa.ConnectionFactoryName" value="java:comp/env/jdbc/epjndi"/commerce-legacy/> </properties> </persistence-unit> </persistence>
OpenJPA Properties
In the <properties> element of jpa-persistence.xml, you can set a number of different properties. By default, Elastic Path sets the following:
core/ep-core/src/main/resources/META-INF/jpa-persistence.xml <properties> <property name="openjpa.DataCacheManager" value="ehcache" /> <property name="openjpa.ConnectionFactoryName" value="java:comp/env/jdbc/epjndi"/commerce-legacy/> <property name="openjpa.Log" value="slf4j"/commerce-legacy/> <property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true, PrettyPrintLineLength=120, PrintParameters=true"/commerce-legacy/> <property name="openjpa.jdbc.EagerFetchMode" value="parallel"/commerce-legacy/> <property name="openjpa.jdbc.SubclassFetchMode" value="parallel"/commerce-legacy/> <property name="openjpa.DetachState" value="loaded(DetachedStateField=true,AccessUnloaded=false)"/commerce-legacy/> <property name="openjpa.Multithreaded" value="true"/commerce-legacy/> <property name="openjpa.AutoDetach" value="close, commit, nontx-read"/commerce-legacy/> <property name="openjpa.jdbc.DBDictionary" value="batchLimit=100, SupportsSubselect=true, SupportsCorrelatedSubselect=false"/commerce-legacy/> <property name="openjpa.jdbc.TransactionIsolation" value="read-committed"/commerce-legacy/> <!-- Uncomment the section below to enable data caching, comment them out to disable --> <property name="openjpa.DataCache" value="true"/commerce-legacy/> <property name="openjpa.RemoteCommitProvider" value="sjvm"/commerce-legacy/> <property name="openjpa.QueryCompilationCache" value="true(cacheSize=5000,softReferenceSize=0)"/commerce-legacy/> <property name="openjpa.jdbc.FinderCache" value="false"/commerce-legacy/> <property name="openjpa.QueryCache" value="false"/commerce-legacy/> <property name="openjpa.jdbc.QuerySQLCache" value="false"/commerce-legacy/> <!-- OpenJPA 2.x compatibility against spec --> <property name="openjpa.Compatibility" value="convertPositionalParametersToNamed=true,ignoreDetachedStateFieldForProxySerialization=true"/commerce-legacy/> <!-- Pre-Load Metadata --> <property name="openjpa.MetaDataRepository" value="Preload=true"/commerce-legacy/> </properties>
The following table gives a brief summary of OpenJPA properties and their purposes:
Property | Description |
---|---|
ConnectionFactoryName | Tells OpenJPA the JDNI location of the Datasource file. |
Log |
Tells OpenJPA which logging library to use for issuing log messages. |
ConnectionFactoryProperties |
This is used to format the log output of SQL debug messages to a more readable form. |
EagerFetchMode |
Set to "parallel", the most efficient eager fetch mode for Elastic Path's domain model. |
SubclassFetchMode | Set to "parallel" , the most efficient eager fetch mode for Elastic Path's domain model. |
DetachState | Tells OpenJPA to take advantage of a detached state field to make the attach process more efficient. This field is added by the enhancer and is not visible to your application. The loaded value tells OpenJPA to detach all fields and relations that are already loaded, but not to include unloaded fields in the detached graph. Setting DetachedStateField to true tells OpenJPA to use a non-transient detached state field so that objects crossing serialization barriers can still be attached efficiently. This requires, however, that your client tier have the enhanced versions of your classes and the OpenJPA libraries. |
Multithreaded |
Tells OpenJPA that persistent instances and OpenJPA components other than the EntityManagerFactory will be accessed by multiple threads at once. |
AutoDetatch |
Tells OpenJPA to detach entities on transaction commits, instances are read non-transactionally, or when the OpenJPA interface closes. |
DBDictionary | Provides additional database parameters. The batchLimit parameter groups multiple insert and delete operations into a single SQL query to optimize performance. The SupportsSubselect and SupportsCorrelatedSubselect parameters instruct OpenJPA to support subselects in queries, but not correlated subselects. |
TransactionIsolation | Ensures the database transaction isolation level is set to READ_COMMITTED. |
DataCache | Enables OpenJPA data caching. |
DataCacheManager | Tells OpenJPA to use Ehcache as its data cache provider. |
RemoteCommitProvider | Sets OpenJPA to use a single JVM. |
QueryCompilationCache | Tells OpenJPA to cache parsed query strings. As a result, most queries are only parsed once in OpenJPA, and cached thereafter. |
FinderCache | Tells OpenJPA not to use the FinderCache. The FinderCache currently has a bug which causes data corruption issues and must be disabled. |
QueryCache | Tells OpenJPA to cache the object ids returned by query executions. |
QuerySQLCache | Set to false so that OpenJPA does not cache the SQL queries it generates. |
Compatibility | Tells OpenJPA to convert positional parameters to named parameters to support mixed queries, as well as to use OpenJPA 1 behaviour for Detached state serialization. |
MetaDataRepository | Tells OpenJPA to preload the metadata repository as a runtime performance optimization. |
Class Enhancement
OpenJPA uses an enhancer to provide optimal runtime performance, flexible lazy loading, and efficient immediate dirty tracking. The enhancer post-processes the bytecode generated by your Java compiler for your domain classes, adding the necessary fields and methods to implement the required persistence features. This bytecode modification perfectly preserves the line numbers in stack traces and is compatible with Java debuggers.
Annotations
Overview
Most annotations are placed directly into the class files of the objects to be persisted. This makes it easy to maintain consistency between the objects and their persistence definition: there is no need to go find the "mapping file". Annotations are generally quicker to type than XML mapping definitions and IDEs support annotations including syntax checking and auto-completion. The exception to the rule is annotations that define named queries (see below).
Annotations can be at the field level, where the persistence layer writes directly to the field, or at the property level, where the persistence layer uses the getter and setter methods. You cannot mix field and property level annotations within the same class hierarchy.
As a standard, our object model uses property level annotations. The annotations in the class files are written above the getter method for each property.
The OpenJPA User's Guide contains comprehensive documentation on all of the annotations available along with what parameters they take. You will need to refer to this often when doing your own mappings.
Simple annotations
Here's a list of some of the more common simple annotations:
Annotation | Purpose |
---|---|
@Entity |
Denotes an entity class. |
@Table(name = "tablename") |
Denotes that the entity should persist to the tablename table in the schema. |
@Id |
Denotes a simple identity field. |
@Basic |
Denotes a simple value that should be persisted as-is. |
@Column(name = "columnname") |
Denotes that the value should be persisted to the columnname column in the schema. |
@Temporal |
Defines how to use Date fields at the JDBC level. |
@Enumerated |
Controls how Enum fields are mapped. |
@Transient |
Specifies that a field is non-persistent. |
Note that properties in a class defined as an Entity that do not have any annotations may still be treated as persistable depending on the type. To avoid any confusion, ensure you annotate all property getters including @Basic and @Transient properties.
Relationship management
The following lists of some of the annotations commonly used for mapping relationships in JPA:
Annotation | Purpose |
---|---|
@OneToOne |
When an entity A references a single entity B, and no other A's can reference the same B, there is a one to one relationship between A and B . |
@OneToMany |
When an entity A references multiple B entities, and no two A's reference the same B, there is a one to many relationship from A to B . |
@ManyToOne |
When an entity A references a single entity B, and other A's might also reference the same B, there is a many to one relationship from A to B. |
@ManyToMany |
When an entity A references multiple B entities, and other A's might reference some of the same B's, there is a many to many relationship between A and B . |
@Embedded |
Embedded fields are mapped as part of the datastore record of the declaring entity. A class can be marked as embeddable by adding the @Embeddable annotation to the class. |
Inheritance
JPA provides two annotations for supporting inheritance:
Annotation | Purpose |
---|---|
@MappedSuperclass |
A non-entity class that can define persistent state and mapping information for entity subclasses. Mapped superclasses are usually abstract. Unlike true entities, you cannot query a mapped superclass, pass a mapped superclass instance to any EntityManager or Query methods, or declare a persistent relation with a mapped superclass target. |
@Inheritance |
Indicates the inheritance the strategy for a hierarchy of entities. There are 3 strategies to chose from:
|
OpenJPA-specific annotations
OpenJPA provides some useful annotations in addition to those provided by the JPA specification. Many of these are used by Elastic Path due to the complexity of the domain object model. Commonly used OpenJPA-specific are as follows:
Annotation | Purpose |
---|---|
@ForeignKey |
Defines a foreign key. This annotation is present when there is a database foreign key, and allows OpenJPA to calculate the correct order to issue database statements without violating key constraints. |
@Dependent |
Marks a direct relation as dependent. This means the referenced object is deleted whenever the owning object is deleted, or whenever the relation is severed by nulling or resetting the owning field. |
@ElementJoinColumn |
Array, collection, or map element join column. |
@ElementForeignKey |
Defines a foreign key constraint to the columns of a collection element. |
@ElementDependent |
Marks the entity elements of a collection, array, or map field as dependent. This means the referenced object is deleted whenever the owning object is deleted, or whenever the relation is severed by nulling or resetting the owning field. |
@Factory |
Contains the name of a method that will be invoked to instantiate the field from the external form stored in the database. |
@Externalizer |
Sets the name of a method that will be invoked to convert the field into its external form for database storage. |
@DataCache | If @DataCache(enabled = false) is present, then the entity will not be cached. All other combinations will enable caching, assuming that openjpa.DataCache = true in the jpa-persistence.xml file of the entity. |
Example
Below is an example of an annotated class file which shows many of the annotation types in use:
@Entity @Table(name = "TPRODUCT") public class ProductImpl extends AbstractEntityImpl implements Product { ... @Basic @Column(name = "START_DATE") public Date getStartDate() { return this.startDate; } @Version @Column(name = "LAST_MODIFIED_DATE") @Temporal(TemporalType.TIMESTAMP) public Date getLastModifiedDate() { return lastModifiedDate; } @ManyToOne(targetEntity = ProductTypeImpl.class) @JoinColumn(name = "PRODUCT_TYPE_UID") public ProductType getProductType() { return this.productType; } @Transient public ProductSku getDefaultSku() { if (defaultSku == null && productSkus != null && productSkus.size() > 0) { return (ProductSku) productSkus.values().iterator().next(); } return defaultSku; } @OneToMany(targetEntity = ProductPriceImpl.class, cascade = { CascadeType.PERSIST, CascadeType.REMOVE }) @MapKey(name = "currencyCode") @ElementJoinColumn(name = "PRODUCT_UID") public Map getProductPrices() { return productPrices; } ... }
Java Persistence Query Language (JPQL)
JPQL overview
JPQL executes over the abstract persistence schema (the entities you've created) defined by your persistence unit, rather than over the database like traditional SQL. JPQL supports a SQL-like syntax, and dynamic and static (named) queries.
Below is an example of a JPQL query:
SELECT [PD:<result>] [PD:FROM <candidate-class(es)>] [PD:WHERE <filter>] [PD:GROUP BY <grouping>] [PD:HAVING <having>] [PD:ORDER BY <ordering>]
Query basics
Below is a simple query for all CategoryTypeImpl entities:
SELECT ct FROM CategoryTypeImpl ct
The optional where clause places criteria on matching results, for example:
SELECT j FROM ImportJobImpl j WHERE j.name = '01-SnapItUp'
You can also specify parameters, for example:
SELECT c FROM CategoryImpl c WHERE c.code = ?1
Fetch joins
JPQL queries may specify one or more join fetch declarations, which allow the query to specify which fields in the returned instances will be pre-fetched
SELECT ct FROM CategoryTypeImpl ct JOIN FETCH ct.categoryAttributeGroupAttributes
Multiple fields may be specified in separate join fetch declarations. You may want to use other join types depending on the data, e.g. left outer join fetch.
Named queries
JPA supports named queries. While named queries can be defined in the annotations of a class – usually the class in which the named query is most relevant – they can also be defined external to the implementation class source files in XML files. In this case, each Java package has its own named queries XML file in the following location: META-INF/ <packagename>-orm.xml.. This is useful for keeping all the named queries specific to a package in the same place while allowing easier extensibility, so that queries can be modified without requiring the application to be recompiled.
Here is an example of named queries in an XML file:
<?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>com.elasticpath.domain.customer.impl</package> <entity class="CustomerDeletedImpl"> <named-query name="CUSTOMER_UIDS_SELECT_BY_DELETED_DATE"> <query>select pd.customerUid from CustomerDeletedImpl as pd where pd.deletedDate >= ?1</query> </named-query> </entity> <entity class="CustomerGroupImpl"> <named-query name="CUSTOMERGROUP_SELECT_ALL"> <query>select cg from CustomerGroupImpl cg</query> </named-query> <named-query name="CUSTOMERGROUP_FIND_BY_NAME"> <query>select cg from CustomerGroupImpl cg where cg.name = ?1</query> </named-query> </entity> </entity-mappings>
Persistence coding
Entity Manager
One of the key interfaces in the Java Persistence API is the EntityManager. This is similar to Hibernate's Session interface, providing services for CRUD operations (persist, remove, find, merge etc), creating Query instances and interfacing to the Transaction API.
Spring integration
Spring provides JPA integration, implementing JPA Container responsibilities. It includes beans like LocalEntityManagerFactoryBean which make it easy to configure the Entity Manager Factory through Spring configuration files, and SharedEntityManagerBean which provides a shared EntityManager reference.
Core Engine Persistence model integration
Elastic Path integrates JPA with its core persistence model. In general, Elastic Path uses Spring Inversion of Control and Dependency Injection when possible.
Elastic Path provides the following classes for persistence. You can find these in the com.elasticpath.persistence.openjpa.impl package of ep-persistence-openjpa.
Class | Implements core interface | Purpose | How it works |
---|---|---|---|
JpaPersistenceEngineImpl | PersistenceEngine | The main persistence engine class. This is what you would set as the persistenceEngine property in your Spring configuration files for the service beans. | This class uses a shared EntityManager provided by the Spring configuration. |
JpaSessionImpl | PersistenceSession | This is used by code such as the Import Manager which uses the PersistenceSession interface to run queries on the session in a transaction under it's control. | This class uses the EntityManager methods directly. |
JpaQueryImpl | Query | This is used by code such as the Import Manager which uses the Query interface to manipulate queries within it's own transaction. | This class uses the javax.persistence.Query methods directly. |
JpaTransactionImpl | Transaction | This is used by code such as the Import Manager which uses the Transaction interface control it's own transactions. | This class uses the EntityTransaction methods directly. |
The majority of the time you'll just use the persistence-api PersistenceEngine interface, and configure Spring to inject the JPA implementation, so your code generally doesn't need to know anything about JPA.
Eager vs. Lazy loading
You can define fields to load lazily or eagerly the annotations. Be aware that using eager loading for everything can seriously affect performance and is unnecessary in cases when you don't need every linked object.
Lazy loading will defer loading of a field's associated entity until it is accessed, but it must be accessed while the connection is still open. Trying to access that field after the connection is closed will throw a IllegalStateException.
Best practice is to define all (non-basic) fields as lazy loading (the default for @OneToMany and @ManyToMany mappings) and then define named queries with fetch join to eagerly load the relation fields. When you need just the basic data you use a query without the fetch join and when you need everything you use the fetch join query.
For example, the following named query should be used when you want basic Category information:
@NamedQuery(name = "CATEGORY_SELECT_BY_GUID", query = "SELECT c FROM CategoryImpl c WHERE c.code = ?1")
Whereas when you require all locale dependent fields and attributes, use the following:
@NamedQuery(name = "CATEGORY_SELECT_BY_GUID_EAGER", query = "SELECT c FROM CategoryImpl c " + "LEFT OUTER JOIN FETCH c.localeDependantFieldsMap " + "LEFT OUTER JOIN FETCH c.attributeValueMap " + "WHERE c.code = ?1")
Fetch Groups
OpenJPA provides the concept of Fetch Groups - sets of fields that load together. They can be used to pool together associated fields in order to provide performance improvements over standard data fetching. Specifying fetch groups allows for tuning of lazy loading and eager fetching behavior. Fetch groups are used in our system for objects that are indexed by our Search Server to define exactly what fields need to be loaded by the indexing jobs.
Problems and Workarounds
JPA is designed to be simple and easy to use, so it is less complex than Hibernate. Sometimes this means that mappings that were possible in Hibernate are not possible to do in JPA without some modifications to the object model.
Locale and Currency
JPA does not support the Locale and Currency types so some special OpenJPA annotations are required when using these datatypes.
For example, here are annotated getters for Locale and Currency fields:
@Persistent(optional = false) @Externalizer("toString") @Factory("org.apache.commons.lang.LocaleUtils.toLocale") @Column(name = "LOCALE", length = 20) public Locale getLocale() { return locale; } @Persistent @Column(name = "CURRENCY", length = 3) @Externalizer("getCurrencyCode") @Factory("com.elasticpath.commons.util.impl.UtilityImpl.currencyFromString") public Currency getCurrency() { return currency; }
This tells JPA the externalizer method to use to convert the field into its external form for database storage (eg getCurrencyCode()) for Currency), and to the full class and method to instantiate the field from the external form stored in the database.
Extensible Enums
Like Locale and Currency, extensible enums use the Externalizer and Factory annotations for conversion:
/** * Get the status of the order. * * @return the order status */ @Override @Persistent(optional = false) @Column(name = "STATUS") @Externalizer("getName") @Factory("valueOf") public OrderStatus getStatus() { return status; }
Constructors
JPA requires all entities to have a default constructor which it will call when the persistence layer starts up. If there is no default constructor the build-time enhancer will add one.
Be careful if your constructor expects other objects to be previously instantiated. For example:
public ShoppingCartImpl() { super(); this.shippingServiceLevelList = new ArrayList(); this.viewHistory = (ViewHistory) getElasticPath().getBean("viewHistory"); }
The above is an old snippet from ShoppingCartImpl which has been re-implemented as a JPA Entity. Before this class was annotated as a JPA Entity, the constructor wasn't being called until the ElasticPathImpl bean factory added a bean for ShoppingCartImpl, so the call to getElasticPath() would always work. However, once this class was added to the list of persistence entities, the constructor was being called before ElasticPathImpl was being instantiated, so the call to getElasticPath() would fail.
The work around is to put any code in the constructor that relies on other objects in a more appropriate place. In this particular case the code to get the viewHistory bean was moved into the getViewHistory method of ShoppingCartImpl.
Useful tools
The OpenJPA Maven plugin includes some useful tools that can help with using JPA.
Mapping Tool
The mapping tool can generate a schema from your object model. It is very useful for checking if your annotations map the object(s) to the current schema, or for generating the SQL if you add a new object.
You can run the tool by calling the following Maven command in the ep-core directory:
mvn -Dopenjpa.ConnectionDriverName=com.mysql.jdbc.Driver openjpa:sql
The generated file, database.sql, can be found in ep-core/target.
Logging
Detailed logging for JPA can be enabled by adding the following to your log4j.properties files:
log4j.category.openjpa.jdbc.SQL=TRACE log4j.category.openjpa.Runtime=TRACE log4j.category.openjpa.MetaData=TRACE log4j.category.openjpa.Enhance=TRACE log4j.category.openjpa.Query=TRACE
These values should be removed or set to ${ep_log_level} for distribution.
Required libraries
The following libraries, which can be found in your local repository (by default, ~/.m2/repository), are required for use by JPA:
GroupId | ArtifactId | Version | Purpose |
---|---|---|---|
org.apache.openjpa |
openjpa |
2.4.0-ep2.0 |
Main Apache OpenJPA API. |
org.apache.geronimo.specs |
geronimo-jta_1.1_spec |
1.1 |
JTA specifications API. |
org.apache.geronimo.specs |
geronimo-jms_1.1_spec |
1.0.1 |
JMS specifications API. |
org.apache.geronimo.specs |
geronimo-jpa_2.0_spec |
1.1 |
JPA specifications API. |
net.sourceforge.serp |
serp |
1.14.1 |
Framework for manipulating Java bytecode. |
commons-collections |
commons-collections |
3.2.2 |
Newer release of the commons collections API. |
commons-pool |
commons-pool |
1.6 |
Object pooling components. |
Extensibility
- MergingPersistenceUnitPostProcessor - Allows multiple jpa-persistence.xml files defining the same persistence unit name to exist in your project's classpath. The list of mapping files and managed classes are merged at runtime. For details on adding references to an extension project's jpa-persistence.xml, see Add references to the Persistence Unit
- OverridingPersistenceUnitPostProcessor - Allows an extension project to list mapping files and managed classes that should be excluded from the persistence unit, as well as adding and/or overriding JPA properties. For more details on these overrides, see Exclude references from the Persistence Unit and Override Persistence Unit Properties.