【SpringBoot整合系列】SpringBoot整合JPA

发布于:2024-04-28 ⋅ 阅读:(24) ⋅ 点赞:(0)

前期回顾

ORM解决方案

框架名称 所属公司 适用场合
Hibernate 创始人:Gavin King
公司:JBOSS 所有
适用与需求变化不多的中小型项目中,比如后台管理系统
Mybatis Apache社区 适用于表关联较多的项目,持续维护开发迭代较快的项目,需求变化较多的项目,如互联网项目
Mybatis-Plus 苞米豆社区, 为猿类崛起而生 MyBatis-Plus是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
Spring Data JPA Spring的母公司是VMware 传统项目或者关系模型较为清晰稳定的项目,建议JPA

JPA

简介

  • JPA是Java Persistence API的简称,中文名Java持久层API,是Sun公司在JDK5.0后提出的一套基于ORM的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。
  • Sun公司引入新的JPA ORM规范出于两个原因:
    • 其一,简化现有Java EE和Java SE应用开发工作;
    • 其二,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。Sun公司希望整合ORM技术,实现天下归一;
  • JPA是在吸收现有ORM框架的基础上发展而来,具有易于使用,伸缩性强等优点。
  • 由于JPA只是一个规范,它本身不执行任何操作。 它需要一个实现。 因此,像Hibernate,TopLink和iBatis这样的ORM工具实现了JPA数据持久性规范。
  • JSR 338,详细内容可参考:https://github.com/javaee/jpa-spec)

JPA的组成技术

ORM映射元数据

  • JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;

Java持久化API

  • 用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。

查询语言(JPQL)

  • 这是持久化操作中很重要的一个方面,通过面向对象的查询语言查询数据,避免程序和SQL语句紧密耦合。

JPA的优势

  1. 简化开发:Jpa提供了一种面向对象的方式来进行数据库操作,开发人员可以使用Java对象来表示数据库表和记录,而不需要编写复杂的SQL语句。这样可以大大简化开发过程,提高开发效率。
  2. 高度抽象:Jpa提供了一套高度抽象的API,隐藏了底层数据库的细节,开发人员可以更加专注于业务逻辑的实现,而不需要关注数据库的具体实现细节。这样可以降低开发的复杂性,提高代码的可维护性。
  3. 跨数据库支持:Jpa支持多种数据库,开发人员可以在不同的数据库之间切换,而不需要修改大量的代码。这样可以提高系统的灵活性和可扩展性。
  4. 自动化的事务管理:Jpa提供了自动化的事务管理机制,开发人员可以使用注解来标识事务的边界,Jpa会自动处理事务的提交和回滚。这样可以简化事务管理的代码,提高系统的稳定性和可靠性。

JPA的缺点

  1. 学习成本较高:Jpa是一种复杂的技术,需要开发人员具备一定的数据库和ORM(对象关系映射)的知识。对于初学者来说,学习和掌握Jpa可能需要一定的时间和精力。
  2. 性能问题:由于Jpa是一种高度抽象的技术,它会对数据库的访问和操作进行一定的封装和转换,这可能会导致一定的性能损失。在对性能要求较高的场景下,可能需要使用原生的SQL语句来进行数据库操作。
  3. 灵活性受限:Jpa提供了一套标准的API,开发人员需要按照这套API来进行开发,这可能会限制一些特定的需求和场景。在一些复杂的业务场景下,可能需要使用原生的SQL语句或其他ORM框架来实现。

Spring Data JPA

Spring Data JPA简介

  • 官网:https://spring.io/projects/spring-data-jpa
  • Spring Data JPA是Spring Data家族的一部分,可以轻松实现基于JPA的存储库。 此模块处理对基于JPA的数据访问层的增强支持。 它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。
  • Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展。
  • 在相当长的一段时间内,实现应用程序的数据访问层一直很麻烦。 必须编写太多样板代码来执行简单查询以及执行分页和审计。 Spring Data JPA旨在通过减少实际需要的工作量来显著改善数据访问层的实现。

