Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Examples/JPA/Migration/OpenJPA/Mappings"

< EclipseLink‎ | Examples‎ | JPA‎ | Migration‎ | OpenJPA
m
m
Line 37: Line 37:
 
@TableGenerator(name = "OPENJPA_SEQUENCE_TABLE", table = "OPENJPA_SEQUENCE_TABLE", pkColumnName = "ID", valueColumnName = "SEQUENCE_VALUE", pkColumnValue = "0")  <br>
 
@TableGenerator(name = "OPENJPA_SEQUENCE_TABLE", table = "OPENJPA_SEQUENCE_TABLE", pkColumnName = "ID", valueColumnName = "SEQUENCE_VALUE", pkColumnValue = "0")  <br>
 
@GeneratedValue(strategy = GenerationType.TABLE, generator = "OPENJPA_SEQUENCE_TABLE")
 
@GeneratedValue(strategy = GenerationType.TABLE, generator = "OPENJPA_SEQUENCE_TABLE")
 +
 +
=== @javax.persistence.OrderColumn on Sets ===
 +
 +
An entity class with @javax.persistence.OrderColumn and @javax.persistence.ElementCollection annotations on a Set of objects field type will cause an error in EclipseLink. This is due to the fact that the Set interface does not guarantee order therefore, it is not a valid type to use with an order column specification.
 +
 +
==== Solution ====
 +
 +
There are two solutions depending on the importance of retaining the order of the objects. If order is important, change the type of the field from a Set of objects to a List of objects. If order is not important, remove the @javax.persistence.OrderColumn annotation.
 +
 +
=== @javax.persistence.ElementCollection ===
 +
 +
An entity class with an @ElementCollection annotation on a field results in a separate table being created for the field. This table contains at least two columns, one that stores the element’s id and another that stores its value. In OpenJPA, the column that stores the element’s value is named ‘ELEMENT’ by default while EclipseLink names it by the field name.
 +
 +
==== Solution ====
 +
 +
Any place that has an @ElementCollection annotation, if there is no @Column annotation with a name attribute specified, add the following annotation:
 +
@Column(name="ELEMENT") annotation.
 +
 +
=== @javax.persistence.ElementCollection in @javax.persistence.Embeddable ===
 +
 +
In OpenJPA, an embeddable class with an @ElementCollection field maps to a collection table whose name consists of the the embeddable class name concatenated with the name of the collection attribute, separated by an underscore. However, EclipseLink concatenates the collection attribute name with the entity containing the embeddable instead of the embeddable itself. Similarly, the ID field column in the collection table is set differently.
 +
 +
==== Solution ====
 +
 +
When an embeddable class contains a field annotated with @ElementCollection, add an @CollectionTable annotation with the name attribute set to the embeddable class name concatenated with the field name, separated with an underscore. Also, add a joinColumns attributes set to an @JoinColumn with the name attribute set to the embeddable class concatenated with “ID”.
 +
Note: if not present, add the @Column(name="ELEMENT") mentioned in the section above.
 +
<br>Example:
 +
{|{{BMTableStyle}}
 +
|-{{BMTHStyle}}
 +
! Before
 +
! After
 +
|-
 +
|
 +
<source lang="java">
 +
@Embeddable
 +
public class EmbeddableA {
 +
@ElementCollection
 +
private List<String> listOfStrings = new ArrayList<String>();
 +
}
 +
 +
 +
</source>
 +
||
 +
<source lang="java">
 +
@Embeddable
 +
public class EmbeddableA {
 +
@ElementCollection
 +
@CollectionTable(name="EMBEDDABLEA_LISTOFSTRINGS",
 +
    joinColumns=@JoinColumn(name="EMBEDDABLEA_ID"))
 +
@Column(name="ELEMENT")
 +
private List<String> listOfStrings = new ArrayList<String>();
 +
}
 +
</source>
 +
|}
 +
 +
=== Unannotated Collection Fields ===
 +
 +
OpenJPA ignores unannotated fields of type java.util.Collection or any of it’s subinterfaces; (BeanContext, BeanContextServices, BlockingDeque<E>, BlockingQueue<E>, Deque<E>, List<E>, NavigableSet<E>, Queue<E>, Set<E>, SortedSet<E>, TransferQueue<E>). Since these types are not a default persistent type, they are not persisted by OpenJPA. However, EclipseLink persists these fields and adds the corresponding columns to the database tables.
 +
 +
