Java Persistence/Relationships

From Wikibooks, open books for an open world
Jump to: navigation, search

Contents

Relationships[edit]

A relationship is a reference from one object to another. In Java, relationships are defined through object references (pointers) from a source object to the target object. Technically, in Java there is no difference between a relationship to another object and a "relationship" to a data attribute such as a String or Date (primitives are different), as both are pointers; however, logically and for the sake of persistence, data attributes are considered part of the object, and references to other persistent objects are considered relationships.

In a relational database relationships are defined through foreign keys. The source row contains the primary key of the target row to define the relationship (and sometimes the inverse). A query must be performed to read the target objects of the relationship using the foreign key and primary key information.

In Java, if a relationship is to a collection of other objects, a Collection or array type is used in Java to hold the contents of the relationship. In a relational database, collection relations are either defined by the target objects having a foreign key back to the source object's primary key, or by having an intermediate join table to store the relationship (both objects' primary keys).

All relationships in Java and JPA are unidirectional, in that if a source object references a target object there is no guarantee that the target object also has a relationship to the source object. This is different than a relational database, in which relationships are defined through foreign keys and querying such that the inverse query always exists.

JPA Relationship Types[edit]

  • OneToOne - A unique reference from one object to another, inverse of a OneToOne.
  • ManyToOne - A reference from one object to another, inverse of a OneToMany.
  • OneToMany - A Collection or Map of objects, inverse of a ManyToOne.
  • ManyToMany - A Collection or Map of objects, inverse of a ManyToMany.
  • Embedded - A reference to a object that shares the same table of the parent.
  • ElementCollection - JPA 2.0, a Collection or Map of Basic or Embeddable objects, stored in a separate table.

This covers the majority of types of relationships that exist in most object models. Each type of relationship also covers multiple different implementations, such as OneToMany allowing either a join table, or foreign key in the target, and collection mappings also allow Collection types and Map types. There are also other possible complex relationship types, see Advanced Relationships.

Lazy Fetching[edit]

The cost of retrieving and building an object's relationships far exceeds the cost of selecting the object. This is especially true for relationships such as manager or managedEmployees such that, if any employee were selected, it would trigger the loading of every employee through the relationship hierarchy. Obviously this is a bad thing, and yet having relationships in objects is very desirable.

The solution to this issue is lazy fetching (lazy loading). Lazy fetching allows the fetching of a relationship to be deferred until it is accessed. This is important not only to avoid the database access, but also to avoid the cost of building the objects if they are not needed.

In JPA lazy fetching can be set on any relationship using the fetch attribute. The fetch can be set to either LAZY or EAGER as defined in the FetchType enum. The default fetch type is LAZY for all relationships except for OneToOne and ManyToOne, but in general it is a good idea to make every relationship LAZY. The EAGER default for OneToOne and ManyToOne is for implementation reasons (more difficult to implement), not because it is a good idea. Technically in JPA LAZY is just a hint, and a JPA provider is not required to support it, however in reality all main JPA providers support it, and they would be pretty useless if they did not.

Example of a lazy one to one relationship annotations[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="ADDR_ID")
  private Address address;
  ...
}

Example of a lazy one to one relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-one name="address" fetch="LAZY">
            <join-column name="ADDR_ID"/>
        </one-to-one>
    </attributes>
</entity>

Magic[edit]

Lazy fetching normally involves some sort of magic in the JPA provider to transparently fault in the relationships as they are accessed. The typical magic for collection relationships is for the JPA provider to set the relationships to its own Collection, List, Set or Map implementation. When any (or most) method is accessed on this collection proxy, it loads the real collection and forwards the method. This is why JPA requires that all collection relationships use one of the collection interfaces (although some JPA providers support collection implementations too).

For OneToOne and ManyToOne relationships the magic normally involves some sort of byte code manipulation of the entity class, or creation of a subclass. This allows the access to the field or get/set methods to be intercepted, and for the relationships to be first retrieved before allowing access to the value. Some JPA providers use different methods, such as wrapping the reference in a proxy object, although this can have issues with null values and primitive methods. To perform the byte code magic normally an agent or post-processor is required. Ensure that you correctly use your providers agent or post-processor otherwise lazy may not work. You may also notice additional variables when in a debugger, but in general debugging will still work as normal.

Basics[edit]

A Basic attribute can also be made LAZY, but this is normally a different mechanism than lazy relationships, and should normally be avoided unless the attribute is rarely accessed.

See Basic Attributes : Lazy Fetching.

Serialization, and Detaching[edit]

