快速上手SpringBoot整合JPA

在公司刚刚加入一个新的项目组, 发现新项目组使用的ORM框架我没有系统的使用过(JPA), 于是去网上查找了相关资料发现没有很系统的讲解, 便想要系统的记录一下方便日后复习

因为学习这个框架一切都是由生产实际展开的没有了解底层, 所以更加适合快速上手

默认大家对SpringBoot, 构建工具, lombok, Mysql有所了解以及使用

数据源使用: mysql 8.0

一. Spring Data, JPA, SpringDataJPA(JPA)的简单介绍

  1. Spring Data: Spring 的一个子项目。用于简化数据库访问,支持NoSQL关系数据库存储。目标是使数据库操作变得简单便捷.
  2. JPA: Java Persistence API 叫做java持久化接口规范, 定义了java操作数据库的模式.
  3. Spring Data JPA: 是Spring Data项目的一部分, 基于JPA规范标准, 提供了一套简洁的API和注解, 用于与关系型数据库进行交互, 使我们可以通过简单的Java对象来表示数据库表, 并且通过继承提供的框架类来执行通用的CRUD操作.
  4. JPA是Java定义的ORM规范, Hibernate是这种ORM规范的实现之一, SpringData JPA是对JPA这种规范的进一步封装与抽象, 底层依然使用了Hibernate技术实现, 简单来说就是进一步增强了对数据库的操作, 将开发人员从繁重的数据库操作以及SQL中解放出来, 可以专注于业务.

二. SpringBoot 整合 SpringDataJPA

1. 导入相关依赖, 构建项目为SpringBoot项目

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/>
    </parent>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- jpa 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- lombok 依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

2. 创建测试数据库以及创建测试表结构与信息

此处数据库名为jpa_learn, 测试表名为animal.

3. 编写配置信息

在配置文件application.yaml或application.properties中添加以下信息.

server.port=9000
#mysql驱动
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#mysql的jdbc连接
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jpa_learn
#用户名
spring.datasource.username=root
#密码
spring.datasource.password=${your.pass}
#jpa配置, 显示执行的sql语句
spring.jpa.show-sql=true
#设置hibernate的方言 MySQL5Dialect
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect

4. 创建测试包结构

5. 创建与Mysql表映射的Java实体类

@Table(name = "animal")
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public class Animal {
    @Id
    @Column(name = "id")
    private Integer id;
    
    @Column(name = "name")
    private String name;
    
    @Column(name = "sex")
    private String sex;
    
    @Column(name = "age")
    private Integer age;
}

Animal类整体上有两种注解标注的, 一类是lombok提供的, 另一部分是JPA提供的, 分别解释:

        @Table(name="") : 标注了当前类是与数据库哪张表映射的.

        @Entity : 标注了当前类是与数据库表映射的实体类

        @Id : 这个属性在数据库表中是主键

        @Column : 当前属性是与数据库中哪个字段对应的

三. Spring Data JPA的简单使用

完成了上述准备工作就可以使用JPA框架来进行最基础的数据库操作了, 此处以查询操作为例演示

JPA提供了多种操作数据库的方式, 接下来注意解析

1. 继承JpaRepository<T, ID>

继承类框架可以直接获得常见的数据库操作, 其中 T 是要操作的数据库表所映射的实体类, ID顾名思义是表主键的映射类型

public interface AnimalDao extends JpaRepository<Animal, Integer> {
    
}
@SpringBootTest
public class JPATest {
    @Autowired
    private AnimalDao animalDao;

    @Test
    public void test01() {
        animalDao.findAll().forEach(System.out::println);
    }
}


2. 使用JPQL进行查询

JPQL: JPQL是一种强大的面向对象查询语言,专为JPA框架设计。它允许开发者通过面向对象的方式检索实体数据,而无需关心底层数据库的具体实现。与传统SQL不同,JPQL直接操作Java实体类及其属性,而非数据库表和列。这种抽象层使开发者能够编写与数据库无关的查询,增强了应用程序的可移植性。JPQL查询会被JPA提供者转换为针对特定数据库的SQL语句执行,从而实现了对象关系映射(ORM)框架的核心功能.

public interface AnimalDao extends JpaRepository<Animal, Integer> {
    
    @Query(value = "select a from Animal a")
    List<Animal> getAllAnimals();
    
}



如果需要查询部分字段或者我们希望查询出的结果由一个Dto来接收我们可以直接通过构造器来实现.

public interface AnimalDao extends JpaRepository<Animal, Integer> {

    @Query(value = "select a from Animal a")
    public List<Animal> getAllAnimals();

    @Query(value = "select new com.yangyang.jpa.dto.AnimalDto(a.name) from Animal a")
    public List<AnimalDto> getAllAnimalDtos();
}

3. 使用原生SQL进行查询

@Query() 注解有一个属性叫做 nativeQuery 默认值为false使用JPQL查询, 如果指定为true证明是一个原生SQL

public interface AnimalDao extends JpaRepository<Animal, Integer> {

    @Query(value = "select a from Animal a")
    public List<Animal> getAllAnimals();

    @Query(value = "select new com.yangyang.jpa.dto.AnimalDto(a.name) from Animal a")
    public List<AnimalDto> getAllAnimalDtos();

    @Query(value = "select * from animal", nativeQuery = true)
    public List<Animal> getAnimalsBySql();
}

4. 根据方法名查询

Spring Data 根据 方法名的结构 解析出查询逻辑,生成对应的数据库查询.

方法名结构: 

<动词><By><属性名1><关键词><属性名2><排序>

动词:

前缀说明
findBy / getBy / readBy查询单个或多个实体
existsBy是否存在,返回 boolean
countBy统计数量
deleteBy删除实体
removeBy删除(和 deleteBy 一样)
queryBy同 findBy,可自定义语义

条件关键字: 

关键字说明示例
AndfindByUsernameAndStatus
OrfindByUsernameOrEmail
Between范围findByAgeBetween(int min, int max)
LessThan / GreaterThan小于 / 大于findByAgeGreaterThan(int age)
Before / After时间前 / 时间后findByCreatedAtAfter(Date date)
IsNull / IsNotNull判空findByEmailIsNull()
Like模糊匹配(需加 %)findByUsernameLike("%admin%")
Containing包含(自动加 %)findByUsernameContaining("adm")
StartingWith / EndingWith前缀 / 后缀匹配findByUsernameStartingWith("a")
In / NotIn在集合中findByIdIn(List<Long> ids)
True / False布尔判断findByEnabledTrue()

排序规则

语法示例
OrderBy<Field>AscfindByStatusOrderByAgeAsc()
OrderBy<Field>DescfindByStatusOrderByCreatedAtDesc()

限制返回条数

语法示例
findTop1By...查询符合条件的第一条记录
findFirst3By...查询前三条

返回类型

返回类型说明
User查询单个结果(若多条结果会报错)
Optional<User>单条结果,防止空指针
List<User>多条结果
Page<User>分页结果(需传入 Pageable)
booleanexistsBy...
longcountBy...

测试

public interface AnimalDao extends JpaRepository<Animal, Integer> {

    @Query(value = "select a from Animal a")
    public List<Animal> getAllAnimals();

    @Query(value = "select new com.yangyang.jpa.dto.AnimalDto(a.name) from Animal a")
    public List<AnimalDto> getAllAnimalDtos();

    @Query(value = "select * from animal", nativeQuery = true)
    public List<Animal> getAnimalsBySql();


    public Animal findById(int id);


    // 根据id, 姓名, 年龄范围来进行查询
    Animal findByIdAndNameContainingAndAgeBetween(Integer id, String name, Integer ageAfter, Integer ageBefore);

}
    @Test
    public void test06() {
        System.out.println(animalDao.findByIdAndNameContainingAndAgeBetween(1, "狗", 1, 100));
    }

其实简单来说就是首先规定进行的数据操作, 之后规定属性和条件, 之后去定义属性条件之间的拼接方式
 

四. 动态查询

刚刚说完了简单的数据库查询, 现在来说一下重要的动态查询, 动态查询无非就是构建请求条件和排序规则的过程, 在Mybatis中我们通过动态标签来实现, 在MP中我们构建QueryWrapper来实现

1. Specification

Specification是一个函数式接口, 由这个接口可以定义一个动态查询的条件, 因为是一个函数式接口我们可以通过lambda表达式的方式来构建

首先我们需要将dao层去继承JPASpecificationExecutor<T>接口, 其中定义了多种支持传入Specification的方法, 也就是动态查询

    @Test
    public void test07() {
        AnimalDto animalDto = AnimalDto.builder().name("狗").minAge(1).maxAge(100).build();

        Specification<Animal> specification =
                (root, query, criteriaBuilder) -> {
                    List<Predicate> predicates = new ArrayList<>();

                    if (animalDto.getName() != null) {
                        Predicate predicate = criteriaBuilder.like(root.get("name"), "%" + animalDto.getName() + "%");
                        predicates.add(predicate);
                    }
                    if (animalDto.getMinAge() != null) {
                        Predicate predicate = criteriaBuilder.greaterThanOrEqualTo(root.get("age"), animalDto.getMinAge());
                        predicates.add(predicate);
                    }
                    if (animalDto.getMaxAge() != null) {
                        Predicate predicate = criteriaBuilder.lessThanOrEqualTo(root.get("age"), animalDto.getMaxAge());
                        predicates.add(predicate);
                    }
                    return query.where(predicates.toArray(new Predicate[0])).getRestriction();
                };

        List<Animal> all = animalDao.findAll(specification);
        all.forEach(System.out::println);
    }

2. 公司框架使用的是通过 ...QueryCriteria配合工具类来构建查询语句

请求直接传入...QueryCriteria, 工具类通过反射构建出查询条件Specification直接进行查询

五. 分页查询

介绍两个关键的接口/类, Pageable(接口)主要用于接收前端传来的分页数据, PageRequest(类)主要构建分页数据

PageRequest: 

    @Test
    public void test08() {
        PageRequest pageRequest = PageRequest.of(0, 10);
        Page<Animal> all = animalDao.findAll(pageRequest);
        List<Animal> content = all.getContent();
        content.forEach(System.out::println);
        System.out.println(all.getTotalElements());
        System.out.println(all.getTotalPages());
    }

Pageable:

添加web依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

写controller直接接收Pageable作为参数, 在service中直接调用dao方法传入pageable参数查询返回Page其中封装了分类结果

@Service
public class AnimalServiceImpl implements AnimalService {
    
    @Autowired
    private AnimalDao animalDao;
    
    @Override
    public List<Animal> getAnimalByPage(Pageable pageable) {
        Page<Animal> all = animalDao.findAll(pageable);
        return all.getContent();
    }
}

六. 更加复杂一点的, 结合条件查询的分页查询

@Service
public class AnimalServiceImpl implements AnimalService {

    @Autowired
    private AnimalDao animalDao;

    @Override
    public List<Animal> getAnimalByPage(Pageable pageable) {
        Page<Animal> all = animalDao.findAll(new Specification<Animal>() {
            @Override
            public Predicate toPredicate(Root<Animal> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.like(root.get("name"), "%狗%");
            }
        }, pageable);
        return all.getContent();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值