Spring Data 家族

  • Spring Data的官网:http://projects.spring.io/spring-data/
  • Spring Data是Spring的一个子项目,旨在统一和简化各类型的持久化存储方式,而不拘泥于是关系型数据库还是NoSQL数据库。
  • Spring Data 的使命是给各种数据访问提供统一的编程接口,从而简化开发人员的代码,提高开发效率。
  • 无论是哪种持久化存储方式,数据库访问对象都会提供对对象的增加、删除、修改和查询的方法,以及排序和分页方法等。
    在这里插入图片描述

Spring Data JPA、JPA和其他框架之间的关系

  • JPA是一套规范,而不是具体的ORM框架。
  • Hibernate、TopLink等是JPA规范的具体实现,这样的好处是开发者可以面向JPA规范进行持久层的开发,而底层的实现则是可以切换的。
  • Spring Data Jpa则是在JPA之上添加另一层抽象,极大地简化持久层开发及ORM框架切换的成本
    在这里插入图片描述

SpringBoot整合JPA

JPA的核心注解

在这里插入图片描述在这里插入图片描述

1.依赖

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

        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2.添加配置

server:
  port: 9999
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    type: com.zaxxer.hikari.HikariDataSource
  jpa:
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

3.实体类

package cn.smbms.pojo;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import javax.persistence.*;
import java.util.Date;

/**
 * @author: zjl
 * @datetime: 2024/4/23
 * @desc:
 */
@Data
@Table(name = "smbms_user")
@Entity
public class User {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @Column(name = "userCode")
    private String userCode;
    private String userName;
    private String userPassword;
    private Integer gender;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
    @Column(name = "phone",nullable = false)
    private String phone;
    private String address;
    @Column(name = "userRole",updatable = false)
    private Integer userRole;
    private Integer createdBy;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date creationDate;
    private Integer modifyBy;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date modifyDate;
}

4.创建数据访问层接口继承自JpaRepository<T, ID>接口

  • JpaRepository拥有基本CRUD功能以及分页功能
    在这里插入图片描述

  • JpaRepository继承自PagingAndSortingRepository。
    在这里插入图片描述

    • 分页接口和实现类
      • Pageable 是Spring Data库中定义的一个接口,用于构造翻页查询,是所有分页相关信息的一个抽象,通过该接口,我们可以得到和分页相关所有信息(例如pageNumber、pageSize等),这样,Jpa就能够通过Pageable参数来得到一个带分页信息的Sql语句
      • PageRequest:Pageable接口的实现类
      • Page接口:用于储存查询的结果集
    • 排序
  • 泛型 T 为任意实体对象,ID为主键的数据类型

public interface UserRepository extends JpaRepository<User, Integer> {
}

5.添加用户业务实现类

@Service
public class UserService {
    @Resource
    private UserRepository userRepository;
    public User saveOrUpdate(User user){
        User saveUser = userRepository.save(user);
        return saveUser;
    }
}

6.测试

测试添加

  • 因为设置了主键ID的策略为自增,因此不用传ID就会执行添加操作。
  • 也可以传一个不存在的ID,也会执行新增操作,但是主键并不是自定义的主键值,而是自增的主键值