A major issue with lazy relationships is ensuring that the relationship is still available after the object has been detached or serialized. For most JPA providers, after serialization any lazy relationship that was not instantiated will be broken, and either throw an error when accessed or return null.

The naive solution is to make every relationship eager. Serialization suffers from the same issue as persistence, in that you can very easily serialize your entire database if you have no lazy relationships. So lazy relationships are just as necessary for serialization as they are for database access; however you need to ensure you have everything you will need after serialization instantiated upfront. You may mark only the relationships that you think you will need after serialization as EAGER; this will work, but there are probably many cases when you do not need these relationships.

A second solution is to access any relationship you will need before returning the object for serialization. This has the advantage of being use case specific, so different use cases can instantiate different relationships. For collection relationships sending size() is normally the best way to ensure a lazy relationship is instantiated. For OneToOne and ManyToOne relationships, normally just accessing the relationship is enough (i.e. employee.getAddress()), although for some JPA providers that use proxies you may need to send the object a message (i.e.employee.getAddress().hashCode()).

A third solution is to use the JPQL JOIN FETCH for the relationship when querying the objects. A join fetch will normally ensure the relationship has been instantiated. Some caution should be used with join fetch however, as it can become inefficient if used on collection relationships, especially multiple collection relationships as it requires an n^2 join on the database.

Some JPA providers may also provide certain query hints, or other such serialization options.

The same issue can occur without serialization, if a detached object is accessed after the end of the transaction. Some JPA providers allow lazy relationship access after the end of the transaction, or after the EntityManager has been closed, however some do not. If your JPA provider does not, then you may require that you ensure you have instantiated all the lazy relationships that you will need before ending the transaction.

Eager Join Fetching[edit]

One common misconception is that EAGER means that the relationship should be join fetched, i.e. retrieved in the same SQL SELECT statement as the source object. Some JPA providers do implement eager this way. However, just because something is desired to be loaded does not mean that it should be join fetched. Consider Employee - Phone, a Phone's employee reference is made EAGER as the employee is almost always loaded before the phone. However when loading the phone, you do not want to join the employee, the employee has already been read and is already in the cache or persistence context. Also just because you want two collection relationships loaded, does not mean you want them join fetch which would result in a very inefficient join that would return n^2 data.

Join fetching is something that JPA currently only provides through JPQL, which is normally the correct place for it, as each use case has different relationship requirements. Some JPA providers also provide a join fetch option at the mapping level to always join fetch a relationship, but this is normally not the same thing as EAGER. Join fetching is not normally the most efficient way to load a relationship anyway, normally batch reading a relationship is much more efficient when supported by your JPA provider.

See Join Fetching

See Batch Reading

Cascading[edit]

Relationship mappings have a cascade option that allows the relationship to be cascaded for common operations. cascade is normally used to model dependent relationships, such as Order -> OrderLine. Cascading the orderLines relationship allows for the Order's -> OrderLines to be persisted, removed, merged along with their parent.

The following operations can be cascaded, as defined in the CascadeType enum:

  • PERSIST - Cascaded the EntityManager.persist() operation. If persist() is called on the parent, and the child is also new, it will also be persisted. If it is existing, nothing will occur, although calling persist() on an existing object will still cascade the persist operation to its dependents. If you persist an object, and it is related to a new object, and the relationship does not cascade persist, then an exception will occur. This may require that you first call persist on the related object before relating it to the parent. General it may seem odd, or be desirable to always cascade the persist operation, if a new object is related to another object, then it should probably be persisted. There is most likely not a major issue with always cascading persist on every relationship, although it may have an impact on performance. Calling persist on a related object is not required, on commit any related object whose relationship is cascade persist will automatically be persisted. The advantage of calling persist up front is that any generated ids will (unless using identity) be assigned, and the prePersist event will be raised.
  • REMOVE - Cascaded the EntityManager.remove() operation. If remove() is called on the parent then the child will also be removed. This should only be used for dependent relationships. Note that only the remove() operation is cascaded, if you remove a dependent object from a OneToMany collection it will not be deleted, JPA requires that you explicitly call remove() on it. Some JPA providers may support an option to have objects removed from dependent collection deleted, JPA 2.0 also defines an option for this.
  • MERGE - Cascaded the EntityManager.merge() operation. If merge() is called on the parent, then the child will also be merged. This should normally be used for dependent relationships. Note that this only affects the cascading of the merge, the relationship reference itself will always be merged. This can be a major issue if you use transient variables to limit serialization, you may need to manually merge, or reset transient relationships in this case. Some JPA providers provide additional merge operations.
  • REFRESH - Cascaded the EntityManager.refresh() operation. If refresh() is called on the parent then the child will also be refreshed. This should normally be used for dependent relationships. Be careful enabling this for all relationships, as it could cause changes made to other objects to be reset.
  • ALL - Cascaded all the above operations.

