JPA基本步骤:
关于课程实例:
1 数据表中 实体关系分析
理清思路:
- 作者 和 昵称之间 一对一 的关系 一个作者只有一个昵称
- 作者 和 文章之间 一对多 的关系 一个作者可以有多个文章 作者也可以没有文章
- 文章 和 专题之间 多对多 的关系 文章可以被多个专题收录 一个专题也可以收录多个文章
- 文章 和 评论之间 一对多 的关系 文章可以有很多评论 也可以没有评论
包含 :
- 作者更新、注册、删除
- 文章新增、更新、删除
- 评论文章
- 专题新增、更新、删除
- 专题收录
2 JPA使用步骤:
在springboot框架中使用JPA 的步骤
2.1、 SpringBoot 框架构建
2.2 、引入JPA 模块
2.3、 在springboot中配置JPA 也就是在 application.properties 或者 application.yaml 中配置
2.4、 定义实体类
2.5、 实现Repository 接口
开始:
2.1、 SpringBoot 框架构建
2.2 、引入JPA 模块
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xxz</groupId>
<artifactId>JPA-xxz</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>JPA-xxz</name>
<description>JPA-xxz</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.1</version>
</plugin>
</plugins>
</build>
</project>
这个就包含了JPA 要使用的三个模块 引入这个模块我们在springboot中就可以使用JPA了
2.3、 在springboot中配置JPA
这只是表结构不用自己去创建 但是数据库 需要自己去创建。
也就是在 application.properties 或者 application.yaml 中配置
这里用 **application.yaml ** 基本用 update
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring-boot-jpa?useUnicode=ture&characterEncoding=utf-8
username: root
password: xxxx
jpa:
hibernate:
ddl-auto: update # 数据库操作表结构的方式 通常是 update
show-sql: true # 控制台输出sql语句
jpa.hibernate.ddl-auto:
- update : 第一次加载Hibernate 、 会根据我们的 model 类创建新,以后再加载 Hibernate 时,会根据model 类自动更新表结构。**注意:**即使model 类结构发生了变化,表结构不会删除以前的行或列 只会新增一个列。
- create : 每次加载 Hibernate 时 会根据model 类创建新的表 注意:表数据的丢失。 慎用!!!!这个属性
这玩意要是部署到真实环境中 你有可能将数据库搞丢!!!!
- create-drop : 加载 Hibernate 时 会根据 model 类创建一个新的表 sessionFactory 销毁的时候,把对应表给删除掉,相当于临时表。
- none : 不使用这个属性
- validate : 加载 Hibernate 时根据 model 类 来验证我的表结构,不会创建新的表,或者更新表。
2.4、 定义实体类
以作者开始
类创建好后来添加一些属性
package com.xxz.domain;
import java.util.Date;
/**
* @author xxz
* @version 1.0
* @Created on: 2023-12-30 13:44
* @Description: 作者实体类
*/
public class Author {
// 因为 这个实体类模型 是对应你数据库表的 所以我们需要一个主键
private Long id;
// 因为流程图上给作者这个实体类上规划了一个属性 昵称
private String nickName;
//自己可以添加其他自己想添加的属性
private String phone; //手机号
private Date signDate; // 创建时间
}
属性添加好后 这个实体类还不是 数据模型类 model类
还需增加
实体类标识注解 @Entity
主键标识注解 @Id
主键的生成规则注解 @GeneratedValue
@Entity @Id @GeneratedValue
package com.xxz.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import java.util.Date;
@Entity //这个注解是将 实体类 标记成 model数据模型类 不加不会创建表
public class Author {
// 因为 这个实体类模型 是对应你数据库表的 所以我们需要一个主键
@Id // @id 标识 主键
@GeneratedValue //增加主键的生成规则注解
private Long id;
private String nickName;
private String phone;
private Date signDate;
}
然后必须增加空构造 和 get set方法
快捷键 Alt + Ins
增加空构造:
package com.xxz.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import java.util.Date;
/**
* @author xxz
* @version 1.0
* @Created on: 2023-12-30 13:44
* @Description: 作者实体类
*/
注意 会将我们这里的驼峰命名的实体 自动装换为 下划线命名 存进数据表中的字段
@Entity //这个注解是将 实体类 标记成 model数据模型类 不加不会创建表
public class Author {
// 因为 这个实体类模型 是对应你数据库表的 所以我们需要一个主键
@Id // @id 标识 主键
@GeneratedValue //增加主键的生成规则注解
private Long id;
// 因为流程图上给作者这个实体类上规划了一个属性 昵称
private String nickName;
//自己可以添加
private String phone; //手机号
private Date signDate; // 创建时间
public Author() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Date getSignDate() {
return signDate;
}
public void setSignDate(Date signDate) {
this.signDate = signDate;
}
}
到此 我们构建实体类模型 结束!!!
这样 实体类 就会在数据库中新建一个表 可我们如何操作这个表呢 就需要 实现 Repository 接口
2.5、 实现 Repository 接口
创建 AuthorRepostiory
接口后 需要继承 JpaRepository
这是最顶级也是功能最全的 接口 JpaRepository
并且需要传递两个参数
一 是泛型 这个接口对应那个实体类类型
二 是实体类 主键类型
这样就可以使用jpa
进行操作了
package com.xxz.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AuthorRepostiory extends JpaRepository<Author,Long> {
}
开始测试
启动主启动类
这个时候去数据库看 这个 表生成没有
发现表生成了:
测试保存信息:
构建保存信息的 实体对象:
package com.xxz;
import com.xxz.domain.Author;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest
public class JpaXxzApplicationTests {
// 测试保存信息
//构建保存信息的 实体对象
public void saveAuthorTest(){
Author author = new Author();
author.setNickName("xxz");
author.setPhone("12312312312");
author.setSignDate(new Date());
// 到此 实体对象构建好了 下来使用 自定义的接口来调用这个实体对象
}
}
要使用 接口 先注入接口 也就是上面的 AuthorRepostiory
发现 数据存入成功~~
定义实体类:
通常在企业开发中有两个做法:
1 先创建数据库表,然后再根据表来创建实体类。这是传统的数据库建模思想。 也就是 mybatis
形式
2 先编写实体类,然后再生成数据库表。这种采用领域建模思想,更加OOP。建议采用。
关于注解:
主要通过注解来定义实体类
1 @Entity
应用于实体类,表明该实体类被JPA管理,将映射到指定的数据库表。
2 @Table
应用于实体类,通过 **name
**属性指定对应该实体类映射表的名字。就是用于自己指定表的名字
3 @Id
应用于实体类的属性或者属性对应的**getter**
方法,表示该属性映射为数据库表的主键。
4 @GerneratedValue
于@ld一同使用,表示主键的生成策略,通过**strategy**
属性指定。不指定就是默认 auto
JPA提供的生成策略有
-
AUTO 一一 JPA自动选择合适的策略,是默认选项。
-
IDENTITY一一 采用数据库ID自增长的方式来生成主键值,Oracle不支持这种方式。
-
SEQUENCE 一一 通过序列生主键,通过**@SequenceGenerator**注解指定序列名,MySql不支持这种方式。
-
TABLE 一一 采用表生成方式来生成主键值,这种方式比较通用,但是效率低。
5 @ Basic
应用于属性,表示该属性映射到数据库表,
@Entity 标注的实体类的所有属性,默认即为
@Basic 有两个属性:
1、 fetch : 属性的读取策略,有 EAGER 和 LAZY 两种取, 分表标识主动抓取和延迟抓取【懒加载】默认为
EAGER
2、 optonal: 表示该属性是否允许为 null, 默认值为 true
@Basic(fetch =FetchType.LAZY)标注某属性时,表示只有调用Hibernate对象的该属性的 get 方法时,才会从数据库表中查找对应该属性的字段值
6 @Lob
也就是 大字段 类型都会使用 同时使用@Basic 且为懒加载
应用到实体类属性上,表示将属性映射成数据库支持的大对象类型,Clob 或者 Blog。其中:
-
Clob (Character Large Ojects) 类型是长字符串类型,
java.sql.Clob 、Character[ ] 、char[ ] 和 string 将被映射为 Clob 类型
-
Blog (Binary Large Objects) 类型是字节类型,java.sql.Blob 、 Byte[ ] 、byte[ ] 和
实现了 Serializable 接口的类型将被映射为 Blob 类型。
因为这两种类型的数据一般占用的内存空间比较大,所以通常使用 延迟加载 的方式,与 @Basic 标注同时使用,
设置加载方式为 FetchType.LAZY。
7 @Column
应用于实体类的属性,可以指定数据库表字段的名字和其他属性。其属性包括:
-
name : 表示数据库表中该字段的名称,默认情形属性名称一致.
-
nullable : 表示该字段是否允许为 null ,默认为 true
-
unique : 表示该字段是否是唯一标识,默认为 false
-
length : 表示该字段的大小,仅对 string 类型的字段有效
-
insertable : ** 表示在ORM框架执行插入操作时,该字段是否应出现INSETRT语句中**,默认为 true
-
updateable: 表示在ORM框架执行更新操作时,该字段是否应该出现在UPDATE语句中默认为 true 。对于一经创建就
不能更改的字段,该属性非常有用,比如 email 属性。
-
-
columnDefinition
- 表示该字段在数据库中的实际类型。通常ORM框架可以根据属性类型自动判断数据库中字段的类型,但是依然有些
例外:
-
Date 类型 无法确定数据库中字段类型究竟是 DATE TIME 还是 TIMESTAMP
-
String 的默认映射类型为 VARCHAR 如果希望将 String 类型映射到特定的数据库的 BLOB 或 TEXT 字段类型
则需要进行设置
8 @Transient
表示不会持久化到数据库中
应用在实体类属性上,表示该属性不映射到数据库表,JPA会忽略该属性.
9 @Temporal
应用到实体类属性上,表明该属性映射到数据库是一个时间类型,具体定义:
-
@Temporal(TemporalType.DATE) 映射为日期 // date (只有日期)
-
@Temporal(TemporalType.TIME) 映射为日期 // time (是有时间)
-
@Temporal(TemporalType.TIMESTAMP) 映射为日期 // date time
(日期+时间)
-
-
package com.xxz.pojo;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
// 使用jpa 就是省去了手动创建表的 是通过反向创建表 遵从驼峰命名规则 所有的字段名要和 页面的name属性保持一致
@Entity // 数据库对应
@Table(name = "t_blog") //自己指定生成表的名字
public class Blog {
@Id
@GeneratedValue //id生成策略 默认自动生成
private Long id; //主键
private String title; //标题
@Basic(fetch = FetchType.LAZY) //LAZY 懒加载 就是当你使用的时候才会加载这个值不使用的时候不加载 因为值比较大 没事就加载会耗资源
@Lob //因为String是默认最大的varchar类型 最大255 可实际的数据内容是远大于255的所以可以数据库级别设置longtext 或者这样修改 二选一即可
private String content; // 内容
private String firstPicture; // 首图
private String flag; // 标记
private Integer views; // 浏览次数 去blogserviceimpl初始化浏览量为 0
private boolean appreciation; // 赞赏开启
private boolean shareStatement; // 版权开启 即转载说明
private boolean commentabled; // 评论开启
private boolean published; // 发布 为1 发布 0
private boolean recommend; // 是否推荐
@Temporal(TemporalType.TIMESTAMP) // 对应生成时间格式 包括 日期和时间 这个比较全
private Date createTime; // 创建时间
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime; // 更新时间
@ManyToOne // 多对一 几个博客对应一个类型
private Type type;
@ManyToMany(cascade = {CascadeType.PERSIST}) // 多对多
private List<Tag> tags = new ArrayList<>();
@ManyToOne // 多对一
private User user;
@OneToMany(mappedBy = "blog") // 一对多
private List<Comment> comments = new ArrayList<>();
@Transient // 不会进行数据库的操作 这个不会保存进数据库 只是一个正常的属性值 只会正常映射
private String tagIds; // 查询tag 使用集合的时候需要
private String description;
// 空的无参构造函数
public Blog() {
}
// 省略 get set tostring 方法
}
关于构造函数
也就是 创建实体类对象的时候 必须有那个 空的无参构造函数
JPA中对象是由Hibernate为我们创建的,当我们通过ID来获取某个实体的时候,这个实体给我们返回了这个对象的创建是
由Hibernate内部通过反射技术来创建的,反射的时候用到了默认的构造函数,所以这时候必须给它提供一个public的无参
构造函数。
Repository 接口
1 接口层次于方法
Spring Data JPA简化了持久层的操作,开发者只需声明持久层接口,而不需要实现该接口。SpringData JPA内部会根据
不同的接口方法,采用不同的策略自动生成实现。
而开发者声明持久层接口,需要直接或者间接的方式继承Repository接口,从而使自定义的持久层拥有了持久层的操作
能力。
Repository 接口:
-
Repository 是Spring Data的核心接口,最顶层接口,不包括任何方法,他的目的是为了统所有Repository的类型,且
让组件扫描的时候自动识别。
扩展的Repository接口
-
CrudRepository 继承了Repository,提供了曾删改查方法, 可以直接调用。
-
PagingAndSortingRepository 继承了CrudRepository,提供了分页和排序两个方法。
-
JpaRepository 继承了PagingAndSortingRepository,针对于JPA技术的接口,提供了
flush(),saveFlush(0),deletelnBatch()等方法。
1 CrudRepository
完整定义:
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S var1); //将一个对象持久化到数据库中
<S extends T> Iterable<S> save(Iterable<S> var1); ///将一组对象持久化到数据库中
T findOne(ID var1); //根据id查找并返回一个对象
boolean exists(ID var1); // 判断某个id是否存在
Iterable<T> findAll(); //返回所有对象
Iterable<T> findAll(Iterable<ID> var1); //根据一组id 返回对应的对象
long count(); //返回共有多少条数据
void delete(ID var1); //根据id删除某个对象
void delete(T var1); //删除某个对象
void delete(Iterable<? extends T> var1); //删除某一组对象
void deleteAll(); //删除所有对象
}
2 PagingAndSortingRepository
public interface PagingAndSortingRepository<T, ID extends Serializable>extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort var1); //根据某个排序获取所有数据
Page<T> findAll(Pageable var1); //根据分页信息获取某一页的数据
}
3 JpaRepository
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
List<T> findAll(); //឴获取所有数据,以List的方式返回
List<T> findAll(Sort var1); //根据某个排序获取所有数据,以List的方式返回
List<T> findAll(Iterable<ID> var1); //根据一组id返回对应的对象,以List的方式返回
<S extends T> List<S> save(Iterable<S> var1); //将一组对象持久化到数据库中以List的方式返回
void flush(); //将修改更新到数据库
<S extends T> S saveAndFlush(S var1); //保存数据并将修改更新到数据库
void deleteInBatch(Iterable<T> var1); //批量删除数据
void deleteAllInBatch(); //批量删除所有数据
T getOne(ID var1); //根据id查找并返回一个对象
}
2 使用步骤:
pository**
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
List<T> findAll(); //឴获取所有数据,以List的方式返回
List<T> findAll(Sort var1); //根据某个排序获取所有数据,以List的方式返回
List<T> findAll(Iterable<ID> var1); //根据一组id返回对应的对象,以List的方式返回
<S extends T> List<S> save(Iterable<S> var1); //将一组对象持久化到数据库中以List的方式返回
void flush(); //将修改更新到数据库
<S extends T> S saveAndFlush(S var1); //保存数据并将修改更新到数据库
void deleteInBatch(Iterable<T> var1); //批量删除数据
void deleteAllInBatch(); //批量删除所有数据
T getOne(ID var1); //根据id查找并返回一个对象
}
慢慢更新 !!!!!