1. 数据持久化
1.1 JPA
在JPA 产生之前,围绕如何简化数据库操作的相关讨论已经是层出不穷,其中ORM 框架最为开发人员所关注。ORM是一种用于实现面向对象编程语言里不同类型系统的数据之间的转换的技术,它们将对象拆分成SQL 语句,从而来操作数据库。但是不同的ORM 框架在使用上存在比较大的差异,而JPA 规范就是为了解决这个问题:规范ORM 框架,使用ORM 框架统一的接口和用法。
最早的JPA 规范是由Java 官方提出的,随Java EE 5 规范一同发布。
1.2 实体
通常,实体表示关系数据库中的表,井且每个实体实例对应于该表中的行。实体的持久状态通过持久化字段或持久化属性来表示,这些字段或属性使用对象/关系映射注解将实体和实体关系映射到基础数据存储中的关系数据。
1.2.1 实体类的要求
( 1 )类必须用@Entity 注解。
( 2 )类必须有一个public 或protected 的元参数的构造函数。该类可以具有其他构造函数。
( 3 )类不能声明为final 。没有方法的或持久化实例变量必须声明为final 。
( 4 )如果实体实例被当作值以分离对象方式进行传递(如通过会话Bean 的远程业务接口),
则该类必须实现Serializable 接口。
( 5 )实体可以扩展实体类或者是非实体类,并且非实体类可以扩展实体类。
( 6 )持久化实例变量必须声明为private 、protected 或package-private ,并且只能通过实体类的
方法直接访问。客户端必须通过访问器或业务方法访问实体的状态。
举个例子:
package com.duxihu.demothymeleaf.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class DemoUser {
@Id //主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//自增长策略
private Long id;
private String name;
private Integer age;
protected DemoUser(){//无参构造函数;设为protected防止被直接使用
}
public DemoUser(Long id ,String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "DemoUser{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
如果实体使用持久化属性, 实体必须遵循JavaBean 组件的方法约定。对于实体的每个持久化属性,都有一个getter 方法和setter方法。如果属性是布尔值,可以使用isProperty ,而不是getProperty 。持久化属性的对象/关系映射注解必须应用于getter方法。映射注解不能应用于注解为@Transient或标记为transient 的字段或属性。
1.2.2 在实体字段和属性中使用集合
集合作为持久化字段和属性,必须使用受Java 集合支持的接口:
• java.util.Collection。
• java.util.Set 。
• java.util.List 。
• java. util.Map 。
如果实体的字段或属性由基本类型或可嵌入类的集合组成,请在字段或属性上使用@ElementCollection 注解。
@ElementCollection protected Set<String> loverName =new HashSet<>();
1.2.3 实体中的主键
实体可以具有简单主键或复合主键。
简单主键使用@Id 注解来表示主键属性或字段。
当主键由多个属性组成时,使用复合主键,复合主键必须在主键类中定义,使用@Embeddedld和@IdClass来标识。
主键或复合主键的属性或字段必须是以下Java 语言类型之一。
Java 基本数据类型。
Java 基本数据类型的包装类型。
java.lang.String 。
java.util.Date (时间类型应为DATE )。
java.sql.Date
java.math.BigDecimal
java.math.Biglnteger
不应在主键中使用浮点类型。
主键类必须满足以下要求
( 1 )类的访问控制修饰符必须是public 。
( 2 ) 如果使用基于属性的访问, 主键类的属性必须为public 或protected 。
( 3 )该类必须有一个公共默认构造函数。
( 4 )类必须实现hashCode()方法和equals(Object other)方法。
( 5 )类必须是可序列化的。
( 6 )复合主键必须被表示并映射到实体类的多个字段或属性,或者必须被表示井映射为可嵌
入类。
( 7 ) 如果类映射到实体类的多个字段或属性, 则主键类中的主键宇段或属性的名称和类型必
须与实体类的名称和类型匹配.
1.2.4 实体间的关系
( 1 ) 一对一 One to One
( 2 ) 一对多 One to Many
( 3 ) 多对一 Many to One
( 4 ) 多对多 Many to Many
实体关系中的方向可以是双向的或单向的。
1. 双向关系 , 双向关系必须遵循以下规则:
( 1 )双向关系的反面必须通过使用@OneToOne @OneToMany 或@ ManyToMany 注解的
mappedBy元素引用其拥有方。mappedBy元素指定实体中作为关系所有者的属性或字段。
( 2 )多对一双向关系的许多方面不能定义mappedBy 元素。许多方面总是关系的拥有方。
( 3 )对于一对一双向关系,拥有侧对应于包含相应外键的一侧。
( 4 )对于多对多双向关系,任一侧可以是拥有侧。
2. 单向关系
只有一个实体具有引用另一个实体的关系字段或属性.
1.2.5 级联操作相关系
使用关系的实体通常依赖于关系中另一个实体的存在,比如用户详情是用户项的一部分 ,
如果用户被删除,用户详情也应该被删除,这称为级联删除关系。
实体的级联操作
ALL : ALL 级联操作将应用于父实体的相关实体。所有等同于指定cascade={DETACH,MERGE, PERSIST, REFRESH, REMOVE}
DETACH: 如果父实体与持久化上下文分离,则相关实体也将被分离
MERGE : 如果父实体被合并到持久化上下文中,则相关实体也将被合并
PERSIST : 如果父实体被持久化到持久化上下文中,则相关实体也将被持久化
REFRESH : 如果父实体在当前持久化上下文中被刷新,相关实体也将被刷新
REMOVE : 如果父实体从当前持久化上下文中删除,相关实体也将被删除
级联删除关系使用@OneToOne 和@OneToMany 关系的cascade=REMOVE 元素指定。例如,
@OneToMany(cascade=REMOVE , mappedBy=”User ”)
public Set<UserNumbers> getNumbers () { return numbers ; }
删除关系中的“孤儿”
@OneToMany 和@oneToOne 中的orphanRemoval 属性采用布尔值,默认值为false 。修改为true,则在删除用户后默认删除用户妻子们;
@OneToMany(mappedBy="User", orphanRemoval="true")
public List <UserWifes> getWifes() { ... }
1.2 Spring Data JPA 是对JPA 规范的实现。
对于普通开发者而言,自己实现应用程序的数据访问层是一件极其烦琐的过程。必须编写太多的样板代码来执行简单查询、分页和审计。
Spring Data JPA 包含如下特征。
( 1 )基于Spring 和JPA 来构建复杂的存储库。
( 2 )支持Querydsl谓词,因此支持类型安全的JPA查询。
( 3 )域类的透明审计。
( 4