Cmobilecom AF 5.19 Developer Guide

5.1 Entity Annotations

For entities to be managed by Cmobilecom AF and JPA, entity types must extend PersistenceEntityBase and be annotated by JPA standard. For example,

	@Entity(name = "Employee")
	@Table(name = "Employee")
	public class Employee extends PersistenceEntityBase {

	}
PersistenceEntityBase, annotated with @MappedSuperclass, is the base type of all persistent entity types.

Entity Id

Entity id is Long type. AUTO id generation strategy is recommended for efficiency. For example,

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Long getId() {
		return id;
	}
With AUTO strategy, entity id is mapped to table Identity column. For those databases that do not support identity column, sequence will be used for id generation.

For composite entity id, use @IdClass or @EmbeddedId.

The ORM mappings for entity properties use JPA annotations on getter methods, not member variables. For example,

	@Column(nullable=false, length=30)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

One To One

For example, Employee and EmployeeDetail.

	public class Employee extends PersistenceEntityBase {

		@OneToOne(cascade={CascadeType.ALL})
		@JoinColumn(name="employeeDetailId")
		public EmployeeDetail getEmployeeDetail() {

		}
	}

Many To One

For example, Employee and Department. An employee is associated with one Department, and one Department can have many employees.

	public class Employee extends PersistenceEntityBase {

		@ManyToOne
		@JoinColumn(nullable=false, name="departmentId")
		public Department getDepartment() {
			return department;
		}

		public void setDepartment(Department department) {
			this.department = department;
		}
	}

One To Many

For example, one ExpenseClaim can have many ExpenseClaimItem(s). ExpenseClaimItem(s) are child entities of ExpenseClaim. Reverse mapping is used from ExpenseClaim to ExpenseClaimItem.

	@Entity(name = "ExpenseClaim")
	@Table(name = "ExpenseClaim")
	@ParentType({ExpenseClaimItem.class})
	public class ExpenseClaim extends PersistenceEntityBase {
		private List<ExpenseClaimItem> expenseClaimItems;

		@OneToMany(cascade={CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.LAZY,
				mappedBy="expenseClaim", targetEntity=ExpenseClaimItem.class)
		@OrderBy(ExpenseClaimItem.PROPERTY_DATE + " DESC")
		public List<ExpenseClaimItem> getExpenseClaimItems() {
			return expenseClaimItems;
		}

		public void setExpenseClaimItems(List<ExpenseClaimItem> expenseClaimItems) {
			this.expenseClaimItems = expenseClaimItems;
		}
	}

	@Entity(name = "ExpenseClaimItem")
	@Table(name = "ExpenseClaimItem")
	public class ExpenseClaimItem extends PersistenceEntityBase implements ChildEntity {
		private ExpenseClaim expenseClaim;

		@ParentEntity
		@ManyToOne
		@JoinColumn(name = "expenseClaimId", nullable = false)
		public ExpenseClaim getExpenseClaim() {
			return expenseClaim;
		}

		public void setExpenseClaim(ExpenseClaim expenseClaim) {
			this.expenseClaim = expenseClaim;
		}

	}
Note that CascadeType.ALL and orphanRemoval should be present for Parent/Child entity relationship. The annotation:
	@OrderBy(ExpenseClaimItem.PROPERTY_DATE + " DESC")
Tells JPA that the list of ExpenseClaimItems are ordered by "date" descending.

For Parent/Child relationship, parent entity type is annotated with @ParentType. Child entity type implements ChildEntity, and its parent property getter method is annotated with @ParentEntity.

Many To Many

For example, User and Role. One user can have many roles, and one role can be associated with many users.

	public class User extends PersistenceEntityBase {
		private Set<Role> roles;

		@ManyToMany(fetch = FetchType.LAZY, targetEntity = Role.class)
		@JoinTable(name = "User_Role",
			joinColumns = {@JoinColumn(name = "userId")},
			inverseJoinColumns = {@JoinColumn(name = "roleId")})
		public Set<Role> getRoles() {
			return roles;
		}

		public void setRoles(Set<Role> roles) {
			this.roles = roles;
		}
	}

Mapped Superclass

For class hierarchy, annotate superclasses with @MappedSuperclass, and annotate entity classes with @Entity, mapping them to separated tables. For example,
               Merchandise        <----- @MappedSuperclass
                    |
               Electronics        <----- @MappedSuperclass
               /        \
          Computer     Printer    <----- @Entity
Merchandise and Electronics are mapped Superclasses, and Computer and Printer are entities mapped to separate tables.

	@MappedSuperclass
	abstract public class Merchandise extends PersistenceEntityBase {

	}

	@MappedSuperclass
	abstract public class Electronics extends Merchandise {

	}

	@Entity(name = "Computer")
	@Table(name = "Computer")
	public class Computer extends Electronics {

	}

	@Entity(name = "Printer")
	@Table(name = "Printer")
	public class Printer extends Electronics {

	}

Entity Hierarchy

Entity types within one inheritance hierarchy can be mapped by separate tables, joined tables or single table.
	               EntityType A
	               ------------
	               /          \
	     EntityType B      EntityTYpe C
	     ------------      ------------
         /          \
EntityType D    EntityType E
------------    ------------
For example, use SINGLE_TABLE mapping strategy with DiscriminatorColumn for simplicity, performance and query polymorphism.
	@Entity(name = "EntityTypeA")
	@Table(name = "TableName")
	@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
	@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.CHAR)
	@DiscriminatorValue("A")
	public class EntityTypeA extends PersistenceEntityBase {

	}

	@Entity(name = "EntityTypeB")
	@DiscriminatorValue("B")
	public class EntityTypeB extends EntityTypeA {

	}

	@Entity(name = "EntityTypeC")
	@DiscriminatorValue("C")
	public class EntityTypeC extends EntityTypeA {

	}

	@Entity(name = "EntityTypeD")
	@DiscriminatorValue("D")
	public class EntityTypeD extends EntityTypeB {

	}

	@Entity(name = "EntityTypeE")
	@DiscriminatorValue("E")
	public class EntityTypeE extends EntityTypeB {

	}