@SpringBootTest
class JpabootApplicationTests {
    @Resource
    private UserService userService;
    @Test
    void saveOrUpdate() {
        User user = new User();
        user.setUserName("薇恩");
        user.setUserCode("VN");
        user.setAddress("下路");
        user.setPhone("180xxxx4568");
        user.setUserRole(3);
        User saveResult = userService.saveOrUpdate(user);
        System.out.println(saveResult);
    }
}
  • 执行结果如下:
    Hibernate: select next_val as id_val from hibernate_sequence for update
    Hibernate: update hibernate_sequence set next_val= ? where next_val=?
    Hibernate: insert into smbms_user (address, birthday, createdBy, creationDate, gender, modifyBy, modifyDate, phone, userCode, userName, userPassword, userRole, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    User(id=31, userCode=VN, userName=暗夜射手, userPassword=null, gender=null, birthday=null, phone=180xxxx4568, address=下路, userRole=3, createdBy=null, creationDate=null, modifyBy=null, modifyDate=null)
    

如果不传phone的话,会报错,提示phone属性不能为空

测试修改

  • 传一个已存在的ID,会触发修改,若属性为空,会将对应的字段值也修改为空,因此需要特别注意
    @Test
    void saveOrUpdate() {
        User user = new User();
        user.setId(31);
        user.setUserName("暗夜射手");
        user.setUserCode("VN");
        //user.setAddress("下路");
        user.setPhone("180xxxx4568");
        user.setUserRole(3);
        User saveResult = userService.saveOrUpdate(user);
        System.out.println(saveResult);
    }
  • 因为userRole设置了updatable = false,因此数据中这个字段的值还会是3,而不是2
  • 执行结果如下:
    Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.createdBy as createdb4_0_0_, user0_.creationDate as creation5_0_0_, user0_.gender as gender6_0_0_, user0_.modifyBy as modifyby7_0_0_, user0_.modifyDate as modifyda8_0_0_, user0_.phone as phone9_0_0_, user0_.userCode as usercod10_0_0_, user0_.userName as usernam11_0_0_, user0_.userPassword as userpas12_0_0_, user0_.userRole as userrol13_0_0_ from smbms_user user0_ where user0_.id=?
    Hibernate: update smbms_user set address=?, birthday=?, createdBy=?, creationDate=?, gender=?, modifyBy=?, modifyDate=?, phone=?, userCode=?, userName=?, userPassword=? where id=?
    User(id=31, userCode=VN, userName=暗夜射手, userPassword=null, gender=null, birthday=null, phone=180xxxx4568, address=null, userRole=3, createdBy=null, creationDate=null, modifyBy=null, modifyDate=null)
    

测试简单查询

service层

    public User findUserById(Integer id){
        return userRepository.findById(id).get();
    }

测试

    @Test
    void findUserById() {
        User user = userService.findUserById(1);
        System.out.println(user);
    }

执行结果如下:

Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.createdBy as createdb4_0_0_, user0_.creationDate as creation5_0_0_, user0_.gender as gender6_0_0_, user0_.modifyBy as modifyby7_0_0_, user0_.modifyDate as modifyda8_0_0_, user0_.phone as phone9_0_0_, user0_.userCode as usercod10_0_0_, user0_.userName as usernam11_0_0_, user0_.userPassword as userpas12_0_0_, user0_.userRole as userrol13_0_0_ from smbms_user user0_ where user0_.id=?
User(id=1, userCode=admin, userName=系统管理员, userPassword=0000aaaa, gender=1, birthday=1983-10-10 00:00:00.0, phone=13688889999, address=北京市海淀区成府路207, userRole=1, createdBy=1, creationDate=2013-03-21 16:52:07.0, modifyBy=null, modifyDate=null)

测试删除(简单和批量删除)

service层

    public void delete() {
        userRepository.deleteById(27);

        User user1 = userRepository.findById(28).get();
        User user2 = userRepository.findById(29).get();
        User user3 = userRepository.findById(30).get();
        List<User> users = new ArrayList<>(Arrays.asList(user1,user2,user3));

        userRepository.deleteInBatch(users);
    }

测试

    @Test
    void delete() {
        userService.delete();
    }

执行结果如下:

Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.createdBy as createdb4_0_0_, user0_.creationDate as creation5_0_0_, user0_.gender as gender6_0_0_, user0_.modifyBy as modifyby7_0_0_, user0_.modifyDate as modifyda8_0_0_, user0_.phone as phone9_0_0_, user0_.userCode as usercod10_0_0_, user0_.userName as usernam11_0_0_, user0_.userPassword as userpas12_0_0_, user0_.userRole as userrol13_0_0_ from smbms_user user0_ where user0_.id=?
Hibernate: delete from smbms_user where id=?
Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.createdBy as createdb4_0_0_, user0_.creationDate as creation5_0_0_, user0_.gender as gender6_0_0_, user0_.modifyBy as modifyby7_0_0_, user0_.modifyDate as modifyda8_0_0_, user0_.phone as phone9_0_0_, user0_.userCode as usercod10_0_0_, user0_.userName as usernam11_0_0_, user0_.userPassword as userpas12_0_0_, user0_.userRole as userrol13_0_0_ from smbms_user user0_ where user0_.id=?
Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.createdBy as createdb4_0_0_, user0_.creationDate as creation5_0_0_, user0_.gender as gender6_0_0_, user0_.modifyBy as modifyby7_0_0_, user0_.modifyDate as modifyda8_0_0_, user0_.phone as phone9_0_0_, user0_.userCode as usercod10_0_0_, user0_.userName as usernam11_0_0_, user0_.userPassword as userpas12_0_0_, user0_.userRole as userrol13_0_0_ from smbms_user user0_ where user0_.id=?
Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.createdBy as createdb4_0_0_, user0_.creationDate as creation5_0_0_, user0_.gender as gender6_0_0_, user0_.modifyBy as modifyby7_0_0_, user0_.modifyDate as modifyda8_0_0_, user0_.phone as phone9_0_0_, user0_.userCode as usercod10_0_0_, user0_.userName as usernam11_0_0_, user0_.userPassword as userpas12_0_0_, user0_.userRole as userrol13_0_0_ from smbms_user user0_ where user0_.id=?
Hibernate: delete from smbms_user where id=? or id=? or id=?

SpringData JPA的查询

第一种查询方式:约定方法名

  • Spring Data JPA会根据前缀、中间连接词(Or、And、Like、NotNull等类似SQL中的关键词),内部拼接SQL代理生成方法的实现。
  • 约定方法名一定要符合命名规范
关键词 SQL符号 样例 对应JPQL 语句片段
And and findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals = findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between between xxx and xxx findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan < findByAgeLessThan … where x.age < ?1
LessThanEqual <= findByAgeLessThanEqual … where x.age <= ?1
GreaterThan > findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual >= findByAgeGreaterThanEqual … where x.age >= ?1
After > findByStartDateAfter … where x.startDate > ?1
Before < findByStartDateBefore … where x.startDate < ?1
IsNull is null findByAgeIsNull … where x.age is null
IsNotNull,NotNull is not null findByAge(Is)NotNull … where x.age not null
Like like findByFirstnameLike … where x.firstname like ?1
NotLike not like findByFirstnameNotLike … where x.firstname not like ?1
StartingWith like ‘xxx%’ findByFirstnameStartingWith … where x.firstname like ?1(parameter bound with appended %)
EndingWith like ‘xxx%’ findByFirstnameEndingWith … where x.firstname like ?1(parameter bound with prepended %)
Containing like ‘%xxx%’ findByFirstnameContaining … where x.firstname like ?1(parameter bound wrapped in %)
OrderBy order by findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not <> findByLastnameNot … where x.lastname <> ?1
In in() findByAgeIn(Collection ages) … where x.age in ?1
NotIn not in() findByAgeNotIn(Collection ages) … where x.age not in ?1
TRUE =true findByActiveTrue() … where x.active = true
FALSE =false findByActiveFalse() … where x.active = false
IgnoreCase upper(xxx)=upper(yyyy) findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

示例

dao层

public interface UserRepository extends JpaRepository<User, Integer> {
    User findByUserCode(String userCode);
}

service层

    public Object findByUserCode(String userCode){
        return userRepository.findByUserCode(userCode);
    }

测试

    @Test
    void findByUserCode() {
        userService.findByUserCode("admin");
    }

执行结果如下:

Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.birthday as birthday3_0_, user0_.createdBy as createdb4_0_, user0_.creationDate as creation5_0_, user0_.gender as gender6_0_, user0_.modifyBy as modifyby7_0_, user0_.modifyDate as modifyda8_0_, user0_.phone as phone9_0_, user0_.userCode as usercod10_0_, user0_.userName as usernam11_0_, user0_.userPassword as userpas12_0_, user0_.userRole as userrol13_0_ from smbms_user user0_ where user0_.userCode=?

第二种查询方式:使用JPQL查询

  • JPQL的全称为Java Persistence Query Language,是一种和SQL非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的SQL语言,从而屏蔽不同数据库之间的差异。
  • JPQL和SQL很像,查询关键字都是一样的,唯一的区别是JPQL是面向对象的。
  • JPQL里面不能出现表名,列名,只能出现Java的类名,属性名,并且区分大小写。不能使用select *。
  • JPQL通过@Query注解,封装了执行数据库的查询的相关方法

示例

dao层

    @Query(value = "select u.userName from User u where u.userCode=?1")
    //@Query(value = "select u from User u where u.userCode=?1")
    String selByCode(String userCode);

service层

    public Object findByUserCode(String userCode){
        //return userRepository.findByUserCode(userCode);
        return userRepository.selByCode(userCode);
    }

测试执行结果如下:

Hibernate: select user0_.userName as col_0_0_ from smbms_user user0_ where user0_.userCode=?

第三种查询方式:使用Exampler查询

  • 在 JpaRepository<T, ID>接口中定义了如下的方法
    • List findAll(Example var1);
    • List findAll(Example var1, Sort var2);
  • Example接口,代表的是完整的查询条件。由实体对象(查询条件值)和匹配器(查询方式)共同创建。最终根据实例来findAll即可。
  • 匹配器:ExampleMatcher对象,它是匹配“实体对象”的,表示了如何使用“实体对象”中的“值”进行查询,它代表的是“查询方式”,解释了如何去查的问题。
  • 如:要查询姓“王”的客户,即姓名以“王”开头的客户,该对象就表示了“以某某开头的”这个查询方式。

示例

service层

    public List<User> getUser() {
        User user = new User();
        //user.setUserCode("zhanghua");
        user.setUserRole(3);
        user.setCreatedBy(2);
        Example<User> example = Example.of(user);
        return userRepository.findAll(example);
    }

测试

    @Test
    void getUser() {
        List<User> userList = userService.getUser();
        userList.forEach(System.out::println);
    }

执行结果如下:

Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.birthday as birthday3_0_, user0_.createdBy as createdb4_0_, user0_.creationDate as creation5_0_, user0_.gender as gender6_0_, user0_.modifyBy as modifyby7_0_, user0_.modifyDate as modifyda8_0_, user0_.phone as phone9_0_, user0_.userCode as usercod10_0_, user0_.userName as usernam11_0_, user0_.userPassword as userpas12_0_, user0_.userRole as userrol13_0_ from smbms_user user0_ where user0_.userRole=3 and user0_.createdBy=2
User(id=8, userCode=zhaoyan, userName=赵燕, userPassword=1, gender=1, birthday=1986-03-07 00:00:00.0, phone=18098764545, address=北京市海淀区回龙观小区10号楼, userRole=3, createdBy=2, creationDate=2016-04-21 13:54:07.0, modifyBy=null, modifyDate=null)
User(id=10, userCode=sunlei, userName=孙磊, userPassword=1, gender=2, birthday=1981-01-04 00:00:00.0, phone=13387676765, address=北京市朝阳区管庄新月小区12, userRole=3, createdBy=2, creationDate=2015-05-06 10:52:07.0, modifyBy=null, modifyDate=null)

第四种查询方式:使用原生SQL查询

  • JPQL通过@Query注解,封装了执行数据库的查询的相关方法在

示例

dao层

    @Query(value = "select * from smbms_user where phone = :phone",nativeQuery = true)
    List<User> getUserByPhone(@Param("phone") String phone);

service层

    public  List<User> findUserByPhone() {
        return userRepository.getUserByPhone("13387676765");
    }

测试执行结果如下:

Hibernate: select * from smbms_user where phone = ?
User(id=10, userCode=sunlei, userName=孙磊, userPassword=1, gender=2, birthday=1981-01-04 00:00:00.0, phone=13387676765, address=北京市朝阳区管庄新月小区12, userRole=3, createdBy=2, creationDate=2015-05-06 10:52:07.0, modifyBy=null, modifyDate=null)

分页查询:

方式1:PagingAndSortingRepository分页接口,适用于简单的分页查询;

示例1

service层

    public Page<User> page(Integer currentPage) {
        User user = new User();
        //user.setUserCode("zhanghua");
        user.setUserRole(3);
        user.setCreatedBy(1);
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withIgnoreCase()
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
        Example<User> example = Example.of(user,matcher);
        Pageable pageable = PageRequest.of(currentPage-1,5, Sort.by(Sort.Order.desc("birthday")));
        return userRepository.findAll(example,pageable);
    }

测试

    @Test
    void page() {
        Page<User> pageResult = userService.page(1);
        System.out.println(pageResult);
    }

执行结果如下:

Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.birthday as birthday3_0_, user0_.createdBy as createdb4_0_, user0_.creationDate as creation5_0_, user0_.gender as gender6_0_, user0_.modifyBy as modifyby7_0_, user0_.modifyDate as modifyda8_0_, user0_.phone as phone9_0_, user0_.userCode as usercod10_0_, user0_.userName as usernam11_0_, user0_.userPassword as userpas12_0_, user0_.userRole as userrol13_0_ from smbms_user user0_ where user0_.createdBy=1 and user0_.userRole=3 order by user0_.birthday desc limit ?
Page 1 of 1 containing cn.smbms.pojo.User instances
示例2:

dao层

    Page<User> findByUserCode(String userCode, Pageable pageable);

service层

    public Page<User> page(Integer currentPage) {
        Pageable pageable = PageRequest.of(currentPage-1,5, Sort.by(Sort.Order.desc("birthday")));
        return userRepository.findByUserCode("admin",pageable);
    }

方式2:Specification。自定义方法findAll,适用于动态分页查询;

  • Specification 是Spring Data JPA提供的一种用于构建动态查询条件的接口,可以根据不同的条件动态组合查询条件,自定义的UserRepository 必须实现JpaSpecificationExecutor接口
示例

dao层

Page<SysUser> findAll(Specification<SysUser> specification,Pageable pageable);

service层

@Override
    public Page<SysUser> findUserList(String userName, Integer userRole, Integer currentPageNo, Integer pageSize) throws Exception {
        Specification<SysUser> specification=new Specification<SysUser>() {
            @Override
            public Predicate toPredicate(Root<SysUser> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //断言
                List<Predicate> predicateList=new ArrayList<>();
                if(!StringUtils.isEmpty(userName)){
                    Predicate predicate=criteriaBuilder.like(root.get("userName").as(String.class),"%"+userName+"%");
                    predicateList.add(predicate);
                }
                if(!StringUtils.isEmpty(userRole) && userRole>0){
                    Predicate predicate=criteriaBuilder.equal(root.get("userRole").as(Integer.class),userRole);
                    predicateList.add(predicate);
                }
                return criteriaBuilder.and(predicateList.toArray(new Predicate[0]));
            }
        };
        Sort sort=Sort.by(Sort.Order.desc("id"));
        Pageable pageable=PageRequest.of(currentPageNo-1,pageSize,sort);
        return userRepository.findAll(specification,pageable);
    }

SpringData JPA配置多数据源

同源数据库的多源支持

日常项目中因为使用的分布式开发模式,不同的服务有不同的数据源,常常需要在一个项目中使用多个数据源,因此需要配置 Spring Boot Jpa 对多数据源的使用,一般分一下为三步:

  1. 配置多数据源
  2. 不同源的实体类放入不同包路径
  3. 声明不同的包路径下使用不同的数据源、事务支持

异构数据库多源支持

比如我们的项目中,即需要对 mysql 的支持,也需要对 Mongodb 的查询等。实体类声明@Entity 关系型数据库支持类型,声明@Document 为 Mongodb 支持类型,不同的数据源使用不同的实体就可以了。

示例

interface PersonRepository extends Repository<Person, Long> {}

@Entity
public class Person {}

interface UserRepository extends Repository<User, Long> {}

@Document
public class User {}

如果 User 用户既使用 Mysql 也使用 Mongodb也可以做混合使用

interface JpaPersonRepository extends Repository<Person, Long> {}

interface MongoDBPersonRepository extends Repository<Person, Long> {}

@Entity
@Document
public class Person {}

也可以通过对不同的包路径进行声明,比如 A 包路径下使用 mysql,B 包路径下使用 MongoDB。

@EnableJpaRepositories(basePackages = "com.neo.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.neo.repositories.mongo")
interface Configuration { }

多数据源使用案例

1.依赖

2.配置

spring:
  datasource:
    smbms:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8&useSSL=false&
      username: root
      password: 123456
    news:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/news?useUnicode=true&characterEncoding=utf-8&useSSL=false&
      username: root
      password: 123456
  jpa:
    show-sql: true
    properties:
      hibernate:
        hbm2ddl:
          auto: update
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
        format_sql: true

3.实体类

4.配置类

package cn.smbms.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;

/**
 * @author: zjl
 * @datetime: 2024/4/24
 * @desc:
 */
@Configuration
public class DataSourceConfig {

    @Resource
    private JpaProperties jpaProperties;

    @Resource
    private HibernateProperties hibernateProperties;

    @Bean(name = "smbmsDataSource")
    @Primary
    @ConfigurationProperties("spring.datasource.smbms")
    public DataSource firstDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties("spring.datasource.secondary")
    public DataSource secondDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "vendorProperties")
    public Map<String, Object> getVendorProperties() {
        return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(),
                new HibernateSettings());
    }
}
package cn.smbms.config;

