前期回顾
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.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>接口中定义了如下的方法
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);
}
测试
@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 对多数据源的使用,一般分一下为三步:
- 配置多数据源
- 不同源的实体类放入不同包路径
- 声明不同的包路径下使用不同的数据源、事务支持
异构数据库多源支持
比如我们的项目中,即需要对 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.测试
略