Skip to content
jbprek edited this page May 24, 2015 · 1 revision

JPA Relationships and operations on entities

##Standard Relationships

##Mappings

##Collection Mapping

##Standard Relationships

The following describe the basic entity relationships, see more notes inside each project.

Key terms : Single Value Association, Collection Value Association.

###1. One to One unidirectional, see project rel_one2one_uni

Relationship of Employee and ParkingLot:

    @Entity
    public class Employee {
        @Id private long id;        
        @OneToOne
        //@JoinColumn(name = "PARKING_ID")
        private ParkingLotEntity phones;
        ...

    @Entity
    public class ParkingLotEntity {
        @Id private long id;
        ...

Note: It's possible for two Employess to refer to the same ParkingLot

###2. One to One bidirectional , see project rel_one2one_bi

Relationship of Employee and ParkingLot again.  Parking differs from above.

@Entity
@Table(name="PARKING_LOT")
public class ParkingLot{    
    @Id private long id;
    
    /* If mappedBy is removed then foreign keys are created on both tables */
    @OneToOne(mappedBy = "phones")
    private Employee employee;

###3. Many to One , see project rel_many2one

Relationship of Employee and Department.

@Entity
public class Employee {    
    @Id private long id;
    ...    
    @ManyToOne
    @JoinColumn(name = "DEPT_ID")
    
    
    @Entity
    public class Department {
        @Id private long id;

###4. One to Many unidirectional, see project rel_one2many_uni

Relationship of Person and Phones

###5. One to Many bidirectional , see project rel_one2many_bi

Relationship of Employee and Department again.

    @Entity
    public class Department {        
        @Id  private long id;
        ....
        @OneToMany(mappedBy="department")
        private List<Employee> employeesByAssignment = new ArrayList<>();

    @Entity
    public class Employee {        
        @Id private long id;
        ....
        /* OWNER SIDE*/
        @ManyToOne
        @JoinColumn(name = "DEPT_ID")
        private Department department;

###6. Many to Many unidirectional, see project rel_many2many_uni

 RelationShip of Tasks and Employees.  (Task is owner).

###7. Many to Many bidirectional , see project rel_many2many_bi

Relationship of Employee and Project.

    @Entity
    public class Employee {
        @Id  private long id;
        ...
        @ManyToMany
        @JoinTable(name = "EMPLOYEE_PROJECT", 
            joinColumns = @JoinColumn(name="EMPLOYEE_ID"), 
            inverseJoinColumns = @JoinColumn(name="PROJECT_ID"))
        private Set<Project> projects;
        
        
    @Entity
    public class Project {
        @Id  private long id;
        ...
        @ManyToMany(mappedBy = "projects")
        private Set<Employee> employeesByAssignment;

##Mappings

Embeddable use, project embed_example

Demonstration of @Embeddable

    @Embeddable
    @Access(AccessType.FIELD)
    public class Address implements Serializable {
        private String street;
        private String number;
        private String town;
        private String country;
        @Column(name="ZIP_CODE")
        private String zip;
        
    @Entity
    public class Employee implements Serializable {
        @Id  private long id;
        @Embedded
        @AttributeOverrides({
                @AttributeOverride(name= "country", column = @Column(name="CNTRY")),
                @AttributeOverride(name= "zip", column = @Column(name="ZIP")),
        })
        private Address address; 
        ....

Compound PK @IdClass , adv_map_cpk_idclass

    // IdClass - No Setters
    public class EmployeeId implements Serializable {
        private long code;
        private String county;
        
    @Entity
    @IdClass(value = EmployeeId.class)
    public class Employee implements Serializable {    
        // Same fields as IdClass
        @Id
        private long code;
        @Id
        private String county;
        ...    

Compound PK @EmbeddedId, adv_map_cpk_embedid

    @Embeddable
    public class EmployeeId implements Serializable {
        private long code;
        private String county;

    @Entity
    public class Employee implements Serializable {    
        @EmbeddedId
        private EmployeeId id;

###0. Introduction

####Rules for Maps

  • Use the @MapKeyClass and targetEntity/targetClass elements of the relationship and element collection mappings to specify the classes when an untyped Map is used.
  • Use @MapKey with one-to-many or many-to-many relationship Map that is keyed on an attribute of the target entity.
  • Use @MapKeyJoinColumn to override the join column of the entity key.
  • Use @Column to override the column storing the values of an element collection of basic types.
  • Use @MapKeyColumn to override the column storing the keys when keyed by a basic type.
  • Use @MapKeyTemporal and @MapKeyEnumerated if you need to further qualify a basic key that is a temporal or enumerated type.
  • Use @AttributeOverride with a “key.” or “value.” prefix to override the column of an embeddable attribute type that is a Map key or a value, respectively.

####Summary of Mapping a Map

Map                         Mapping                 Key Annotation              Value Annotation

Map<Basic,Basic>            @ElementCollection      @MapKeyColumn,              @Column
                                                    @MayKeyEnumerated,
                                                    @MapKeyTemporal

Map<Basic,Embeddable>       @ElementCollection      @MapKeyColumn,              Mapped by embeddable,
                                                    @MayKeyEnumerated,          @AttributeOverride,
                                                    @MapKeyTemporal             @AssociationOverride

Map<Basic,Entity>           @OneToMany,             @MapKey,                    Mapped by entity
                            @ManyToMany             @MapKeyColumn,
                                                    @MayKeyEnumerated,
                                                    @MapKeyTemporal

Map<Embeddable,Basic>       @ElementCollection      Mapped by embeddable,       @Column
                                                    @AttributeOverride

Map<Embeddable,Embeddable>  @ElementCollection      Mapped by embeddable,       Mapped by embeddable,
                                                    @AttributeOverride          @AttributeOverride,
                                                                                @AssociationOverride

Map<Embeddable,Entity>      @OneToMany,            Mapped by embeddable         Mapped by entity
                            @ManyToMany            @AttributeOverride

Map<Entity,Basic>           @ElementCollection      @MapKeyJoinColumn           @Column


Map<Entity,Embeddable>      @ElementCollection      @MapKeyJoinColumn           Mapped by embeddable,
                                                                                @AttributeOverride,
  
Map<Entity,Entity>          @OneToMany,             @MapKeyJoinColumn           Mapped by entity
                            @ManyToMany

###1. @ElementCollection Example, see project cm_element_collection

Collection Mapping of Employee to NickName(s) and VacationEntry(ies). 

    @Embeddable
    public class VacationEntry {
        @Temporal(TemporalType.DATE)
        private Date startDate;
        private int duration;
        
    @Entity
    public class Employee {
        @Id private long id;
           
        private String name;
    
        @ElementCollection
        @CollectionTable(name="CM_EC_EMP_VACATION",
                         joinColumns = @JoinColumn(name="EMP_ID"))
        @AttributeOverrides({ .. })
        private List<VacationEntry> vacationEntries  = new ArrayList<>();
    
        @ElementCollection
        @CollectionTable(name = "CM_EC_EMP_NNAMES", joinColumns = @JoinColumn(name = "EMP_ID"))
        private List<String> nickNames = new ArrayList<>();

###2. List Ordering @OrderBy and @OrderColumn , see project cm_list

- Use of @OrderBy in Employees-Department relationship, Unresolved **problem**: cannot obtain sorted list of department employess.
- Example of @OrderColumn  with PrintQueue PrintJob, same  **problem** cannot get them in proper order

###3. Map mapping keyed by basic type, String in our case, see project cm_map_key_basic - Key is String under package jpa.relationship.mapuse.stringkey

    Person with phones mapped by category
     
    @Entity
    public class PersonStringPhoneType {        
        @Id private int id;
        ....
        @ElementCollection
        @CollectionTable(name = "PERSON_PHONE")
        @MapKeyColumn(name = "PHONE_TYPE")
        @Column(name = "PHONE_NUM")
        private Map<String, String> phoneNumbers = new HashMap<>();

###4. Map mapping keyed by enum, see project cm_map_key_enum

- Key is Emum under package jpa.relationship.mapuse.emumkey

 Same as above using an Enum instead of a String.
    
    public enum PhoneType { HOME, MOBILE }
     
    @Entity
    public class PersonEnumPhoneType {        
        @Id  private int id;
        ....
        @ElementCollection
        @CollectionTable(name = "EMP_PHONE")
        @MapKeyEnumerated(EnumType.STRING)
        @Column(name = "PHONE_NUM")
        private Map<PhoneType, String> phoneNumbers = new HashMap<>();

###5. Map used in One to Many relationship, see project cm_map_one2many

    @Entity
    public class Employee {        
        @Id
        private long id;
        ...        
        @ManyToOne
        @JoinColumn(name = "DEPT_ID")
        private Department department;
        
        
    @Entity
    public class Department {        
        @Id  private long id;
        ...
        @OneToMany(mappedBy="department")
        @MapKeyColumn(name="CUB_ID", nullable = true)
        private Map<String, Employee> employeesByCubicle;

##6. Map used in Many to Many relationship, see project cm_map_many2many

    @Entity
    public class Employee {        
        @Id private long id;
        ...
        @ManyToMany
        @JoinTable(name = "CM_MMB_EMPLOYEE_PROJECT",
                joinColumns = @JoinColumn(name="EMPLOYEE_ID"),
                inverseJoinColumns = @JoinColumn(name="PROJECT_ID"))
        @MapKeyColumn(name="ASSIGNEMENT")
        private Map<String, Project> projects = new HashMap<>();
        
    @Entity
    public class Project {
        @Id  private long id;
        ...
        @ManyToMany(mappedBy = "projects")
        private Set<Employee> employeesByAssignment = new HashSet<>();
        
***Not Working*** , see in DDL below that PK in CM_MMB_EMPLOYEE_PROJECT is (EMPLOYEE_ID, ASSIGNEMENT) instead of the expected (EMPLOYEE_ID, PROJECT_ID)
 
    create table CM_MMB_EMPLOYEE (
        id bigint not null auto_increment, 
        name varchar(255), 
        primary key (id))
    create table CM_MMB_EMPLOYEE_PROJECT (
        EMPLOYEE_ID bigint not null,
        PROJECT_ID bigint not null, 
        ASSIGNEMENT varchar(255) not null, 
        primary key (EMPLOYEE_ID, ASSIGNEMENT))
    create table CM_MMB_PROJECT (
        id bigint not null auto_increment, 
        name varchar(255), 
        primary key (id))
    alter table CM_MMB_EMPLOYEE_PROJECT 
        add constraint FK_479ucto5kbsc9tqx4v2equvvw 
        foreign key (PROJECT_ID) references CM_MMB_PROJECT (id) 

Points to remember:

  • Entity Hierachy rules:

    1. The primary key must be defined on the entity that is the root of the entity hierarchy or on a mapped superclass of the entity hierarchy.
    2. The primary key must be defined exactly once in an entity hierarchy.
  • Tree Inheritace types specified with @Inheritance( stategy = InheritanceType.[SINGLE_TABLE|JOINED|TABLE_PER_CLASS]

  • Single Table is most performant for a deep hierarchy of polymorphic entity classes.

  • Single Table is inefficient in database space used.

See the use of @Inheritance(strategy= InheritanceType.SINGLE_TABLE),
 @DiscriminatorColumn and  @DiscriminatorValue

/* ROOT */
@Entity
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="EMP_TYPE")
public abstract class Employee {
    @Id
    private long id;
    private String name;

/* Direct subclass of ROOT, no discriminator value specified, entity name will be used as default */
@Entity
public class ContractEmployee extends Employee {
    private int dailyRate;
    private int term;
    
/* Mapped Superclass derived directly from ROOT*/
@MappedSuperclass
public abstract class CompanyEmployee extends Employee{
    private int vacationDays;
    
/* Mapped Superclass subvlass, with discriminator value specified*/    
@Entity
@DiscriminatorValue("FT_EMP")
public class FullTimeEmployee extends CompanyEmployee {
    private int salary;
    private int pension;

/* Mapped Superclass subvlass, with discriminator value specified*/        
@Entity
@DiscriminatorValue("PT_EMP")
public class PartTimeEmployee extends CompanyEmployee{
    private int hourlyRate;
 See the use of @Inheritance(strategy= InheritanceType.JOINED),
     @DiscriminatorColumn and  @DiscriminatorValue
     
@Entity
@Inheritance(strategy= InheritanceType.JOINED)
@DiscriminatorColumn(name="EMP_TYPE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Employee {
    @Id
    @GeneratedValue
    private long id;
    ...
    
@Entity
@DiscriminatorValue("1")
public class ContractEmployee extends Employee {
   ....

@MappedSuperclass
public abstract class CompanyEmployee extends Employee{
    ....

@Entity
@DiscriminatorValue("2")
public class FullTimeEmployee extends CompanyEmployee {
    ....
    
@Entity
@DiscriminatorValue("3")
public class PartTimeEmployee extends CompanyEmployee{
    ....
See the use of @Inheritance(strategy= InheritanceType.TABLE_PER_CLASS),
     @DiscriminatorColumn and  @DiscriminatorValue, also see the use of
     @AttributeOverrides and @AssociationOverride
     
@Entity
@Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name="EMP_TYPE")
public abstract class Employee {

    @Id
    // @GeneratedValue Is NOT ALLOWED
    private long id;
    
@Table(name="CONTRACT_EMP")
@Entity
@AttributeOverrides({
        @AttributeOverride(name="name",column=@Column(name="FULLNAME")),
        @AttributeOverride(name="startDate",column=@Column(name="SDATE"))
})
public class ContractEmployee extends Employee {
.... 

@MappedSuperclass
public abstract class CompanyEmployee extends Employee{
....

@Table(name="FULTIME_EMP")
@Entity
@AssociationOverride(name="department",joinColumns = @JoinColumn(name="DPT"))
public class FullTimeEmployee extends CompanyEmployee  
....
@Table(name="PRTTIME_EMP")
@Entity
@AttributeOverride(name="manager", column=@Column(name="MGR"))
public class PartTimeEmployee extends CompanyEmployee{
....

###1. Many To Many with relationship state, project arel_many2many_state Unresolved problem follows the entities as they should be:

//Employee Entity
@Entity
public class Employee {
    @Id  private long id;
    private String name;
    @OneToMany(mappedBy = "employee")
    private Set<ProjectAssignement> projectAssignements = new HashSet<>();

//Project Entity
@Entity
public class Project {
    @Id private long id;
    private String name;
    @OneToMany(mappedBy = "project")
    private Set<ProjectAssignement> projectAssignements = new HashSet<>();

//IdClass for ProjectAssignement entity
public class ProjectAssignementId implements Serializable {
    private long employeeId;
    private long projectId;

//ProjectAssignement Entity
@Entity
@IdClass(value = ProjectAssignementId.class)
public class ProjectAssignement {
    @Id
    @ManyToOne
    @JoinColumn(name = "EMP_ID")
    private Employee employee;
    Id
    @ManyToOne
    @JoinColumn(name = "RROJ_ID")
    private Project project;

Problem Description Use of Id class combined with @Id within ProjectAssignement doesn't seem to work. As a workaround a generated surrogate key is been used.

Weaknesses

  • Unresolved issues with @OrderBy project cm_list
  • Unresolved issues with ManyToMany and Map(s) project cm_map_many2many
  • Unresolved issues with @IdClass when their fields are used as part of @OneToMany, project arel_many2many_state

Future

  • Explore @OrderBy when applied at @ElementCollection of basic type