Example of a cascaded one to one relationship annotations[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToOne(cascade={CascadeType.ALL})
  @JoinColumn(name="ADDR_ID")
  private Address address;
  ...
}

Example of a cascaded one to one relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-one name="address">
            <join-column name="ADDR_ID"/>
            <cascade>
                <cascade-all/>
            </cascade>
        </one-to-one>
    </attributes>
</entity>

Orphan Removal (JPA 2.0)[edit]

Cascading of the remove operation only occurs when the remove is called on the object. This is not normally what is desired on a dependent relationship. If the related objects cannot exist without the source, then it is normally desired to have them deleted when the source is deleted, but also have them deleted when they are no longer referenced from the source. JPA 1.0 did not provide an option for this, so when a dependent object was removed from the source relationship, it had to be explicitly removed from the EntityManager. JPA 2.0 provides a orphanRemoval option on the OneToMany and OneToOne annotations and XML. Orphan removal will ensure that any object no longer referenced from the relationship is deleted from the database.

Example of an orphan removal one to many relationship annotations[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany(orphanRemoval=true, cascade={CascadeType.ALL})
  private List<PhoneNumbers> phones;
  ...
}

Example of an orphan removal one to many relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phones" orphan-removal="true">
            <cascade>
                <cascade-all/>
            </cascade>
        </one-to-many>
    </attributes>
</entity>

Target Entity[edit]

Relationship mappings have a targetEntity attribute that allows the reference class (target) of the relationship to be specified. This is normally not required to be set as it is defaulted from the field type, get method return type, or collection's generic type.

This can also be used if your field uses a public interface type, for example field is interface Address, but the mapping needs to be to implementation class AddressImpl. Another usage is if your field is a superclass type, but you want to map the relationship to a subclass.

Example of a target entity relationship annotations[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany(targetEntity=Phone.class)
  @JoinColumn(name="OWNER_ID")
  private List phones;
  ...
}

Example of a target entity relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phones" target-entity="org.acme.Phone">
            <join-column name="OWNER_ID"/>
        </one-to-many>
    </attributes>
</entity>

Collections[edit]

Collection mappings include OneToMany, ManyToMany, and in JPA 2.0 ElementCollection. JPA requires that the type of the collection field or get/set methods be one of the Java collection interfaces, Collection, List, Set, or Map.

Collection Implementations[edit]

Your field should not be of a collection implementation type, such as ArrayList. Some JPA providers may support using collection implementations, many support EAGER collection relationships to use the implementation class. You can set any implementation as the instance value of the collection, but when reading an object from the database, if it is LAZY the JPA provider will normally put in a special LAZY collection.

Duplicates[edit]

A List in Java supports duplicate entries, and a Set does not. In the database, duplicates are generally not supported. Technically it could be possible if a JoinTable is used, but JPA does not require duplicates to be supported, and most providers do not.

If you require duplicate support, you may need to create an object that represents and maps to the join table. This object would still require a unique Id, such as a GeneratedValue. See Mapping a Join Table with Additional Columns.

Ordering[edit]

JPA allows the collection values to be ordered by the database when retrieved. This is done through the @OrderBy annotation or <order-by> XML element.

The value of the OrderBy is a JPQL ORDER BY string. The can be an attribute name followed by ASC or DESC for ascending or descending ordering. You could also use a path or nested attribute, or a "," for multiple attributes. If no OrderBy value is given it is assumed to be the Id of the target object.

The OrderBy value must be a mapped attribute of the target object. If you want to have an ordered List you need to add an index attribute to your target object and an index column to its table. You will also have to ensure you set the index values. JPA 2.0 will have extended support for an ordered List using an OrderColumn.

Note that using an OrderBy does not ensure the collection is ordered in memory. You are responsible for adding to the collection in the correct order. Java does define a SortedSet interface and TreeSet collection implementation that does maintain an order. JPA does not specifically support SortedSet, but some JPA providers may allow you to use a SortedSet or TreeSet for your collection type, and maintain the correct ordering. By default these require your target object to implement the Comparable interface, or set a Comparator. You can also use the Collections.sort() method to sort a List when required. One option to sort in memory is to use property access and in your set and add methods call Collections.sort().