==== Solution ====
 +
 +
If the fields were intended to be ignored by the JPA provider, continue ignoring these fields by adding an @javax.persistence.Transient annotation on these fields.
 +
 +
=== Private Accessor Methods ===
 +
 +
According to the JPA 2.1 spec, when property access is used, the property accessor methods must be public or protected. Accordingly, OpenJPA ignores any private accessor methods. However, EclipseLink persists the fields.
 +
 +
==== Solution ====
 +
 +
If the fields were intended to be ignored by the JPA provider, continue ignoring these fields by adding an @javax.persistence.Transient annotation on the accessor method.
 +
 +
=== Getter/Setter Accessor Methods ===
 +
 +
When using property access, a getter and setter method must be defined for a field to be persisted. In OpenJPA, any fields that only have a getter method with no setter method are ignored even if they are annotated. However, if EclipseLink finds a getter method annotated without a corresponding setter method, an error is thrown to the user instead of simply ignoring it.
 +
 +
==== Solution ====
 +
 +
If the fields were intended to be ignored by the JPA provider, continue ignoring these fields by removing any annotations on the getter accessor method.
 +
 +
=== Long Table/Column Names ===
 +
 +
Table and column names of long length (more than 128 characters) are truncated in OpenJPA and inserted into the database. EclipseLink doesn’t persist any entities that have long table names or long column names.
 +
 +
==== Solution ====
 +
 +
Since OpenJPA already truncates the table/column names when creating the database table, the entity just needs to reflect the information in the database. Therefore, the solution is to set the table/column names in the entity object to the names in the existing database table created by OpenJPA.

Revision as of 12:56, 11 November 2014

OpenJPA to EclipseLink JPA Migration: Mappings

The following are some mapping scenarios encountered when migrating from OpenJPA to EclipseLink. If your scenario is not captured here please file a enhancement request referencing this page that describes the mapping related migration issue you are facing.

Mapping Assistance with Dali

In general it is recommended to use a JPA mapping tool such as Eclipse Dali (WTP) to map the entities. It is built into Eclipse 3.4 and higher within WTP and allows excellent validation and configuration assistance to address many common mapping errors.

@Column in Relationship Mappings

When defining relationship mappings involving Foriegn Keys (@OneToOne and @ManyToOne) JPA supports the specification of non-default column names using @JoinColumn and @JoinColumns. OpenJPA allows the specification of @Column. Since this has no defined meaning under JPA EclipseLink throws a validation exception to assist customers in identifying this incorrect mapping configuration.

Solution

Convert all @OneToOne and @ManyToOne mappings that have an @Column configuration to use @JoinColumn. If the target class of the relationship has a composite identifier (primary key) then @JoinColumns will be required.

Note: If the relationship defined by the @JoinColumn(s) involves columns which are also mapped as identifiers (primary key columns) then additional care will be required to ensure these are mapped with @Id and the duplicate mappings are properly mapped and managed in the entity class.

@Transient with Relationship Mappings

It is undefined in the JPA specification to define a mapping (@Basic, @OneToOne, @ManyToOne, @OneToMany, @ManyToMany, ...) and transient. While OpenJPA allows this, EclipseLink correctly throws and exception indicating the conflict in the configuration. Transient configuration on an attribute is intended to prevent default mappings from being applied when calculating the mappings for an entity. With default mappings unmapped attributes are assumed according to the specification and @Transient prevents these assumptions.

Solution

Determine which configuration is wanted. If the mapping wanted then remove @Transient. If the intention was to avoid the default mapping then either remove or comment out the mapping configuration.

@GeneratedValue/@GeneratedValue(‘generator=AUTO’)

When a primary key is annotated by @GeneratedValue with ‘generator=AUTO’ specified or no attributes specified, OpenJPA and EclipseLink create different tables to generate values for the primary keys. If there are existing entities with primary keys generated by OpenJPA, persisting new entities using EclipseLink will cause an error since EclipseLink will be looking for its own table to generate these ids.

Solution

EclipseLink can add new entities with existing ones by configuring the entities to use OpenJPA’s table to generate the values of their primary keys. By doing so, EclipseLink will use the last value listed in the OpenJPA table to generate any new keys.
This solution is implemented by replacing the @GeneratedValue or @GeneratedValue(generator=AUTO) with the following annotations:
@TableGenerator(name = "OPENJPA_SEQUENCE_TABLE", table = "OPENJPA_SEQUENCE_TABLE", pkColumnName = "ID", valueColumnName = "SEQUENCE_VALUE", pkColumnValue = "0")
@GeneratedValue(strategy = GenerationType.TABLE, generator = "OPENJPA_SEQUENCE_TABLE")