For JPQL query polymorphism:
	select * from EntityTypeB;
Entities of subclass entity types (D and E) will be included in query results.
	select * from EntityTypeA e where type(e) in (EntityTypeC, EntityTypeD);
Select all entities of EntityTypeC and EntityTypeD.

For other mapping strategies(joined tables, separated tables), refer to JPA docs.

Common Types

Entity property common type annotations:

java.lang.String. For example,


	@Column(nullable=false, length=30)
	public String getName() {
		return name;
	}

	@javax.persistence.Lob
  	@Column(nullable=false, length=65536)
  	public String getDescription() {
		return description;
	}

java.util.Date. For example,

	@Column(nullable=false)
	@Temporal(value=TemporalType.DATE)
	public Date getHiredDate() {
		return hiredDate;
	}

	@Column(nullable=false)
	@Temporal(value=TemporalType.TIMESTAMP)
	public Date getCreatedDate() {
		return createdDate;
	}
LocalDate/LocalDateTime/LocalTime. For example,

	public LocalDate getBirthday() {
		return birthday;
	}

	public LocalDateTime getMeetingTime() {
		return meetingTime;
	}

	public LocalTime getBusinessHourStartTime() {
		return businessHourStartTime;
	}
Enum type. For example,

	public enum EmployeeType {
		FULL_TIME,
		PART_TIME
	}

	@Column(nullable=false)
	@Enumerated(value=EnumType.ORDINAL)
	public EmployeeType getType() {
		return type;
	}
If an Enum property is annotated as ordinal, it can be mapped to different number sizes in database. For example, tinyint for MySql and NUMBER(2,0) for Oracle database.

Number type. For example,


	private Integer age;
	private BigDecimal salary;

	@Column(nullable=false)
	public Integer getAge() {

	}

	@Column(nullable=false)
	public BigDecimal getSalary() {

	}

The Number type can be mapped to different number sizes in database, but columnDefinition is not recommended since it is not portable. For example, map the age property to smallint for MySql and NUMBER(3,0) for Oracle database.

Boolean type. For example,


	private Boolean active;

	@Column(nullable=false)
	public Boolean getActive() {

	}
Boolean is mapped to bit(1) for MySql, and NUMBER(1,0) for Oracle database.

java.util.Locale type. For example,


	private Locale locale;

	@Column(nullable=false, length=15)
	public Locale getLocale() {

	}
JPA converter LocaleConverter(autoApply=true) is included in system module conf/orm.xml.
	<converter class="com.cmobilecom.af.entity.persistence.LocaleConverter" auto-apply="true" />
Locale is mapped to its LanguageTag except ROOT which is mapped to empty string.

DataType. For example,


	@Column(length=30)
	public DataType getEntityType() {
		return entityType;
	}
JPA converter DataTypeConverter(autoApply=true) is included in system module conf/orm.xml.
	<converter class="com.cmobilecom.af.entity.persistence.DataTypeConverter" auto-apply="true" />
DataType is mapped to a string with format: module.typeName.

User Type

To map User to the UserImpl of system module, set targetEntity to the UserImpl class. For example,

	import com.cmobilecom.af.module.system.entity.UserImpl;

	private User approver;

	@ManyToOne(targetEntity=UserImpl.class)
	@JoinColumn(name="approverUserId")
	public User getApprover() {
		return approver;
	}

	public void setApprover(User approver) {
		this.approver = approver;
	}
The UserImpl of system module is mapped to database table: Users.

User-Defined Types

To map a user-defined type to database column, a JPA converter needs to be provided. For example,

	public class FullName {
	 	String firstName;
	 	String lastName;

	 	public FullName(String firstName, String lastName) {
	 		...
	 	}

	 	public String toString() {
	 		return firstName + "." + lastName;
	 	}
	}
Define its JPA converter:

	@Converter(autoApply=true)
	public class FullNameConverter implements AttributeConverter<FullName, String> {

		@Override
		public String convertToDatabaseColumn(FullName attribute) {
			if (attribute == null)
				return null;

			return attribute.toString();
		}

		@Override
		public FullName convertToEntityAttribute(String dbData) {
			if (dbData == null)
				return null;

			int index = dbData.indexOf('.');
			String firstName = dbData.substring(0, index);
			String lastName = dbData.substring(index + 1);
			return new FullName(firstName, lastName);
		}

	}
With autoApply set to true, the converter will be applied to the FullName type automatically.

	@Column(length=30)
	public FullName getFullName() {
		return fullName;
	}
Add the converter in the module orm.xml.
	<converter class="mypackage.FullNameConverter" auto-apply="true" />

More Information

For complete ORM mappings, refer to JPA and Cmobilecom JPA documentation. Cmobilecom JPA is a JPA standard implementation focusing on minimal runtime code and performance, and provides extension for multitenancy support.
Project BuildQuery CriteriaFrames / No Frames