Example of a collection order by annotation[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany
  @OrderBy("areaCode")
  private List<Phone> phones;
  ...
}

Example of a collection order by XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phones">
            <order-by>areaCode</order-by>
        </one-to-many>
    </attributes>
</entity>

Order Column (JPA 2.0)[edit]

JPA 2.0 adds support for an OrderColumn. An OrderColumn can be used to define an order List on any collection mapping. It is defined through the @OrderColumn annotation or <order-column> XML element.

The OrderColumn is maintained by the mapping and should not be an attribute of the target object. The table for the OrderColumn depends on the mapping. For a OneToMany mapping it will be in the target object's table. For a ManyToMany mapping or a OneToMany using a JoinTable it will be in the join table. For an ElementCollection mapping it will be in the target table.

Example of a collection order column database[edit]

EMPLOYEE (table)

ID FIRSTNAME LASTNAME SALARY
1 Bob Way 50000
2 Sarah Smith 60000

EMPLOYEE_PHONE (table)

EMPLOYEE_ID PHONE_ID INDEX
1 1 0
1 3 1
2 2 0
2 4 1

PHONE(table)

ID AREACODE NUMBER
1 613 792-7777
2 416 798-6666
3 613 792-9999
4 416 798-5555

Example of a collection order column annotation[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany
  @OrderColumn(name="INDEX")
  private List<Phone> phones;
  ...
}

Example of a collection order column XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phones">
            <order-column name="INDEX"/>
        </one-to-many>
    </attributes>
</entity>

Common Problems[edit]

Object corruption, one side of the relationship is not updated after updating the other side[edit]

A common problem with bi-directional relationships is the application updates one side of the relationship, but the other side does not get updated, and becomes out of sync. In JPA, as in Java in general, it is the responsibility of the application or the object model to maintain relationships. If your application adds to one side of a relationship, then it must add to the other side.

This is commonly resolved through add or set methods in the object model that handle both sides of the relationships, so the application code does not need to worry about it. There are two ways to go about this: you can either add the relationship maintenance code to only one side of the relationship, and only use the setter from that side (such as making the other side protected), or add it to both sides and ensure you avoid an infinite loop.

For example:

public class Employee {
    private List phones;
    ...
    public void addPhone(Phone phone) {
        this.phones.add(phone);
        if (phone.getOwner() != this) {
            phone.setOwner(this);
        }
    }
    ...
}
 
public class Phone {
    private Employee owner;
    ...
    public void setOwner(Employee employee) {
        this.owner = employee;
        if (!employee.getPhones().contains(this)) {
            employee.getPhones().add(this);
        }
    }
    ...
}

The code is similar for bi-directional OneToOne and ManyToMany relationships.

Some expect the JPA provider to have magic that automatically maintains relationships. This was actually part of the EJB CMP 2 specification. However the issue is if the objects are detached or serialized to another VM, or new objects are related before being managed, or the object model is used outside the scope of JPA, then the magic is gone, and the application is left figuring things out, so in general it may be better to add the code to the object model. However some JPA providers do have support for automatically maintaining relationships.

In some cases it is undesirable to instantiate a large collection when adding a child object. One solution is to not map the bi-directional relationship, and instead query for it as required. Also some JPA providers optimize their lazy collection objects to handle this case, so you can still add to the collection without instantiating it.

Poor performance, excessive queries[edit]

This most common issue leading to poor performance is the usage of EAGER relationships. This requires the related objects to be read when the source objects are read. So for example reading the president of the company with EAGER managedEmployees will cause every Employee in the company to be read. The solution is to always make all relationships LAZY. By default OneToMany and ManyToMany are LAZY but OneToOne and ManyToOne are not, so make sure you configure them to be. See, Lazy Fetching. Sometimes you have LAZY configured but it does not work, see Lazy is not working

Another common problems is the n+1 issue. For example consider that you read all Employee objects then access their Address. Since each Address is accessed separately this will cause n+1 queries, which can be a major performance problem. This can be solved through Join Fetching and Batch Reading.

Lazy is not working[edit]

Lazy OneToOne and ManyToOne relationships typically require some form of weaving or byte-code generation. Normally when running in JSE an agent option is required to allow the byte-code weaving, so ensure you have the agent configured correctly. Some JPA providers perform dynamic subclass generation, so do not require an agent.

Example agent

   java -javaagent:eclipselink.jar ...

Some JPA providers also provide static weaving instead, or in addition to dynamic weaving. For static weaving some preprocessor must be run on your JPA classes.

When running in JEE lazy should normally work, as the class loader hook is required by the EJB specification. However some JEE providers may not support this, so static weaving may be required.

