Cmobilecom JPA 2.0.1 Developer Guide

7. ORM Mapping

ORM mapping uses annotations and/or XML to map entities and their relationships to database, and XML mappings override corresponding annotations.

For complete ORM mapping, see JPA ORM Schema 2.2.

For proprietary ORM mapping extension, see Cmobilecom JPA ORM Extension Schema 1.0 which extends JPA ORM Schema 2.2.

Persistence Unit Metadata

The persistence-unit-metadata element applies to the persistence unit. There should be at most one persistence-unit-metadata element among all orm mapping files.

To ignore all annotations and use XML mappings only, add <xml-mapping-metadata-complete/> element.

The persistence-unit-defaults element specifies persistence unit default values, including catalog, schema, delimited identifiers, access, cascade-persist and default listeners.

For example, META-INF/orm.xml

<entity-mappings>
    <persistence-unit-metadata>
        <xml-mapping-metadata-complete/>
    
        <persistence-unit-defaults>
            <schema>jpa_example_schema</schema>
    
            <entity-listeners>
                <entity-listener class="com.cmobilecom.jpa.example.managed_classes.DefaultListener"/>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
    
</entity-mappings>    

OneToOne

OneToOne maps one to one relationship between two entity types. For example, Annotations:
@Entity
public class Employee {
    @OneToOne(fetch=FetchType.LAZY, mappedBy="employee")
    private Office office;
}

@Entity
public class Office {
    @OneToOne(fetch=FetchType.EAGER)
    @JoinColumn(name="employee_id")
    private Employee employee;
}
XML:
<entity-mappings>
    <entity class="Employee">
        <attributes>
            <one-to-one name="office" fetch="LAZY" mapped-by="employee">
            </one-to-one>
        </attributes>
    </entity>
    
    <entity class="Office">
        <attributes>
            <one-to-one name="employee" fetch="EAGER">
                <join-column name="employee_id"/>
            </one-to-one>
        </attributes>
    </entity>
</entity-mappings>

OneToMany/ManyToOne

OneToMany maps one to many relationship between two entity types, and ManyToOne maps many to one relationship between two entity types. For example,
@Entity
public class Employer {
    @OneToMany(cascade={CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.LAZY,
        mappedBy="employer", targetEntity=Employee.class)
    @OrderColumn(nullable=false)
    private List<Employee> employees;
}

@Entity
public class Employee {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="employer_id", nullable = false)
    private Employer employer;
}
XML:
<entity-mappings>
    <entity class="Employer">
        <attributes>
            <one-to-many name="employees" fetch="LAZY" orphan-removal="true"
                    mapped-by="employer" target-entity="Employee">
                <order-column nullable="false"/>
				
                <cascade>
                    <cascade-all/>
                </cascade>
            </one-to-many>
        </attributes>
    </entity>
    
    <entity class="Employee">
        <attributes>
            <many-to-one name="employer" fetch="LAZY">
                <join-column name="employer_id" nullable="false"/>
            </many-to-one>
        </attributes>
    </entity>
</entity-mappings>

ManyToMany

ManyToMany maps many to many relationship between two entity types. For example,

Annotations:

@Entity
@Table(name="User_Table")
@IdClass(UserId.class)
public class User {
    public enum Type {
        MANAGER,
        REGULAR
    }

    @Id
    @Enumerated(EnumType.ORDINAL)
    private Type type;
    
    @Id
    @Column(length=20)
    private String username;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "User_Role",
        joinColumns = {@JoinColumn(name = "user_type", referencedColumnName = "type"),
            @JoinColumn(name = "username", referencedColumnName = "username")},
        inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private List<Role> roles;
}
    
@Entity
public class Role {
    @Id
    private Long id;
}
    
public class UserId {
    private Type type;
    private String username;
    // getter/setter, equals(), hashCode()
}

Join Table foreign keys:

   
       User_Table                       User_Role                Role
    ------------------------------------------------------------------------
    (type,username)  <---  (user_type,username)  (role_id)  --->  (id)
    
XML:
<entity-mappings>
    <entity class="User">
        <table name="User_Table"/>
        <id-class class="UserId"/>
    
        <attributes>
            <id name="type">
                <enumerated>ORDINAL</enumerated>
            </id>
    
            <id name="username">
                <column length="20"/>
            </id>
    
            <many-to-many name="roles" fetch="LAZY">
                <join-table name="User_Role">
       	    	<join-column name="user_type" referenced-column-name="type"/>
                    <join-column name="username" referenced-column-name="username"/>
                    <inverse-join-column name="role_id"/>
              	</join-table>
            </many-to-many>
        </attributes>
    </entity>
    
    <entity class="Role">
        <attributes>
            <id name="id">
            </id>
        </attributes>
    </entity>
</entity-mappings>

Element Collection

Element collection maps a relationship between entity and a collection of elements of basic or embeddable type. For example,

Annotations:

public class Employee {
    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(name="Employee_PhoneNumber")
    @Column(name="phoneNumber", length = 15)
    private List<PhoneNumber> phoneNumbers;
}
Collection table:
    Employee     Employee_PhoneNumber
    ------------------------------------
      id   <---- Employee_id, phoneNumber
