@Entity(name = "Employee")
@Table(name = "Employee")
public class Employee extends PersistenceEntityBase {
}
PersistenceEntityBase, annotated with @MappedSuperclass, is the base type of all persistent entity types.
@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;
}
public class Employee extends PersistenceEntityBase {
@OneToOne(cascade={CascadeType.ALL})
@JoinColumn(name="employeeDetailId")
public EmployeeDetail getEmployeeDetail() {
}
}
public class Employee extends PersistenceEntityBase {
@ManyToOne
@JoinColumn(nullable=false, name="departmentId")
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
@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.
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;
}
}
Merchandise <----- @MappedSuperclass | Electronics <----- @MappedSuperclass / \ Computer Printer <----- @EntityMerchandise 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 {
}
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.
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.
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.
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" />