Also ensure that you are not accessing the relationship when you shouldn't be. For example if you use property access, and in your set method access the related lazy value, this will cause it to be loaded. Either remove the set method side-effects, or use field access.

Broken relationships after serialization[edit]

If your relationship is marked as lazy then if it has not been instantiated before the object is serialized, then it may not get serialized. This may cause an error, or return null if it is accessed after deserialization.

See, Serialization, and Detaching

Dependent object removed from OneToMany collection is not deleted[edit]

When you remove an object from a collection, if you also want the object deleted from the database you must call remove() on the object. In JPA 1.0 even if your relationship is cascade REMOVE, you still must call remove(), only the remove of the parent object is cascaded, not removal from the collection.

JPA 2.0 will provide an option for having removes from the collection trigger deletion. Some JPA providers support an option for this in JPA 1.0.

See, Cascading

My relationship target is an interface[edit]

If your relationship field's type is a public interface of your class, and only has a single implementer, then this is simple to solve, you just need to set a targetEntity on your mapping. See, Target Entity.

If your interface has multiple implementers, then this is more complex. JPA does not directly support mapping interfaces. One solution is to convert the interface to an abstract class and use inheritance to map it. You could also keep the interface, create the abstract class and make sure each implementer extends it, and set the targetEntity to be the abstract class.

Another solution is to define virtual attributes using get/set methods for each possible implementer, and map these separately, and mark the interface get/set as transient. You could also not map the attribute, and instead query for it as required.

See, Variable and Heterogeneous Relationships

Some JPA providers have support for interfaces and variable relationships.

TopLink, EclipseLink : Support variable relationships through their @VariableOneToOne annotation and XML. Mapping to and querying interfaces are also supported through their ClassDescriptor's InterfacePolicy API.

Advanced[edit]

Advanced Relationships[edit]

JPA 2.0 Relationship Enhancements[edit]

  • ElementCollection - A Collection or Map of Embeddable or Basic values.
  • Map Columns - A OneToMany or ManyToMany or ElementCollection that has a Basic, Embeddable or Entity key not part of the target object.
  • Order Columns - A OneToMany or ManyToMany or ElementCollection can now have a OrderColumn that defines the order of the collection when a List is used.
  • Unidirectional OneToMany - A OneToMany no longer requires the ManyToOne inverse relationship to be defined.

Other Types of Relationships[edit]

  • Variable OneToOne, ManyToOne - A reference to an interface or common unmapped inheritance class that has multiple distinct implementors.
  • Variable OneToMany, ManyToMany - A Collection or Map of heterogeneous objects that share an interface or common unmapped inheritance class that has multiple distinct implementers.
  • Nested collection relationships, such as an array of arrays, List of Lists, or Map of Maps, or other such combinations.
  • Object-Relational Data Type - Relationships stored in the database using STRUCT, VARRAY, REF, or NESTEDTABLE types.
  • XML relationships - Relationships stored as XML documents.

Maps[edit]

Java defines the Map interface to represent collections whose values are indexed on a key. There are several Map implementations, the most common is HashMap, but also Hashtable and TreeMap.

JPA allows a Map to be used for any collection mapping including, OneToMany, ManyToMany and ElementCollection. JPA requires that the Map interface be used as the attribute type, although some JPA providers may also support using Map implementations.

In JPA 1.0 the map key must be a mapped attribute of the collection values. The @MapKey annotation or <map-key> XML element is used to define a map relationship. If the MapKey is not specified it defaults to the target object's Id.

Example of a map key relationship annotation[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany(mappedBy="owner")
  @MapKey(name="type")
  private Map<String, PhoneNumber> phoneNumbers;
  ...
}
 
@Entity
public class PhoneNumber {
  @Id
  private long id;
  @Basic
  private String type;  // Either "home", "work", or "fax".
  ...
  @ManyToOne
  private Employee owner;
  ...
}

Example of a map key relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phoneNumbers" mapped-by="owner">
            <map-key name="type"/>
        </one-to-many>
    </attributes>
</entity>
<entity name="PhoneNumber" class="org.acme.PhoneNumber" access="FIELD">
    <attributes>
        <id name="id"/>
        <basic name="type"/>
        <many-to-one name="owner"/>
    </attributes>
</entity>

Map Key Columns (JPA 2.0)[edit]

JPA 2.0 allows for a Map where the key is not part of the target object to be persisted. The Map key can be any of the following:

  • A Basic value, stored in the target's table or join table.
  • An Embedded object, stored in the target's table or join table.
  • A foreign key to another Entity, stored in the target's table or join table.