XML:
<entity-mappings>
    <entity class="Employee">
        <attributes>
            <element-collection name="phoneNumbers" fetch="EAGER">
                <column name="phoneNumber" length="15"/>
                <collection-table name="Employee_PhoneNumber"/>
            </element-collection>
         </attributes>
    </entity>
</entity-mappings>

Embedded

Attributes of an embeddable type can be embedded into another managed type. For example,

Annotations:

@Embeddable
public class ZipCode {
    @Column(length = 5)
    private String majorCode;
    
    @Column(length = 4)
    private String minorCode;
}

@Embeddable
public class Address {
    @Column(length = 50)
    private String street;
    
    @Column(length = 30)
    private String city;
    
    @Column(length = 20)
    private String state;
    
    @Embedded
    private ZipCode zipCode;
}

@Entity
public class Employee {
    @Embedded
    private Address address;
}
ZipCode is embedded into Address which is embedded into Employee.

XML:

<entity-mappings>
    <embeddable name="ZipCode"/>
    
    <embeddable name="Address">
         <attributes>
   			<embedded name="zipCode"/>
         </attributes>
    </embeddable>
    
    <entity class="Employee">
        <attributes>
   			<embedded name="address"/>
         </attributes>
    </entity>
</entity-mappings>

AttributeOverride/AssociationOverride

AttributeOverride/AssociationOverride annotations or corresponding XML mappings override inherited or embedded attribute/association mappings. For example, <p> Annotations:
@MappedSuperclass
public class Person {
    @Column(length = 30, nullable = false)
    private String name;
    
    @OneToOne
    /* default
	@JoinColumns({
		@JoinColumn(name="user_type", referencedColumnName = "type"),
		@JoinColumn(name="user_username", referencedColumnName = "username")
	})
    */
    private User user;
}
    
@Entity
@AttributeOverride(name = "name", column = @Column(length = 32, nullable = false))
@AssociationOverride(name = "user", joinColumns = {
        @JoinColumn(name = "userType", referencedColumnName = "type"),
        @JoinColumn(name = "username", referencedColumnName = "username")
    }
)
public class Employee extends Person {
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name="zipCode.majorCode", column = @Column(name = "zipcode_major",
            length = 10, nullable = false)),
        @AttributeOverride(name="zipCode.minorCode", column = @Column(name = "zipcode_minor",
            length = 4, nullable = true))
	})
	private Address address;
}
ZipCode is embedded into Address which is embedded into Employee.

XML:

<entity-mappings>
    <mapped-superclass name="Person">
         <attributes>
            <one-to-one name="user"/>
         </attributes>
    <mapped-superclass/>
    
    <entity class="Employee">
    
    	<attribute-override name="name">
            <column length="32" nullable="false"/>
        </attribute-override>
    
        <association-override name="user">
            <join-column name="userType" referenced-column-name="type"/>
            <join-column name="username" referenced-column-name="username"/>
        </association-override>
    
        <attributes>
            <embedded name="address">
                <attribute-override name="zipCode.majorCode">
                    <column name="zip_code_major" length="6" nullable="false"/>
                </attribute-override>
    
                <attribute-override name="zipCode.minorCode">
                    <column name="zip_code_minor" length="4" nullable="true"/>
                </attribute-override>
            </embedded>
         </attributes>
    </entity>
</entity-mappings>

Expression

(Cmobilecom-JPA proprietary feature)

Expressions #{expr} are supported in mapping table schema names and multitenant attributes, which can be resolved by its corresponding property (expression as property name) set in META-INF/persistence.xml or passed to Persistence.createEntityManagerFactory() method. Expressions are useful in case that ORM mappings are shared by multiple persistence units. For example,

orm.xml

    <entity-mappings>
        <entity class="Employee">
            <table schema="#{employee.schema}" name="Employee"/>
            <multitenant enabled="#{multitenant.enabled}"/>
        </entity>
    </entity-mappings>
Method 1: Resolve expressions by setting properties in META-INF/persistence.xml:
<persistence>

    <persistence-unit name="pu_1" transaction-type="RESOURCE_LOCAL">

        <mapping-file>orm.xml</mapping-file>

        <properties>
            <property name="employee.schema" value="hr" />
            <property name="multitenant.enabled" value="true" />
        </properties>

    </persistence-unit>

    <persistence-unit name="pu_2" transaction-type="RESOURCE_LOCAL">

        <mapping-file>orm.xml</mapping-file>

        <properties>
            <property name="employee.schema" value="" />
            <property name="multitenant.enabled" value="false" />
        </properties>

    </persistence-unit>

</persistence>
Method 2: Resolve expressions by passing properties to the method of creating EntityManagerFactory:
    // persistence unit 1
    Map<String, Object> properties = new HashMap<>();
    properties.put("employee.schema", "hr");
    properties.put("multitenant.enabled", "true");

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu_1", properties);
    EntityManager em = emf.createEntityManager();

    // persistence unit 2
    Map<String, Object> properties = new HashMap<>();
    properties.put("employee.schema", "");
    properties.put("multitenant.enabled", "false");

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu_2", properties);
    EntityManager em = emf.createEntityManager();
Persistence UnitEntity IdentifierFrames / No Frames