JPA只是一个简化对象关系映射来管理Java应用程序中的关系数据的规范。 它提供了一个平台,可以直接使用对象而不是使用SQL语句。
对于SpringBoot而言,整合JPA是容易的,只需要在maven中引入以下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
选取MySQL为例,引入mysql驱动:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
同时在application.yml中做如下配置:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/database?serverTimezone = GMT%2B8 & useUnicode=true #最后一项为使用utf-8编码 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: database: MySQL database-platform: org.hibernate.dialect.MySQL5InnoDBDialect show-sql: true #是否自动显示sql语句 hibernate: ddl-auto: update
需要注意的是,ddl-auto有五个属性:
ddl-auto | 功能 |
---|---|
create | 程序每次时,检查标注的实体类,并依据实体类重新建表,会导致数据丢失 |
create-drop | 程序运行时,检查标注的实体类,并依据实体类重新建表,并在程序结束时销毁表 |
update | 程序运行时,检查标注的实体类,如果没有表就建表,如果当前表内字段属性与实体类中不符,则会更新表 |
validate | 程序运行时,检查标注的实体类,如果当前表内字段属性与实体类中不符,或不存在对应的表,则会更新表 |
none | 禁用 |
JPA可以协助使用者依据实体类创建表,这要求使用者提供标注,以下为实例:
@Entity //实体 @Table(name = "user")//JPA会依据name建表读表 public class User read more
Spring-boot data JPA(一)建库与一对多、多对多关系
JPA只是一个简化对象关系映射来管理Java应用程序中的关系数据的规范。 它提供了一个平台,可以直接使用对象而不是使用SQL语句。
对于SpringBoot而言,整合JPA是容易的,只需要在maven中引入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
选取MySQL为例,引入mysql驱动:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
同时在application.yml中做如下配置:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/database?serverTimezone = GMT%2B8 & useUnicode=true #最后一项为使用utf-8编码
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
database: MySQL
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true #是否自动显示sql语句
hibernate:
ddl-auto: update
需要注意的是,ddl-auto有五个属性:
ddl-auto | 功能 |
---|---|
create | 程序每次时,检查标注的实体类,并依据实体类重新建表,会导致数据丢失 |
create-drop | 程序运行时,检查标注的实体类,并依据实体类重新建表,并在程序结束时销毁表 |
update | 程序运行时,检查标注的实体类,如果没有表就建表,如果当前表内字段属性与实体类中不符,则会更新表 |
validate | 程序运行时,检查标注的实体类,如果当前表内字段属性与实体类中不符,或不存在对应的表,则会更新表 |
none | 禁用 |
JPA可以协助使用者依据实体类创建表,这要求使用者提供标注,以下为实例:
@Entity //实体
@Table(name = "user")//JPA会依据name建表读表
public class User{
@Id//标识主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键自增策略
private Integer id;
@Column(name = "name", unique = true, nullable = false, length = 64)//name属性对应表中的属性,unique,nullable分别对应属性是否唯一,是否为空(默认为否),length则代表存储长度
private String username;
//省略空参构造,与get/set函数
}
为避免空参构造与get/set的繁琐,提高代码的简洁性,可以引入lombok:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
lombok提供以下注释:
注释 | 作用域及作用 |
---|---|
@Setter | 注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。 |
@Getter | 注解在类或字段,注解在类时为所有字段生成getter方法,注解在字段上时只为该字段生成getter方法 |
@ToString | 注解在类,添加toString方法。 |
@EqualsAndHashCode | 注解在类,生成hashCode和equals方法。 |
@NoArgsConstructor | 注解在类,生成无参的构造方法。 |
@RequiredArgsConstructor | 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。 |
@AllArgsConstructor | 注解在类,生成包含类中所有字段的构造方法。 |
@Data | 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法 |
@Slf4j | 注解在类,生成log变量,严格意义来说是常量。 |
JPA要求一个空参构造与所有的Get/Set函数,根据我们的需求,重新构造User:
@Data
@NoArgsConstructor
@Entity
@Table(name = "user")
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
@Column(name = "name", unique = true, nullable = false, length = 64)
private String username;
}
可以为User添加一对多关系Admin,一个Admin管理多个User:
@Data
@NoArgsConstructor
@Entity
@Table(name = "admin")
public class Admin{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer adminId;
@OneToMany(targetEntity = User.class, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
//targetEntity指向实体类,cascade代表级联方式
@JoinColumn(name = "adminId")
//name代表作为外键的属性
private Collection<User> users;
}
同时User中添加以下一行:
@JoinColumn(name = "adminId", referencedColumnName = "adminId")
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Admin adminId;
可以为User添加多对多关系Role,在JPA中,多对多的双方并不是完全对等的,通常分为支配方与被支配方: 选择User作为支配方,添加代码:
@ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.MERGE,CascadeType.REFRESH})
@JoinTable(name = "role2tag",//多对多关系在数据库中需要创建新表,这一行是表名
joinColumns = {@JoinColumn(name = "userId", referencedColumnName = "userId")},//两个属性分别支配方被引用的属性,以及改属性在role2tag表中的名称
inverseJoinColumns = {@JoinColumn(name = "roleId", referencedColumnName = "roleId")}//两个属性分别被支配方被引用的属性,以及改属性在role2tag表中的名称
)
private Collection<Role> roles;
同时创建实体类Role:
@Data
@NoArgsConstructor
@Entity
@Table(name = "admin")
public class Role{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer roleId;
@ManyToMany(targetEntity = User.class, mappedBy = "roles", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
//mappedBy对应支配方引用该实体的属性,即roles
private Collection<User> users;
}
JPA的级联属性如下所示:
级联属性 | 作用 |
---|---|
PERSIST | 级联保存 |
MERGE | 级联更新 |
REMOVE | 级联删除 |
REFRESH | 级联获取 |
DETACH | 级联移除 |
ALL | 以上全部 |
REFRESH、DETACH中的级联是相对于程序的而言的,这涉及到JPA的一种底层工作逻辑: 当JPA(请姑且允许我这么说)从数据库中读取到一条数据时,假如对于该对象存在一对多或者多对多的关系,那么JPA并不会读取到这些关联的对象,而是保存用以查询这些对象的外键,只有当这些对象被调用时,JPA才会根据之前保存的外键到数据库中查找。而级联获取,其含义就是JPA会在读取对象的同时,调用关联的对象;级联移除同理。