目录
- 前期回顾
- ORM解决方案
- JPA
- 简介
- JPA的组成技术
- ORM映射元数据
- Java持久化API
- 查询语言(JPQL)
- JPA的优势
- JPA的缺点
- Spring Data JPA
- Spring Data JPA简介
- Spring Data 家族
- Spring Data JPA、JPA和其他框架之间的关系
- SpringBoot整合JPA
- JPA的核心注解
- 1.依赖
- 2.添加配置
- 3.实体类
- 4.创建数据访问层接口继承自JpaRepository<T, ID>接口
- 5.添加用户业务实现类
- 6.测试
- 测试添加
- 测试修改
- 测试简单查询
- 测试删除(简单和批量删除)
- SpringData JPA的查询
- 第一种查询方式:约定方法名
- 示例
- 第二种查询方式:使用JPQL查询
- 示例
- 第三种查询方式:使用Exampler查询
- 示例
- 第四种查询方式:使用原生SQL查询
- 示例
- 分页查询:
- 方式1:PagingAndSortingRepository分页接口,适用于简单的分页查询;
- 示例1
- 示例2:
- 方式2:Specification。自定义方法findAll,适用于动态分页查询;
- 示例
- SpringData JPA配置多数据源
- 同源数据库的多源支持
- 异构数据库多源支持
- 示例
- 多数据源使用案例
- 1.依赖
- 2.配置
- 3.实体类
- 4.配置类
- 5.业务逻辑层
- 6.测试
前期回顾
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的优势
- 简化开发:Jpa提供了一种面向对象的方式来进行数据库操作,开发人员可以使用Java对象来表示数据库表和记录,而不需要编写复杂的SQL语句。这样可以大大简化开发过程,提高开发效率。
- 高度抽象:Jpa提供了一套高度抽象的API,隐藏了底层数据库的细节,开发人员可以更加专注于业务逻辑的实现,而不需要关注数据库的具体实现细节。这样可以降低开发的复杂性,提高代码的可维护性。
- 跨数据库支持:Jpa支持多种数据库,开发人员可以在不同的数据库之间切换,而不需要修改大量的代码。这样可以提高系统的灵活性和可扩展性。
- 自动化的事务管理:Jpa提供了自动化的事务管理机制,开发人员可以使用注解来标识事务的边界,Jpa会自动处理事务的提交和回滚。这样可以简化事务管理的代码,提高系统的稳定性和可靠性。
JPA的缺点
- 学习成本较高:Jpa是一种复杂的技术,需要开发人员具备一定的数据库和ORM(对象关系映射)的知识。对于初学者来说,学习和掌握Jpa可能需要一定的时间和精力。
- 性能问题:由于Jpa是一种高度抽象的技术,它会对数据库的访问和操作进行一定的封装和转换,这可能会导致一定的性能损失。在对性能要求较高的场景下,可能需要使用原生的SQL语句来进行数据库操作。
- 灵活性受限: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.Driverurl: jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456type: com.zaxxer.hikari.HikariDataSourcejpa:hibernate:ddl-auto: updatenaming:physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImplshow-sql: truedatabase-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 {@Resourceprivate UserRepository userRepository;public User saveOrUpdate(User user){User saveUser = userRepository.save(user);return saveUser;}
}
6.测试
测试添加
- 因为设置了主键ID的策略为自增,因此不用传ID就会执行添加操作。
- 也可以传一个不存在的ID,也会执行新增操作,但是主键并不是自定义的主键值,而是自增的主键值
@SpringBootTest
class JpabootApplicationTests {@Resourceprivate UserService userService;@Testvoid 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,会触发修改,若属性为空,会将对应的字段值也修改为空,因此需要特别注意
@Testvoid 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();}
测试
@Testvoid 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);}
测试
@Testvoid 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);}
测试
@Testvoid 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>接口中定义了如下的方法
ListfindAll(Examplevar1);ListfindAll(Examplevar1, 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);}
测试
@Testvoid 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);}
测试
@Testvoid 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层
@Overridepublic Page<SysUser> findUserList(String userName, Integer userRole, Integer currentPageNo, Integer pageSize) throws Exception {Specification<SysUser> specification=new Specification<SysUser>() {@Overridepublic 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 对多数据源的使用,一般分一下为三步:
- 配置多数据源
- 不同源的实体类放入不同包路径
- 声明不同的包路径下使用不同的数据源、事务支持
异构数据库多源支持
比如我们的项目中,即需要对 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.Driverurl: jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8&useSSL=false&username: rootpassword: 123456news:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/news?useUnicode=true&characterEncoding=utf-8&useSSL=false&username: rootpassword: 123456jpa:show-sql: trueproperties:hibernate:hbm2ddl:auto: updatedialect: org.hibernate.dialect.MySQL5InnoDBDialectformat_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 {@Resourceprivate JpaProperties jpaProperties;@Resourceprivate 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")@Primarypublic LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {return builder.dataSource(dataSource).properties(vendorProperties).packages("cn.smbms.pojo") //设置实体类所在位置.persistenceUnit("primaryPersistenceUnit").build();}@Bean(name = "entityManagerPrimary")@Primarypublic EntityManager entityManager(EntityManagerFactoryBuilder builder) {return entityManagerFactoryPrimary(builder).getObject().createEntityManager();}@Bean(name = "transactionManagerPrimary")@PrimaryPlatformTransactionManager 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.测试
略