Map columns can be used for any collection mapping including, OneToMany, ManyToMany and ElementCollection.

This allows for great flexibility and complexity in the number of different models that can be mapped. The type of mapping used is always determined by the value of the Map, not the key. So if the key is a Basic but the value is an Entity a OneToMany mapping is still used. But if the value is a Basic but the key is an Entity a ElementCollection mapping is used.

This allows some very sophisticated database schemas to be mapped. Such as a three way join table, can be mapped using a ManyToMany with a MapKeyJoinColumn for the third foreign key. For a ManyToMany the key is always stored in the JoinTable. For a OneToMany it is stored in the JoinTable if defined, otherwise it is stored in the target Entity's table, even though the target Entity does not map this column. For an ElementCollection the key is stored in the element's table.

The @MapKeyColumn annotation or <map-key-column> XML element is used to define a map relationship where the key is a Basic value, the @MapKeyEnumerated and @MapKeyTemporal can also be used with this for Enum or Calendar types. The @MapKeyJoinColumn annotation or <map-key-join-column> XML element is used to define a map relationship where the key is an Entity value, the @MapKeyJoinColumns can also be used with this for composite foreign keys. The annotation @MapKeyClass or <map-key-class> XML element can be used when the key is an Embeddable or to specify the target class or type if generics are not used.

Example of a map key column relationship database[edit]

EMPLOYEE (table)

ID FIRSTNAME LASTNAME SALARY
1 Bob Way 50000
2 Sarah Smith 60000

PHONE(table)

ID OWNER_ID PHONE_TYPE AREACODE NUMBER
1 1 home 613 792-7777
2 1 cell 613 798-6666
3 2 home 416 792-9999
4 2 fax 416 798-5555

Example of a map key column relationship annotation[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany(mappedBy="owner")
  @MapKeyColumn(name="PHONE_TYPE")
  private Map<String, Phone> phones;
  ...
}
 
@Entity
public class Phone {
  @Id
  private long id;
  ...
  @ManyToOne
  private Employee owner;
  ...
}

Example of a map key column relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phones" mapped-by="owner">
            <map-key-column name="PHONE_TYPE"/>
        </one-to-many>
    </attributes>
</entity>
<entity name="Phone" class="org.acme.Phone" access="FIELD">
    <attributes>
        <id name="id"/>
        <many-to-one name="owner"/>
    </attributes>
</entity>

Example of a map key join column relationship database[edit]

EMPLOYEE (table)

ID FIRSTNAME LASTNAME SALARY
1 Bob Way 50000
2 Sarah Smith 60000

PHONE(table)

ID OWNER_ID PHONE_TYPE_ID AREACODE NUMBER
1 1 1 613 792-7777
2 1 2 613 798-6666
3 2 1 416 792-9999
4 2 3 416 798-5555

PHONETYPE(table)

ID TYPE
1 home
2 cell
3 fax
4 work

Example of a map key join column relationship annotation[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany(mappedBy="owner")
  @MapKeyJoinColumn(name="PHONE_TYPE_ID")
  private Map<PhoneType, Phone> phones;
  ...
}
 
@Entity
public class Phone {
  @Id
  private long id;
  ...
  @ManyToOne
  private Employee owner;
  ...
}
 
@Entity
public class PhoneType {
  @Id
  private long id;
  ...
  @Basic
  private String type;
  ...
}

Example of a map key join column relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phones" mapped-by="owner">
            <map-key-join-column name="PHONE_TYPE_ID"/>
        </one-to-many>
    </attributes>
</entity>
<entity name="Phone" class="org.acme.Phone" access="FIELD">
    <attributes>
        <id name="id"/>
        <many-to-one name="owner"/>
    </attributes>
</entity>
<entity name="PhoneType" class="org.acme.PhoneType" access="FIELD">
    <attributes>
        <id name="id"/>
        <basic name="type"/>
    </attributes>
</entity>


Example of a map key class embedded relationship database[edit]

EMPLOYEE (table)

ID FIRSTNAME LASTNAME SALARY
1 Bob Way 50000
2 Sarah Smith 60000

EMPLOYEE_PHONE (table)

EMPLOYEE_ID PHONE_ID TYPE
1 1 home
1 2 cell
2 3 home
2 4 fax

PHONE (table)

ID AREACODE NUMBER
1 613 792-7777
2 613 798-6666
3 416 792-9999
4 416 798-5555

Example of a map key class embedded relationship annotation[edit]

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany
  @MapKeyClass(PhoneType.class)
  private Map<PhoneType, Phone> phones;
  ...
}
 
