Spring Boot | Spring Boot “整合JPA“

发布于:2024-04-06 ⋅ 阅读:(86) ⋅ 点赞:(0)

在这里插入图片描述

作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!

该文章参考学习教材为:
《Spring Boot企业级开发教程》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章

文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!

(侵权可联系我,进行删除,如果雷同,纯属巧合)


  • JPA ( Java Persistence APIJava 持久化API )是Sun公司官方提出的 Java 持久化规范,它为Java开发人员提供 了 一种对象/关系映射工具管理Java中关系型数据
  • JPA 主要目的是 简化现有的持久化开发工作整合ORM ( Object Relational Mapping,对象/关系映射) 技术。Spring DataJPA规范的基础上,充分利用其优点,提出了 Spring Data JPA模块 对具有 ORM关系数据进行持久化操作

一、Spring Data JPA”介绍“

  • Spring Data JPASpringORM 框架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;
        }
    
        @Override
        public 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 {
    
        @Autowired
        private CommentRepository commentRepository;
    
        /**
         * 普通查询(精确查询)
         */
        @Test
        public 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 {
    
        @Autowired
        private CommentRepository commentRepository;
    
        /**
         * 模糊查询
         */
        @Test
        public 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;
        }
    
        @Override
        public 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 {
    
        @Autowired
        private CommentRepository commentRepository;
    
        //1.使用JpaRepository接口内部方法进行数据操作
        @Test
        public void selectComment() {
            Optional<Comment> optional = commentRepository.findById(2);
            if (optional.isPresent()) {
                System.out.println(optional.get());
                System.out.println();
            }
        }
    
        //2.使用方法关键字进行数据操作
        @Test
        public void selectCommentBykeys() {
            List<Comment> list = commentRepository.findByAuthorNotNull();
            System.out.println(list);
        }
    
        //3.使用@Query注解进行数据操作
        @Test
        public 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封装参数进行 "精确查询" 数据操作
        @Test
        public 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封装参数进行 "模糊查询" 数据操作
        @Test
        public 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);
        }
    
    }