目录 :
- 一、Spring Data JPA”介绍“
- 二、Spring Data JPA”要进行的操作“ :
- ① 编写ORM “实体类” ( 编写“数据库表”对应的“实体类” + 配置“映射关系”的“注解”)
- ② 编写 Repository 接口 ( 继承“JpaRepository接口” + 其中的“操作数据库”的方法 + 通过“注解”实现的“真正操作数据库”的“方法” + Repository 接口的“要实现的要求”)
- 三、使用Spring Boot 整合 JPA (案例演示)
作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!
该文章参考学习教材为:
《Spring Boot企业级开发教程》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!(侵权可联系我,进行删除,如果雷同,纯属巧合)
- JPA ( Java Persistence API, Java 持久化API )是Sun公司官方提出的 Java 持久化规范,它为Java开发人员提供 了 一种对象/关系映射 的 工具管理Java中 的 关系型数据,
- JPA 主要目的是 简化现有的持久化开发工作 和 整合ORM ( Object Relational Mapping,对象/关系映射) 技术。Spring Data在 JPA规范的基础上,充分利用其优点,提出了 Spring Data JPA模块 对具有 ORM关系数据进行持久化操作。
一、Spring Data JPA”介绍“
- Spring Data JPA 是 Spring 在 ORM 框架、JPA规范 的基础上 封装的一套JPA应用框架,它提供了 增删改查 等常用功能,使开发者可以用较少的代码实现数据操作,同时还易于扩展。
二、Spring Data JPA”要进行的操作“ :
① 编写ORM “实体类” ( 编写“数据库表”对应的“实体类” + 配置“映射关系”的“注解”)
Spring Data JPA框架 是针对具有 ORM ( 对象关系映射 ) 关系 的数据进行操作,所以在使用Spring Data JPA时,首先需要编写一个 实体类 与 数据表进行映射,并且配置好映射关系,示例代码如下 :
package com.myh.chapter_06.domain;import javax.persistence.*;@Entity(name = "t_comment") //指定数据库表中对应的实体类 public class Comment {@Id //表示数据库表中“主键”对应的属性@GeneratedValue(strategy = GenerationType.IDENTITY) //设置主键的策略为: 自动递增private Integer id;@Column(name = "content") //表示数据库表中“字段名”对应的属性 (如果“字段名”和“属性名”一致,则可不用配置该注解)private String content;@Column(name = "author")private String author;@Column(name = "a_id")private Integer aId;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getContent() {return content;}public void setContent(String content) {this.content = content; } public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public Integer getaId() {return aId;}public void setaId(Integer aId) {this.aId = aId;}@Overridepublic String toString() {return "Comment{" +"id=" + id +", content='" + content + '\'' +", author='" + author + '\'' +", aId=" + aId +'}';} }
上述代码定义了一个Spring Data JPA实体类 : Comment,并将该类与数据表t _comment 进行 映射。下面针对上述 代码用到的注解 ( 配置 映射关系 的 注解 )进行 简要说明,具体如下 :
注解 ( 配置“映射关系”的注解 ) 描述 @Entity( )
指定 实体类 对应的 数据库表。( 标注要与数据库做映射的实体类),默认情况下,数据表的名称就是“首字母小写” 的类名。
ps :
不属于默认情况下,可以使用 name 属性指定“实体类”对应的 “数据库表名”。@ld( ) 标注在 类属性 或者 getter方法 上,表示数据库表中“主键”对应的 “属性”。 @GeneratedValue( ) 与 @ld 注解标注在同一位置,用于表示 属性对应主键的生成策略。
ps :
该注解 可省略。Spring Data JPA支持的 主键生成策略 包括有TABLE (使用一个特定的数据库表格来保存主键)、SEQUENCE(不支持主键自增长的数据库主键生成策略)、IDENTITY(主键自增) 和 AUTO
( JPA 自主选择前面3种合适的策略,是默认选项)。
例子如:
@GeneratedValue(strategy = GenerationType.IDENTITY)@Column( ) 标注在 属性 上,指定数据库表中“字段名”对应的“属性名”。用该注解的 name属性 进行“字段名” 和 “属性名” 的 自定义映射。
ps :
当 字段名 和 属性名一致 时,可 “不用配置” 该注解,两者会自动建立好映射关系。
② 编写 Repository 接口 ( 继承“JpaRepository接口” + 其中的“操作数据库”的方法 + 通过“注解”实现的“真正操作数据库”的“方法” + Repository 接口的“要实现的要求”)
下面将编写数据库表对应的 Repository 接口,该接口中包含了 “操作数据库” 的“ 方法 ”,但 真正操作数据库 的 方法 为通过 注解实现 的 数据库操作 (来操作数据库) 。
使用 Spring Data JPA 自定义 Repository 接口 时,必须继承 XxxRepository<T,ID>接口 ,其中的 T 代表要操作的 实体类,ID 代表 “实体类” 中 “主键”属性 的 “类型”。开发中可选择继承了 JpaRepository 接口,
它的 继承结构如下图所示 :
接口 描述 Repository接口 顶级父接口,该接口中没有声明任何方法。 CrudRepository接口 CrudRepository接口是 Repository的继承接口之一,该接口中包含了一些 基本的CRUD方法。 PagingAndSortingRepository接口 PagingAndSortingRepository接口 继承CrudRepository接口 的同时,还提供了 “分页” 和 "排序"两个方法 QueryByExampleExecutor接口 QueryByExampleExecutor接口是进行 封装查询 的 顶级父接口,允许通过 Example 实例执行 复杂条件查询。 JpaRepository接口 JpaRepository接口同时 继承 了 PagingAndSortingRepository 接口和 QueryByExampleExecutor 接口,并额外提供了一些 数据操作方法,自定义Repository接口时通常会选择 继承JpaRepository接口。
注意点 :
JpaRepository接口要 实现的泛型 : JpaRepository<Comment,Integer> : JpaRepository<T , ID> , T 表示该Repository接口对应的“要操作的实体类” , ID表示”实体类“中 主键属性“的类型。
在使用 Spring Data JPA 进行 数据操作时,可以有多种实现方式,主要方式如下 :
① 如果自定义接口 继承 了 JpaRepository接口,则 默认包含 了一些常用的 CRUD 方法。② 自定义 Repository接口 中,可以使用 @Query 注解 配合SQL 语句进行数据的 查、改、删 操作。
③自定义 Repository接口中,可以直接使用 方法名关键字进行查询操作。其中Spring Data JPA中支持的方法名关键字及对应的SQL片段说明。
编写 Repository 接口 要“实现的要求” :
在自定义的 Repository 接口中,针对数据的 变更操作(修改、删除) / 改、查,无论是否使用了 @Query 注解,都必须在方法上 添加 @Transactional注解进行 事务管理 以及添加 @Modifying注解 用于 表示“数据变更”,不添加@Transactional注解时,程序执行就会出现InvalidDataAccessApiUsageException 异常。如果在调用Repository 接口方法的业务层Service 类上已经添加了 @Transactional注解 进行 事务管理,那么Repository 接口文件中就可以省略 @Transactional注解 。
- 例子如 :
CommentRepository.java ( Repository接口 ) :
package com.myh.chapter_06.Repository;import com.myh.chapter_06.domain.Comment; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional;import java.util.List;//数据库表对应的Repository接口 //JpaRepository<Comment,Integer> : JpaRepository<T, ID> , T表示该Repository接口对应的“要操作的实体类” , ID表示”实体类“中”主键属性“的类型 public interface CommentRepository extends JpaRepository<Comment,Integer> {//查询author非空的Comment评论信息public List<Comment> findByAuthorNotNull();//?1中的1表示将方法参数中的"第一个参数值"导入到1的这个位置上// nativeQuery = true : 表示执行的是一个"原生的SQL查询",而不是"JPQL查询"@Query(value = "select * from t_comment c where c.a_id = ?1",nativeQuery = true) //通过该注解可进行“删改查”等操作public List<Comment> getCommentPaged(Integer aid, Pageable pageable);//JPA中进行变更操作时(如: 删、改)要他添加@Transactional 和 @Modifying注解(表示变更操作)@Transactional@Modifying//?1表示将方法参数上的“第一个参数值”导入到1的这个位置上//?2表示将方法参数上的“第二个参数值”导入到2的这个位置上@Query(value = "update t_comment set author = ?1 where c.id = ?2",nativeQuery = true) //使用原生SQL查询public int updateDiscuss(String author, Integer id);@Transactional@Modifying@Query(value = "delete t_comment where c.id = ?1",nativeQuery = true) //使用原生SQL查询public int deleteComment(Integer id);/*** 使用JpaRepository接口中自带的方法进行操作数据库 和 使用Example精确匹配查询条件*/@Override@Transactional<S extends Comment> S save(S s); //调用save()方法,传入一个Comment对象即可存数据到数据库中 }
上面的代码中 JpaRepository<Comment,Integer> : JpaRepository< T , ID> , T 表示该Repository接口对应的“要操作的实体类” , ID 表示 ”实体类“ 中 主键属性“ 的 类型。
JPA 还支持使用 Example 实例进行 复杂条件查询。例如,针对 JpaRepository接口 中已存在的
findAll ( Example<S> var1 )方法进行 查询,示例代码如下。使用 Example ”模糊查询“ :
package com.myh.chapter_06;import com.myh.chapter_06.Repository.CommentRepository; import com.myh.chapter_06.domain.Comment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Example;import java.util.List;@SpringBootTest class Chapter06ApplicationTests {@Autowiredprivate CommentRepository commentRepository;/*** 普通查询(精确查询)*/@Testpublic void JPAExampleTests() {/**针对JpaRepository接口中的findAll()方法进行查询*///1.使用Example精确匹配查询条件Comment comment = new Comment();comment.setAuthor("小明");//创建Example对象Example<Comment> example = Example.of(comment); //返回值类型为“参数的类型”List<Comment> list = commentRepository.findAll(example);System.out.println("精确查询的内容为: "+list);} }
使用 Example “精确查询” :
package com.myh.chapter_06;import com.myh.chapter_06.Repository.CommentRepository; import com.myh.chapter_06.domain.Comment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher;import java.util.List;import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.startsWith;@SpringBootTest class Chapter06ApplicationTests {@Autowiredprivate CommentRepository commentRepository;/*** 模糊查询*/@Testpublic void JPAExampleMatcherTests() {/**针对JpaRepository接口中的findAll()方法进行查询*///2.使用Example精确匹配查询条件Comment comment = new Comment();comment.setAuthor("小"); //模糊查询//创建Example对象// startsWith() : 表示字符串应该以指定的前缀开始(进行模糊查询)ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("author", startsWith());Example example = Example.of(comment, matcher);List list = commentRepository.findAll(example);System.out.println("模糊查询的内容为: "+list);} }
三、使用Spring Boot 整合 JPA (案例演示)
使用Spring Boot 整合 JPA 的 案例演示 :
第一步、导入sql文件,创建springboot项目 :
springbootdata.sql
第二步、在 原有依赖 的基础上加入 JPA依赖 :
<!-- spring-boot-starter-data-jpa 启动依赖器 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
完整依赖展示 : pom.xml :
<?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>2.1.3.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.myh</groupId><artifactId>chapter_07</artifactId><version>0.0.1-SNAPSHOT</version><name>chapter_07</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>RELEASE</version><scope>test</scope></dependency></dependencies><!-- <build>--> <!-- <plugins>--> <!-- <plugin>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-maven-plugin</artifactId>--> <!-- </plugin>--> <!-- </plugins>--> <!-- </build>--></project>
第三步、编写ORM实体类 :
Comment.java (实体类):
package com.myh.chapter_07.domain;import javax.persistence.*;@Entity(name = "t_comment") //指定数据库表中对应的实体类 public class Comment {@Id //表示数据库表中“主键”对应的属性@GeneratedValue(strategy = GenerationType.IDENTITY) //设置主键的策略为: 自动递增private Integer id;@Column(name = "content") //表示数据库表中“字段名”对应的属性 (如果“字段名”和“属性名”一致,则可不用配置该注解)private String content;@Column(name = "author")private String author;@Column(name = "a_id")private Integer aId;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public Integer getaId() {return aId;}public void setaId(Integer aId) {this.aId = aId;}@Overridepublic String toString() {return "Comment{" +"id=" + id +", content='" + content + '\'' +", author='" + author + '\'' +", aId=" + aId +'}';} }
第四步、编写Repository接口 :CommentRepository.java (Repository接口):
package com.myh.chapter_07.Repository;import com.myh.chapter_07.domain.Comment; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional;import java.util.List;//数据库表对应的Repository接口 //JpaRepository<Comment,Integer> : JpaRepository<T, ID> , T表示该Repository接口对应的“要操作的实体类” , ID表示”实体类“中”主键属性“的类型 public interface CommentRepository extends JpaRepository<Comment,Integer> {//查询author非空的Comment评论信息public List<Comment> findByAuthorNotNull();//?1中的1表示将方法参数中的"第一个参数值"导入到1的这个位置上// nativeQuery = true : 表示执行的是一个"原生的SQL查询",而不是"JPQL查询"@Query(value = "select * from t_comment c where c.a_id = ?1",nativeQuery = true) //通过该注解可进行“删改查”等操作public List<Comment> getCommentPaged(Integer aid, Pageable pageable);//JPA中进行变更操作时(如: 删、改)要他添加@Transactional 和 @Modifying注解(表示变更操作)@Transactional@Modifying//?1表示将方法参数上的“第一个参数值”导入到1的这个位置上//?2表示将方法参数上的“第二个参数值”导入到2的这个位置上@Query(value = "update t_comment set author = ?1 where c.id = ?2",nativeQuery = true) //使用原生SQL查询public int updateDiscuss(String author, Integer id);@Transactional@Modifying@Query(value = "delete t_comment c where c.id = ?1",nativeQuery = true) //使用原生SQL查询public int deleteComment(Integer id);}
第五步、编写其他文件 :
DruidSourceConfig.java (配置类)
package com.myh.chapter_07.config;import com.alibaba.druid.pool.DruidDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;@Configuration public class DruidSourceConfig { //关于Druid的配置类@Bean //将返回值对象加入到IOC容器中@ConfigurationProperties("spring.datasource") //将配置文件中spring.datasource开头的属性值进行注入public DataSource getDruid() {return new DruidDataSource();} }
application.properties (全局配置文件)
spring.application.name=chapter_07#配置数据库信息 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT&nullCatalogMeansCurrent=true spring.datasource.username=root spring.datasource.password=root#添加并配置第三方数据源Druid(数据库连接池) spring.datasource.type=com.alibaba.druid.pool.DruidDataSource #初始化时创建的连接数。当应用程序启动时,连接池会立即创建这么多连接。 spring.datasource.initialSize=20 #连接池中最小的空闲连接数。连接池会维护至少这么多的空闲连接,当空闲连接数低于这个数值时,连接池会创建新的连接。 spring.datasource.minIdle=10 #连接池中最大的活动连接数。这表示在任何时候,连接池中的活动(即被使用的)连接数不会超过这个数值。如果所有连接都在使用中,并且达到这个上限,那么新的数据库连接请求将被阻塞或拒绝,直到有连接可用。 spring.datasource.maxActive=100
第六步、编写单元测试类 :
Chapter07ApplicationTests.java (单元测试类)
package com.myh.chapter_07;import com.myh.chapter_07.Repository.CommentRepository; import com.myh.chapter_07.Repository.CommentRepository1; import com.myh.chapter_07.domain.Comment; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable;import java.util.List; import java.util.Optional;import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.startsWith;@SpringBootTest class Chapter07ApplicationTests {@Autowiredprivate CommentRepository commentRepository;//1.使用JpaRepository接口内部方法进行数据操作@Testpublic void selectComment() {Optional<Comment> optional = commentRepository.findById(2);if (optional.isPresent()) {System.out.println(optional.get());System.out.println();}}//2.使用方法关键字进行数据操作@Testpublic void selectCommentBykeys() {List<Comment> list = commentRepository.findByAuthorNotNull();System.out.println(list);}//3.使用@Query注解进行数据操作@Testpublic void selectCommentPaged() {//of(int page, int size) , page表示页码,一般从0开始计数 ; size表示每一页都大小/每一页的数据Pageable pageable = PageRequest.of(0, 3); //List<Comment> list = commentRepository.getCommentPaged(1, pageable);System.out.println(list);}//4.使用Example封装参数进行 "精确查询" 数据操作@Testpublic void selectCommentByExample() {//使用Example进行“精确查询”Comment comment = new Comment();comment.setAuthor("张三");//创建Example对象Example<Comment> example = Example.of(comment); //返回值类型为“参数的类型”List<Comment> list = commentRepository.findAll(example);System.out.println(list);}//5.使用Example封装参数进行 "模糊查询" 数据操作@Testpublic void selectCommentByExampleMatcher() {//使用Example进行“模糊查询”Comment comment = new Comment();comment.setAuthor("莫"); //模糊查询//创建Example对象// startsWith() : 表示字符串应该以指定的前缀开始(进行模糊查询)ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("author", startsWith());Example example = Example.of(comment, matcher);List list = commentRepository.findAll(example);System.out.println(list);}}