@javax.persistence.OrderColumn on Sets

An entity class with @javax.persistence.OrderColumn and @javax.persistence.ElementCollection annotations on a Set of objects field type will cause an error in EclipseLink. This is due to the fact that the Set interface does not guarantee order therefore, it is not a valid type to use with an order column specification.

Solution

There are two solutions depending on the importance of retaining the order of the objects. If order is important, change the type of the field from a Set of objects to a List of objects. If order is not important, remove the @javax.persistence.OrderColumn annotation.

@javax.persistence.ElementCollection

An entity class with an @ElementCollection annotation on a field results in a separate table being created for the field. This table contains at least two columns, one that stores the element’s id and another that stores its value. In OpenJPA, the column that stores the element’s value is named ‘ELEMENT’ by default while EclipseLink names it by the field name.

Solution

Any place that has an @ElementCollection annotation, if there is no @Column annotation with a name attribute specified, add the following annotation: @Column(name="ELEMENT") annotation.

@javax.persistence.ElementCollection in @javax.persistence.Embeddable

In OpenJPA, an embeddable class with an @ElementCollection field maps to a collection table whose name consists of the the embeddable class name concatenated with the name of the collection attribute, separated by an underscore. However, EclipseLink concatenates the collection attribute name with the entity containing the embeddable instead of the embeddable itself. Similarly, the ID field column in the collection table is set differently.

Solution

When an embeddable class contains a field annotated with @ElementCollection, add an @CollectionTable annotation with the name attribute set to the embeddable class name concatenated with the field name, separated with an underscore. Also, add a joinColumns attributes set to an @JoinColumn with the name attribute set to the embeddable class concatenated with “ID”. Note: if not present, add the @Column(name="ELEMENT") mentioned in the section above.
Example:

Before After
@Embeddable
public class EmbeddableA {
	@ElementCollection
	private List<String> listOfStrings = new ArrayList<String>();
}
@Embeddable
public class EmbeddableA {
	@ElementCollection
	@CollectionTable(name="EMBEDDABLEA_LISTOFSTRINGS",
     joinColumns=@JoinColumn(name="EMBEDDABLEA_ID"))
	@Column(name="ELEMENT")
	private List<String> listOfStrings = new ArrayList<String>();
}

Unannotated Collection Fields

OpenJPA ignores unannotated fields of type java.util.Collection or any of it’s subinterfaces; (BeanContext, BeanContextServices, BlockingDeque<E>, BlockingQueue<E>, Deque<E>, List<E>, NavigableSet<E>, Queue<E>, Set<E>, SortedSet<E>, TransferQueue<E>). Since these types are not a default persistent type, they are not persisted by OpenJPA. However, EclipseLink persists these fields and adds the corresponding columns to the database tables.

Solution

If the fields were intended to be ignored by the JPA provider, continue ignoring these fields by adding an @javax.persistence.Transient annotation on these fields.

Private Accessor Methods

According to the JPA 2.1 spec, when property access is used, the property accessor methods must be public or protected. Accordingly, OpenJPA ignores any private accessor methods. However, EclipseLink persists the fields.

Solution

If the fields were intended to be ignored by the JPA provider, continue ignoring these fields by adding an @javax.persistence.Transient annotation on the accessor method.

Getter/Setter Accessor Methods

When using property access, a getter and setter method must be defined for a field to be persisted. In OpenJPA, any fields that only have a getter method with no setter method are ignored even if they are annotated. However, if EclipseLink finds a getter method annotated without a corresponding setter method, an error is thrown to the user instead of simply ignoring it.

Solution

If the fields were intended to be ignored by the JPA provider, continue ignoring these fields by removing any annotations on the getter accessor method.

Long Table/Column Names

Table and column names of long length (more than 128 characters) are truncated in OpenJPA and inserted into the database. EclipseLink doesn’t persist any entities that have long table names or long column names.

Solution

Since OpenJPA already truncates the table/column names when creating the database table, the entity just needs to reflect the information in the database. Therefore, the solution is to set the table/column names in the entity object to the names in the existing database table created by OpenJPA.

Back to the top