Entity Annotation

For entities to be managed by JPA and Cmobilecom AF, their 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. If database portability is important, Id table strategy should be used. For example,

	@Id
	@GeneratedValue(strategy=GenerationType.TABLE, generator="ExampleHR.EMP_IdGen")
	@TableGenerator(name="ExampleHR.EMP_IdGen", table="IdGen",
		pkColumnName="entityType", valueColumnName="idValue",
		pkColumnValue="ExampleHR.EMP", initialValue=1, allocationSize=1)
	)  
	public Long getId() {  
		return id;
	}
The IdGen table that is seeded by system has two columns: entityType and idValue. The idValue stores the maximum id that has been allocated for corresponding entity type. The value of entityType column is mapped dataType such as ExampleHR.EMP.

To avoid name confliction, the Id table generator name of an entity type should be prefixed by its mapped data type(module.dataTypeName). For example, ExampleHR.EMP_IdGen.

Id auto generator can be used for simplicity. But it is implemented differently for different databases. For example, JPA uses auto-increment column to generate id for MySql, and uses database sequence to generate id for Oracle database.


	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Long getId() {  
		return id;
	}
Entity property O/R mapping annotations use 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;
	}

ManyToOne

For example, Employee and Department. An employee is associated with one Deparment, and one Deparment 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;
		}
	}

OneToMany

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

	public class ExpenseClaim extends PersistenceEntityBase {
		private List<ExpenseClaimItem> expenseClaimItems;
		
		@OneToMany(cascade={CascadeType.ALL}, orphanRemoval=true, fetch=FetchType.LAZY, 
				mappedBy="expenseClaim", targetEntity=ExpenseClaimItem.class)
		@OrderBy(PersistenceEntity.PROPERTY_SEQUENCE_NO + " ASC")
		public List<ExpenseClaimItem> getExpenseClaimItems() {
			return expenseClaimItems;
		}
	
		public void setExpenseClaimItems(List<ExpenseClaimItem> expenseClaimItems) {
			this.expenseClaimItems = expenseClaimItems;
		}
	}
	
	public class ExpenseClaimItem extends PersistenceEntityBase implements ChildEntity {
		private ExpenseClaim expenseClaim;
	
		@ParentEntity
		@ManyToOne
		@JoinColumn(name = "expenseClaimId")
		public ExpenseClaim getExpenseClaim() {
			return expenseClaim;
		}
	
		public void setExpenseClaim(ExpenseClaim expenseClaim) {
			this.expenseClaim = expenseClaim;
		}
	
	}
Note that CascadeType.ALL and orphanRemoval need to be present for JPA and Cmobilecom AF to manage their relationship correctly.

For ChildEntity, its parent property getter method needs to be annotated with @ParentEntity. To keep the order of child entities, map the property sequenceNo to a database column.


		@Override
		public Integer getSequenceNo() {
			return sequenceNo;
		}

ManyToMany

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;
		}
	}

Entity Type Hierarchy

For class hierarchy, annotate superclasses with @MappedSuperclass, and annotate entity classes with @Entity, mapping them to separated tables.

	@MappedSuperclass
	public class Merchandise extends PersistenceEntityBase {
	
	}
	
	@MappedSuperclass
	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 {
	
	}	

Common Types

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;
	}	
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.

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.

DataType

To map a DataType property, add its converter annotation:

	import com.cmobilecom.af.entity.persistence.DataTypeConverter;
	
	@Column(length=30)
	@Convert(converter=DataTypeConverter.class)
	public DataType getEntityType() {
		return entityType;
	}

User-Defined Types

To map a user-defined type to database column, a converter is needed to be implemented. For example,

	public class FullName {
	 	String firstName;
	 	String lastName;
	 	
	 	public FullName(String firstName, String lastName) {
	 		...
	 	}
	 	
	 	public String toString() {
	 		return firstName + "." + lastName;
	 	}
	}
Define its 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);
		}
	
	}
Use the converter:

	@Column(length=30)
	@Convert(converter=FullNameConverter.class)
	public FullName getFullName() {
		return fullName;
	}
If JPA implementation autoApply works, the @Convert annotation is not needed.

For each persistence unit in persistence.xml, add


	<class>myPackage.FullNameConverter</class>
if exclude-unlisted-classes is true.

More Information

Refer to JPA documentation.