一、jpa的概念
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。主要作用是简化JavaEE和JavaSE的应用开发,还有就是对ORM技术的整合。Hibernate 从3.2开始,就开始兼容JPA。Hibernate3.2获得了Sun TCK的JPA(Java Persistence API)兼容认证。其实如果对hibernate比较熟的,对于理解和应用JPA来说其实就没有多大困难了!,这里就不对Hibernate进行具体的介绍了。下文中用的全部为注释进行操作的,这里就不进行一一解说了。二、jpa的相关注解描述
注解 | 作用 |
---|---|
@Basic | 基本注解,默认有 |
@Entity | 指出该Java类为实体类,将映射到指定的数据库 |
@Table | 标注常用name属性,用于指定数据库的表的名称 |
@Id | 主键的映射 |
@GeneratedValue | 用于标注主键的生成策略,通过strategy属性指定 |
@Column | 映射数据表的列名,指定unique,length等 |
@Temporal | 在属性上调整精度,比如Date |
@Transient | 忽略该属性,不需要映射到数据表的一列,否则默认为@Basic |
@JoinColumn | 用来映射外键 |
@JoinTable | 插入表 多用于多对多或者一对多 |
@OneToOne | 映射一对一的关系 |
@OneToMany | 映射少对多的关系 |
@ManyToOne | 映射多对少的关系 |
@ManyToMany | 映射多对对的关系 |
- @Table的常用属性:
- name : 用来命名 当前实体类 对应的数据库 表的名字
- uniqueConstraints : 用来批量命名唯一键
- catalog : 实体指定的目录名或是数据库名
- schema : 实体指定的目录名或是数据库名
- @Entity的常用属性:
- name : 实体的名称
- @GeneratedValue的常用属性:
- strategy : ID的生成策略
- GenerationType.IDENTITY 主键由数据库自动生成(主要是自动增长型)
- GenerationType.AUTO 主键由程序控制
- GenerationType.TABLE 使用一个特定的数据库表格来保存主键
- GenerationType.SEQUENCE 根据底层数据库的序列来生成主键,条件是数据库支持序列
- strategy : ID的生成策略
- @Column相关属性:
- name : 对应的列表的名称
- unique : 是否是唯一
- nullable : 是否为空
- length : 长度
- insertable : 是否插入表
- updatable :是否更新表
- @Temporal
- value : TemporalType.DATE,TemporalType.TIME,TemporalType.TIMESTAMP,以上三个参数分别表示年月日、时分秒、年与日时分秒
- @JoinColumn部分参数:
- name : 表的名称,一般情况下默认,但是多对多的时候第三方表的时候需要填写
- referencedColumnName : 产生外键,依据的列的名称
- @OneToOne、@OneToMany、@ManyToMany、@ManyToOne相关参数:
- targetEntity : 变量对应的类
- cascade : 关联级别
- fetch : 加载方式 FetchType.EAGER数据同步加 FetchType.LAZY 懒加载,使用的时候才会加载
- optional : 当其为false 真实关系必须存在 当其为true 可以不存在
- mappedBy : 放弃对外键的维护,数值为本类中在外键维护类中的变量名称
- orphanRemoval : 当执行级联操作删除时,如果此为true 那么就把关系被维护端的数据实体删除,false 不删除,删除的只是关系
三、jpa中类实体的映射关系
实体关系映射,简化编程操作,把冗余的操作交给底层框架来处理。只要我们把实体关系的映射写清楚,那么就不用过多的考虑sql语句的关系了。
1、一对一映射(@OneToOne)
一对一关系,说白了就是一个萝卜一个坑,这里例子是Student<----->Car,一个学生只有一辆车,一辆车的归属只是一个学生(当然学生可以没有车,车也可以不属于任何学生),如果关系必须存在的话,需要设置@OneToOne的属性optional
@OneToOne(optional = false)
Car的实例代码:
@Entity(name = "car")
@Table(name = "car",uniqueConstraints = {@UniqueConstraint(columnNames = {"license_plate"})})
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer carId;
@Column(name = "license_plate")
private String licensePlate;
@Column(name = "car_name")
private String carName;
@Column(name = "car_color")
private String carColor;
@OneToOne
@JoinColumn(name = "sid",referencedColumnName = "id")
private Student student;
.......get和set方法
}
Student的实现:
@Entity(name = "student")
@Table(name ="student" ,catalog = "smartdot")
public class Student {
@Id
//生成策略
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "class_num")
private String class_num;
@Column(name = "student_name")
private String student_name;
@Column(
name = "student_num",//对应的列表的名称
unique = true,//是否为唯一
nullable = false,//是否为空
length = 20,//长度
insertable = true,//是否插入表
updatable = true)//是否更新表
private String student_num;
@Temporal(TemporalType.TIMESTAMP)
private Date date;
/**
* fetch 属性为:
* FetchType.EAGER 数据同步加载
* FetchType.LAZY 懒加载,使用的时候才会加载
* mappedBy 属性为:
* 定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,
* 如果类和类之间形成双向关系.我们就需要使用这个属性进行定义,
* 否则可能引起数据一致性的问题。该属性的值是“多”方class里的“一”方的变量名。
*
* targetEntity:
* 定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义。
* cascade 级联操作:
* CascadeType.PERSIST 级联新建
* CascadeType.REMOVE 级联删除
* CascadeType.REFRESH 级联刷新
* CascadeType.MERGE 级联更新
* CascadeType.ALL 级联以上全部
* orphanRemoval :
* 当执行级联操作删除时,如果此为true
* 那么就把关系被维护端的数据实体删除,
* false 不删除,删除的只是关系
*/
@OneToOne(cascade = {CascadeType.ALL},orphanRemoval = true)
@JoinColumn(name = "car_id",referencedColumnName = "carid")
private Car car;
.......get和set方法
}
2、一对多映射(@OneToMany和@ManyTone)
一对多关系即数据库中的一行数据关联另一个数据库中的多行关系,多对一与之相反。举个具体的例子,现在有100本书,有10个学生,分给10个学生那么每一本书肯定属于一个学生,每个学生有多本书。所以他们的对应关系为:
Student-----OneToMany----->Book
Book-----ManyToOne----->Student
@ManyToOne 以在Collection、Set、List、Map上使用,我们可以根据业务需要选择。Collection类是Set和List的父类,在未确定使用Set或List时可使用;
Set集合中对象不能重复,并且是无序的;List集合中的对象可以有重复,并且可以有排序;
Book的实现代码:
@Entity(name = "book")
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
/*
TABLE:使用一个特定的数据库表格来保存主键。
SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
IDENTITY:主键由数据库自动生成(主要是自动增长型)
AUTO:主键由程序控制
*/
private Integer bookId;
@Column(name = "book_name",unique = false,nullable = false)
private String bookName;
@Column(name = "book_price",unique = false,nullable = false)
private String bookPrice;
@Column(name = "book_color",unique = false,nullable = false)
private String bookColor;
@ManyToOne(fetch = FetchType.LAZY,optional = true,cascade = {CascadeType.MERGE,CascadeType.PERSIST })
@JoinColumn(name="sid")
private Student student;
.......get和set方法
}
Student的实现代码:
@Entity(name = "student")
@Table(name ="student" ,catalog = "smartdot")
public class Student {
@Id
//生成策略
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "class_num")
private String class_num;
@Column(name = "student_name")
private String student_name;
@Column(
name = "student_num",//对应的列表的名称
unique = true,//是否为唯一
nullable = false,//是否为空
length = 20,//长度
insertable = true,//是否插入表
updatable = true)//是否更新表
private String student_num;
@Temporal(TemporalType.TIMESTAMP)
private Date date;
@OneToMany(targetEntity = Book.class,fetch=FetchType.EAGER, orphanRemoval = false,cascade={CascadeType.ALL })
@JoinColumn(name = "sid",referencedColumnName = "id")
//注意这里需要初始化
private Set<Book> books = new HashSet<>();
.......get和set方法
}
3、多对多映射关系(@ManyToMany)
多对多关系是关系数据库中两个表之间的一种关系, 该关系中第一个表中的一个行可以与第二个表中的一个或多个行相关。第二个表中的一个行也可以与第一个表中的一个或多个行相关。举个简单的例子:一个学生可以报考多门课程、每一门课程有很多学生报考,学生和课程之间产生了多对多的关系,需要注意的是在多对多关系的中,我们需要让其中的一方放弃对外键的维护,下面的例子中,课程放弃了对外键的维护。
Student的实现:
@Entity(name = "student")
@Table(name ="student" ,catalog = "smartdot")
public class Student {
@Id
//生成策略
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "class_num")
private String class_num;
@Column(name = "student_name")
private String student_name;
@Column(
name = "student_num",//对应的列表的名称
unique = true,//是否为唯一
nullable = false,//是否为空
length = 20,//长度
insertable = true,//是否插入表
updatable = true)//是否更新表
private String student_num;
@Temporal(TemporalType.TIMESTAMP)
private Date date;
@ManyToMany(targetEntity = Course.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinTable(name = "student_course_mapping",
joinColumns = {@JoinColumn(name = "s_id")},
inverseJoinColumns = {@JoinColumn(name = "c_id")})
private Set<Course> courses = new HashSet<>();
.......get和set方法
Course的实现:
@Entity
@Table(name = "course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "course_credit")
private String course_credit;
@Column(name = "course_name")
private String course_name;
//放弃 外键的维护
//mappedBy声明于关系的被维护方,声明的值为关系的维护方的关系对象属性名
@ManyToMany(fetch = FetchType.LAZY,mappedBy = "courses")
private Set<Student> students = new HashSet<>();
.......get和set方法
}
4、单向与双向关联的简介
本文从头到尾所有的示例,使用的都是双向关联。即在关联双方都进行关联声明。而事实上,除了双向关联,还有一种用法是单向关联。即在关联的其中一方进行关联。其实可以简单的理解为Student中有Car的变量,但是Car中没有Student的变量,这时候就是单向关联。这样的话student中有外键只想Car但是Car就不会在产生外键了。
四、具体代码实现数据的保存、修改、查询
1、springboot+jap相关配置:
-
1.1相关的pom.xml,通过maven导入包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.smartdot</groupId>
<artifactId>springbootjap</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>springbootjap Maven Webapp</name>
<!-- FIXME change it to the project‘s website -->
<url>http://www.smartdot.com</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--springboot的相关-->
<!--spring-boot-starter-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring-boot-starter-test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>compile</scope>
</dependency>
<!--spring-boot-starter-aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--spring-boot-configuration-processor-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<!--spring boot web依赖包-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>compile</scope>
</dependency>
<!--jstl包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>compile</scope>
</dependency>
<!-- =========数据库相关======================== -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
<