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.
JPA provides the following features:
- 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 (Structured Query Language) queries as well as the JPQL (Java Persistence Query Language)
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"/>
</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"/>
<property name="openjpa.Log" value="slf4j"/>
<property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true, PrettyPrintLineLength=120, PrintParameters=true"/>
<property name="openjpa.jdbc.EagerFetchMode" value="parallel"/>
<property name="openjpa.jdbc.SubclassFetchMode" value="parallel"/>
<property name="openjpa.DetachState" value="loaded(DetachedStateField=true,AccessUnloaded=false)"/>
<property name="openjpa.Multithreaded" value="true"/>
<property name="openjpa.AutoDetach" value="close, commit, nontx-read"/>
<property name="openjpa.jdbc.DBDictionary" value="batchLimit=100, SupportsSubselect=true, SupportsCorrelatedSubselect=false"/>
<property name="openjpa.jdbc.TransactionIsolation" value="read-committed"/>
<!-- Uncomment the section below to enable data caching, comment them out to disable -->
<property name="openjpa.DataCache" value="true"/>
<property name="openjpa.RemoteCommitProvider" value="sjvm"/>
<property name="openjpa.QueryCompilationCache" value="true(cacheSize=5000,softReferenceSize=0)"/>
<property name="openjpa.jdbc.FinderCache" value="false"/>
<property name="openjpa.QueryCache" value="false"/>
<property name="openjpa.jdbc.QuerySQLCache" value="false"/>
<!-- OpenJPA 2.x compatibility against spec -->
<property name="openjpa.Compatibility"
value="convertPositionalParametersToNamed=true,ignoreDetachedStateFieldForProxySerialization=true"/>
<!-- Pre-Load Metadata -->
<property name="openjpa.MetaDataRepository" value="Preload=true"/>
</properties>
The following list gives a brief summary of OpenJPA properties and their purposes:
ConnectionFactoryName
Tells OpenJPA the JNDI 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
totrue
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 (Java Virtual Machine).
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
. TheFinderCache
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
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.
Read the full documentation
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:
@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:
@OneToOne
When an entity
A
references a single entityB
, and no otherA
's can reference the sameB
, there is a one to one relationship betweenA
andB
.@OneToMany
When an entity
A
references multipleB
entities, and no twoA
's reference the sameB
, there is a one to many relationship fromA
toB
.@ManyToOne
When an entity
A
references a single entityB
, and otherA
's might also reference the sameB
, there is a many to one relationship fromA
toB
.@ManyToMany
When an entity
A
references multipleB
entities, and otherA
's might reference some of the sameB
's, there is a many to many relationship betweenA
andB
.@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:
@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
orQuery
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:
SINGLE_TABLE
Maps all classes in the hierarchy to the base class’ table.
JOINED
Uses a different table for each class in the hierarchy. Each table only includes state declared in its class. Thus to load a subclass instance, the JPA implementation must read from the subclass table as well as the table of each ancestor class, up to the base entity class.
TABLE_PER_CLASS
Uses a different table for each class in the hierarchy. Unlike the
JOINED
strategy, however, each table includes all state for an instance of the corresponding class. To load a subclass instance, the JPA implementation must only read from the subclass table; it does not need to join to superclass tables
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:
@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 thatopenjpa.DataCache = true
in thejpa-persistence.xml
file of the entity
Example Annotations
@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 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. Similar to Hibernate’s Session
interface, this interface provides services for CRUD (Create Read Update Delete) operations, creating query instances, and interfacing to the transaction API. This interface includes operations, such as persist, remove, find, or merge.
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
.
JpaPersistenceEngineImpl
Implements Core Interface: 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
Implements Core Interface: 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 its control.
This class uses the EntityManager
methods directly.
JpaQueryImpl
Implements Core Interface: Query
This is used by code such as the Import Manager which uses the Query interface to manipulate queries within its own transaction.
This class uses the javax.persistence.Query
methods directly.
JpaTransactionImpl
Implements Core Interface: Transaction
This is used by code such as the Import Manager which uses the Transaction interface to control its 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.
The following example shows annotated getters for the 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;
}
JPA uses the following settings to convert datatypes:
@Externalizer
: Specifies the externalizer method to use to convert the field values into the data type required by the database. This method only uses the object that is being serialized.@Factory
: Specifies the fully qualified class and method to use to convert the data type of the values retrieved from the database. For this method, another class and a method to deserialize the data must be specified.
For more information, see the Reference Guide section in Apache OpenJPA User’s Guide.
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 log4j2.xml
files:
<Logger name="openjpa.jdbc.SQL" level="TRACE" additivity="false">
${ep.log.to}
</Logger>
<Logger name="openjpa.jdbc.Runtime" level="TRACE" additivity="false">
${ep.log.to}
</Logger>
<Logger name="openjpa.jdbc.MetaData" level="TRACE" additivity="false">
${ep.log.to}
</Logger>
<Logger name="openjpa.jdbc.Enhance" level="TRACE" additivity="false">
${ep.log.to}
</Logger>
<Logger name="openjpa.jdbc.Query" level="TRACE" additivity="false">
${ep.log.to}
</Logger>
These values should be removed or set to ${ep.log.level}
for distribution.
Extensibility
Elastic Path includes classes to assist with extending JPA Persistence configuration:
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’sjpa-persistence.xml
, see Add references to the Persistence UnitOverridingPersistenceUnitPostProcessor
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