@Entity
public class Phone {
  @Id
  private long id;
  ...
}
 
@Embeddable
public class PhoneType {
  @Basic
  private String type;
  ...
}

Example of a map key class embedded relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-many name="phones">
            <map-key-class>PhoneType</map-key-class>
        </one-to-many>
    </attributes>
</entity>
<entity name="Phone" class="org.acme.Phone" access="FIELD">
    <attributes>
        <id name="id"/>
        <many-to-one name="owner"/>
    </attributes>
</entity>
<embeddable name="PhoneType" class="org.acme.PhoneType" access="FIELD">
    <attributes>
        <basic name="type"/>
    </attributes>
</embeddable>

Join Fetching[edit]

Join fetching is a query optimization technique for reading multiple objects in a single database query. It involves joining the two object's tables in SQL and selecting both object's data. Join fetching is commonly used for OneToOne relationships, but also can be used for any relationship including OneToMany and ManyToMany.

Join fetching is one solution to the classic ORM n+1 performance problem. The issue is if you select n Employee objects, and access each of their addresses, in basic ORM (including JPA) you will get 1 database select for the Employee objects, and then n database selects, one for each Address object. Join fetching solves this issue by only requiring one select, and selecting both the Employee and its Address.

JPA supports join fetching through JPQL using the JOIN FETCH syntax.

Example of JPQL Join Fetch[edit]

SELECT emp FROM Employee emp JOIN FETCH emp.address

This causes both the Employee and Address data to be selected in a single query.

Outer Joins[edit]

Using the JPQL JOIN FETCH syntax a normal INNER join is performed. This has the side effect of filtering any Employee from the result set that did not have an address. An OUTER join in SQL is one that does not filter absent rows on the join, but instead joins a row of all null values. If your relationship allows null or an empty collection for collection relationships, then you can use an OUTER join fetch, this is done in JPQL using the LEFT syntax.

Note that OUTER joins can be less efficient on some databases, so avoid using an OUTER if it is not required.

Example of JPQL Outer Join Fetch[edit]

SELECT emp FROM Employee emp LEFT JOIN FETCH emp.address

Mapping Level Join Fetch and EAGER[edit]

JPA has no way to specify that a join fetch always be used for a relationship. Normally it is better to specify the join fetch at the query level, as some use cases may require the related objects, and other use cases may not. JPA does support an EAGER option on mappings, but this means that the relationship will be loaded, not that it will be joined. It may be desirable to mark all relationships as EAGER as everything is desired to be loaded, but join fetching everything in one huge select could result in a inefficient, overly complex, or invalid join on the database.

Some JPA providers do interpret EAGER as join fetch, so this may work on some JPA providers. Some JPA providers support a separate option for always join fetching a relationship.

TopLink, EclipseLink : Support a @JoinFetch annotation and XML on a mapping to define that the relationship always be join fetched.

Nested Joins[edit]

JPA 1.0 does not allow nested join fetches in JPQL, although this may be supported by some JPA providers. You can join fetch multiple relationships, but not nested relationships.

Example of Multiple JPQL Join Fetch[edit]

SELECT emp FROM Employee emp LEFT JOIN FETCH emp.address LEFT JOIN FETCH emp.phoneNumbers

Duplicate Data and Huge Joins[edit]

One issue with join fetching is that duplicate data can be returned. For example consider join fetching an Employee's phoneNumbers relationship. If each Employee has 3 Phone objects in its phoneNumbers collection, the join will require to bring back n*3 rows. As there are 3 phone rows for each employee row, the employee row will be duplicated 3 times. So you are reading more data than if you have selected the objects in n+1 queries. Normally the fact that your executing fewer queries makes up for the fact that you may be reading duplicate data, but if you consider joining multiple collection relationships you can start to get back j*i duplicate data which can start to become an issue. Even with ManyToOne relationships you can be selecting duplicate data. Consider join fetching an Employee's manager: if all or most employee's have the same manager, you will end up selecting this manager's data many times. In this case you would be better off not using join fetch, and allowing a single query for the manager.

If you start join fetching every relationship, you can start to get some pretty huge joins. This can sometimes be an issue for the database, especially with huge outer joins.

One alternative solution to join fetch that does not suffer from duplicate data is using Batch Fetching.

Batch Fetching[edit]

Batch fetching is a query optimization technique for reading multiple related objects in a finite set of database queries. It involves executing the query for the root objects as normal. But for the related objects the original query is joined with the query for the related objects, allowing all of the related objects to be read in a single database query. Batch fetching can be used for any type of relationship.