import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;

/**
 * @author: zjl
 * @datetime: 2024/4/24
 * @desc:
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactoryPrimary",
        transactionManagerRef = "transactionManagerPrimary",
        basePackages = {"cn.smbms.dao"})//设置dao(repo)所在位置
public class SmbmsConfig {

    @Resource(name = "smbmsDataSource")
    private DataSource dataSource;

    @Resource(name = "vendorProperties")
    private Map<String, Object> vendorProperties;

    @Bean(name = "entityManagerFactoryPrimary")
    @Primary
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(dataSource)
                .properties(vendorProperties)
                .packages("cn.smbms.pojo") //设置实体类所在位置
                .persistenceUnit("primaryPersistenceUnit")
                .build();
    }

    @Bean(name = "entityManagerPrimary")
    @Primary
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }

    @Bean(name = "transactionManagerPrimary")
    @Primary
    PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    }

}
package cn.smbms.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.Map;

/**
 * @author: zjl
 * @datetime: 2024/4/24
 * @desc:
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "entityManagerFactorySecondary",
        transactionManagerRef = "transactionManagerSecondary",
        basePackages = {"cn.smbms.news.dao"})
public class NewsConfig {

    @Autowired
    @Qualifier("newsDataSource")
    private DataSource dataSource;

    @Autowired
    @Qualifier("vendorProperties")
    private Map<String, Object> vendorProperties;

    @Bean(name = "entityManagerFactorySecondary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(dataSource)
                .properties(vendorProperties)
                .packages("com.example.springbootmultijpa.model")
                .persistenceUnit("secondaryPersistenceUnit")
                .build();
    }

    @Bean(name = "entityManagerSecondary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactorySecondary(builder).getObject().createEntityManager();
    }

    @Bean(name = "transactionManagerSecondary")
    PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
    }
}

5.业务逻辑层

6.测试


网站公告

今日签到

点亮在社区的每一天
去签到