Batch fetching is one solution to the classic ORM n+1 performance problem. The issue is if you select n Employee objects, and access each of their addresses, in basic ORM (including JPA) you will get 1 database select for the Employee objects, and then n database selects, one for each Address object. Batch fetching solves this issue by requiring one select for the Employee objects and one select for the Address objects.

Batch fetching is more optimal for reading collection relationships and multiple relationships as it does not require selecting duplicate data as in join fetching.

JPA does not support batch reading, but some JPA providers do.

TopLink, EclipseLink : Support a @BatchFetch annotation and xml element and a "eclipselink.batch" query hint to enable batch reading. Three forms of batch fetching are supported, JOIN, EXISTS, and IN.

See also,

Filtering, Complex Joins[edit]

Normally a relationship is based on a foreign key in the database, but on occasion it is always based on other conditions. Such as Employee having many PhoneNumbers but also a single home phone, or one of his phones that has the "home" type, or a collection of "active" projects, or other such condition.

JPA does not support mapping these types of relationships, as it only supports mappings defined by foreign keys, not based on other columns, constant values, or functions. Some JPA providers may support this. Workarounds include, mapping the foreign key part of the relationship, then filtering the results in your get/set methods of your object. You could also query for the results, instead of defining a relationship.

TopLink, EclipseLink : Support filtering and complex relationships through several mechanisms. You can use a DescriptorCustomizer to define a selectionCriteria on any mapping using the Expression criteria API. This allows for any condition to be applied including constants, functions, or complex joins. You can also use a DescriptorCustomizer to define the SQL or define a StoredProcedureCall for the mapping's selectionQuery.

Variable and Heterogeneous Relationships[edit]

It is sometimes desirable to define a relationship where the type of the relationship can be one of several unrelated, heterogeneous values. This could either be a OneToOne, ManyToOne, OneToMany or ManyToMany relationship. The related values may share a common interface, or may share nothing in common other than subclassing Object. It is also possible to conceive of a relationship that could also be any Basic value, or even an Embeddable.

In general JPA does not support variable, interface, or heterogeneous relationships. JPA does support relationships to inheritance classes, so the easiest workaround is normally to define a common superclass for the related values.

Another solution is to define virtual attributes using get/set methods for each possible implementer, and map these separately, and mark the heterogeneous get/set as transient. You could also not map the attribute, and instead query for it as required.

For heterogeneous Basic or Embeddable relationships one solution is to serialize the value to a binary field. You could also convert the value to a String representation that you can restore the value from, or store the value to two columns, one storing the String value and the other the class name or type.

Some JPA providers have support for interfaces, variable relationships, and/or heterogeneous relationships.

TopLink, EclipseLink : Support variable relationships through their @VariableOneToOne annotation and XML. Mapping to and querying interfaces are also supported through their ClassDescriptor's InterfacePolicy API.

Nested Collections, Maps and Matrices[edit]

It is somewhat common in an object model to have complex collection relationships such as a List of Lists (i.e. a matrix), or a Map of Maps, or a Map of Lists, and so on. Unfortunately these types of collections map very poorly to a relational database.

JPA does not support nested collection relationships, and normally it is best to change your object model to avoid them to make persistence and querying easier. One solution is to create an object that wraps the nested collection.

For example if an Employee had a Map of Projects keyed by a String project-type and the value a List or Projects. To map this a new ProjectType class could be created to store the project-type and a OneToMany to Project.

Example nested collection model (original)[edit]

public class Employee {
  private long id;
  private Map<String, List<Project>> projects;
}

Example nested collection model (modified)[edit]

public class Employee {
  @Id
  @GeneratedValue
  private long id;
  ...
  @OneToMany(mappedBy="employee")
  @MapKey(name="type")
  private Map<String, ProjectType> projects;
}
public class ProjectType {
  @Id
  @GeneratedValue
  private long id;
  @ManyToOne
  private Employee employee;
  @Column(name="PROJ_TYPE")
  private String type;
  @ManyToMany
  private List<Project> projects;
}

Example nested collection database[edit]

EMPLOYEE (table)

ID FIRSTNAME LASTNAME SALARY
1 Bob Way 50000
2 Sarah Smith 60000

PROJECTTYPE (table)

ID EMPLOYEE_ID PROJ_TYPE
1 1 small
2 1 medium
3 2 large

PROJECTTYPE_PROJECT (table)

PROJECTTYPE_ID PROJECT_ID
1 1
1 2
2 3
3 4