Spring Data JPA入门到放弃
参考文档:SpringData JPA:一文带你搞懂 - 知乎 (zhihu.com)
一、 前言
1.1 概述
Java持久化技术是Java开发中的重要组成部分,它主要用于将对象数据持久化到数据库中,以及从数据库中查询和恢复对象数据。在Java持久化技术领域,Java Persistence API (JPA) 和 Spring Data JPA 是两个非常流行的框架。本文将对这两个框架进行简要介绍,以帮助您更好地理解它们的作用和使用方法。
1.2 JPA简介
Java Persistence API (JPA) 是一种基于 ORM (Object-Relational Mapping) 技术的 Java EE 规范(JPA是一种规范,而不是具体的框架)。它主要用于将 Java 对象映射到关系型数据库中,以便于对数据进行持久化操作。
JPA 主要由三个部分组成,分别是 Entity、EntityManager 和 Query。其中 Entity 用于描述 Java 对象和数据库表之间的映射关系;EntityManager 用于管理实体对象的生命周期和完成实体对象与数据库之间的操作;Query 用于查询数据。
JPA 支持多种底层实现,如 Hibernate、EclipseLink 等。在使用时,只需要引入相应的实现框架即可。 总结如下:
- JPA(Java Persistence API)是为Java EE平台设计的一种ORM解决方案。
- JPA提供了一些标准的API以及关系映射的元数据,使得Java开发人员可以在没有具体SQL编程经验的情况下,通过简单的注解配置实现对数据的访问和操作。
- JPA提供了对事务的支持,允许Java开发人员进行基于POJO的开发,在运行时将这些POJO映射成关系数据库表和列,最大限度地减少了Java开发者与数据库的交互。
1.3 Spring Data JPA简介
Spring Data JPA 是 Spring 框架下的一个模块,是基于 JPA 规范的上层封装,旨在简化 JPA 的使用。
Spring Data JPA 提供了一些常用的接口,如 JpaRepository、JpaSpecificationExecutor 等,这些接口包含了很多常用的 CRUD 操作方法,可直接继承使用。同时,Spring Data JPA 还提供了基于方法命名规范的查询方式,可以根据方法名自动生成相应的 SQL 语句,并执行查询操作。这种方式可以大大减少编写 SQL 语句的工作量。
除了基础的 CRUD 操作外,Spring Data JPA 还提供了一些高级功能,如分页、排序、动态查询等。同时,它也支持多种数据库,如 MySQL、PostgreSQL、Oracle 等。 总结如下:
- Spring Data JPA 是 Spring Data 项目家族中的一员,它为基于Spring框架应用程序提供了更加便捷和强大的数据操作方式。
- Spring Data JPA 支持多种数据存储技术,包括关系型数据库和非关系型数据库。
- Spring Data JPA 提供了简单、一致且易于使用的API来访问和操作数据存储,其中包括基本的CRUD操作、自定义查询方法、动态查询等功能。
- Spring Data JPA 也支持QueryDSL、Jinq、Kotlin Query等其他查询框架
二,快速开始
2.1 配置环境
- 使用 Spring Data JPA 需要在项目中配置相关依赖项和数据源。
- Spring Data JPA 支持的数据库类型包括 MySQL、PostgreSQL、Oracle、MongoDB 等。
2.2 添加依赖
- 在项目的 pom.xml 文件中添加如下 Spring Data JPA 相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
?
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
- 如果使用其他数据库类型,可以替换?
mysql-connector-java?依赖,并相应地调整数据源配置。
2.3 创建实体类
- 在项目中创建实体类,用于映射数据库表和列。
- 实体类需要使用 @Entity 注解进行标记,并且需要指定主键和自动生成策略,@Table可以指定该实体对应生成数据表的名称
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// ...
// 省略 getter 和 setter 方法
}
2.4 创建Repository接口
- 在项目中创建 Repository 接口,用于定义数据访问方法。
- Repository 接口需要继承自?
JpaRepository?接口,并且需要使用?@Repositor?注解进行标记,例如:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
//自定义方法
User findByName(String name);
}
- 在示例中,我们定义了一个名为?
UserRepository?的接口,它继承自?JpaRepository?接口,泛型参数分别为实体类型和主键类型,并且新增了一个自定义查询方法?findByName。 JpaRepository?接口包含了基本的增删改查方法,且增和改都是调用save方法(通过已存在id来判断)

2.5 运行示例应用程序
- 编写示例代码并运行应用程序,以验证 Spring Data JPA 的功能和使用方法。
- 示例代码可以是简单的控制台程序,也可以是 Web 应用程序。下面是一个基于 Spring Boot 的 Web 应用程序的示例代码:
@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
private UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
User user = new User();
user.setName("Alice");
user.setEmail("alice@example.com");
userRepository.save(user);
User savedUser = userRepository.findByName("Alice");
System.out.println(savedUser);
}
- 在示例代码中,我们使用?
@SpringBootApplication?注解标记了应用程序入口类,并且在?main?方法中启动了应用程序。 CommandLineRunner?接口的?run?方法用于定义初始化逻辑,在示例中我们创建了一个名为?Alice?的用户,并将其保存到数据库中,随后使用?findByName?方法查询并输出该用户信息。
三、实体映射
在使用 Spring Data JPA 时,需要定义实体类和数据表之间的映射关系。下面介绍常用的实体映射注解。
3.1 Entity注解
@Entity?注解用于标记实体类,表示该类会被映射到数据库中的一个表。
示例代码:
@Entity
public class User {
// 省略属性和方法
}
3.2 Table注解
@Table?注解用于标注实体类与数据库表之间的映射关系,并可以指定表的名称、唯一约束等信息。
示例代码:
@Entity
@Table(name = "user")
public class User {
// 省略属性和方法
}
3.3 Column注解
@Column?注解用于标注实体类属性与数据表字段之间的映射关系,并可以指定字段名称、长度、精度等信息。(指定数据库列的长度和是否为null)
示例代码:
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
@Column(name = "user_name", length = 20, nullable = false)
private String userName;
// 省略其他属性和方法
}
3.4 Id注解
@Id?注解用于标注实体类属性作为主键,通常与?@GeneratedValue?注解一起使用指定主键生成策略。
示例代码:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// 省略其他属性和方法
}
3.5 GeneratedValue注解
@GeneratedValue?注解用于指定主键生成策略,通常与?@Id?注解一起使用。
示例代码:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 省略其他属性和方法
}
3.6 关系映射
关系映射通常包括一对一、一对多和多对多等关系。在 Spring Data JPA 中,可以使用?@OneToOne、@OneToMany?和?@ManyToMany?注解来标注关系映射。这些注解通常与?@JoinColumn?注解一起使用,用于指定关联的外键列。
示例代码:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//一(用户user)对多 (地址address)
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Address> addresses;
// 省略其他属性和方法
}
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//多 (地址address)对一(用户user)
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
// 省略其他属性和方法
}
在上例中,User?和?Address?之间是一对多的关系,所以在?User?实体类中使用了?@OneToMany?注解,在?Address?实体类中使用了?@ManyToOne?注解。mappedBy?属性用于指定关联的属性名称,这里是?user,表示?Address?实体类中的?user?属性与?User?实体类中的?addresses?属性相对应。cascade?属性表示级联操作,这里使用?CascadeType.ALL?表示在删除?User?实体时同时删除其关联的所有?Address?实体。@JoinColumn?注解用于指定外键名称,这里是?user_id,表示?Address?表中的?user_id?列与?User?表中的主键相对应。
四、Repository接口
Repository?接口是 Spring Data JPA 的核心接口之一,它提供了基本的增删改查方法和自定义查询方法,以及分页和排序等功能。在使用时需要继承?Repository?接口并指定对应的实体类和主键类型。
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 省略基本增删改查方法和自定义查询方法
}
4.1 各类Repository接口
4.1.1 增删改查方法接口:Repository?
在继承?Repository?接口后,会默认提供基本的增删改查方法,无需额外的代码实现即可使用。常用的方法如下:
| 方法名 | 描述 |
|---|---|
| T save(T entity) | 保存实体对象 |
| Iterable saveAll(Iterable entities) | 批量保存实体对象 |
| Optional findById(ID id) | 根据主键获取实体对象 |
| boolean existsById(ID id) | 判断是否存在特定主键的实体对象 |
| Iterable findAll() | 获取所有实体对象 |
| Iterable findAllById(Iterable ids) | 根据主键批量获取实体对象 |
| long count() | 获取实体对象的数量 |
| void deleteById(ID id) | 根据主键删除实体对象 |
| void delete(T entity) | 删除实体对象 |
| void deleteAll(Iterable<? extends T> entities) | 批量删除实体对象 |
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 保存用户
User save(User user);
// 根据主键获取用户
Optional<User> findById(Long id);
// 获取所有用户
Iterable<User> findAll();
// 根据主键删除用户
void deleteById(Long id);
}
4.1.2 分页和基本查询接口:JpaRepository
1,特点:
-
提供了基本的CRUD操作:JpaRepository接口继承自PagingAndSortingRepository和CrudRepository接口,因此提供了基本的CRUD操作,包括保存、更新、删除、查询等。
-
支持分页和排序:JpaRepository接口继承自PagingAndSortingRepository接口,因此支持分页和排序功能,可以方便地进行分页查询和结果排序。
-
自动生成查询方法:JpaRepository接口支持使用方法名来自动生成查询方法,无需手动编写SQL语句,可以根据方法名的规则来自动生成查询方法。
-
提供了一些其他便利方法:JpaRepository接口还提供了一些其他便利方法,如批量操作、统计记录数等。?
2,父接口继承结构:

?4.1.3 动态查询接口:JpaSpecificationExecutor
1,介绍
JpaSpecificationExecutor接口定义了一些方法,用于执行基于JPA规范的动态查询操作,包括:
-
<T>?List<T>?findAll(Specification<T>?spec):根据给定的Specification对象进行查询,返回符合条件的实体对象列表。
-
<T>?List<T>?findAll(Specification<T>?spec, Sort sort):根据给定的Specification对象进行查询,并按照指定的排序规则返回结果。
-
<T>?Page<T>?findAll(Specification<T>?spec, Pageable pageable):根据给定的Specification对象进行分页查询,返回符合条件的实体对象分页结果。
JpaSpecificationExecutor接口相对于JpaRepository接口提供了更加灵活、动态的查询功能,适用于需要根据不同条件进行动态查询的场景。
2,接口方法:

3,使用示例代码:
//构造排序对象
Sort.Order showOrder = Sort.Order.asc("order");
Sort sort = Sort.by(showOrder);
String param = "something";
//拼接sql
PredicateBuilder<DocumentationCategory> predicateBuilder = Specifications.or();
for (int i = 0; i < parentIds.length; i++) {
predicateBuilder = predicateBuilder.like("name", "%" + param + "%");
}
//创建查询条件对象
Specification<DocumentationCategory> specification = predicateBuilder.build();
//执行查询
List<DocumentationCategory> documentationCategoryList = documentationCategoryRepository.findAll(specification, sort);
4.2 自定义查询方法
在?Repository?接口中可以定义自定义查询方法,实现按照指定规则查询数据。Spring Data JPA 支持三种方式定义自定义查询方法:方法名称查询、参数设置查询、使用?@Query?注解查询。
4.2.1 示例代码
1,文档表:
@Getter
@Setter
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Documentation extends AbstractBaseEntity {
/**
* 标题
*/
@Column(name = "title", length = 200, nullable = false)
private String title;
/**
* 单位
*/
@Column(name = "unit", length = 200)
private String unit;
/**
* 文档类型,DOCUMENTATION_TYPE
*/
@Column(name = "doc_type", length = 50)
private String docType;
/**
* 年份
*/
@Column(name = "year", length = 4, nullable = false)
private String year;
/**
* 排序
*/
@Column(name = "show_order")
private Integer showOrder;
/**
* 是否置顶
*/
@Column(name = "top_able", columnDefinition = "boolean default false")
private Boolean top;
/**
* 是否上屏
*/
@Column(name = "show_able", columnDefinition = "boolean default false")
private Boolean show;
/**
* 文档资料目录
*/
@ManyToOne
@JoinColumn(name = "document_category_id", nullable = false)
private DocumentationCategory documentCategory;
/**
* 文号
*/
@Column(name = "document_number")
private String documentNumber;
/**
* 文档来源
*/
@ManyToOne(targetEntity = Organization.class)
@JoinColumn(name = "document_source_id")//指定document_source_id字段为外键列
private Organization organization;
}
对应的数据库:
CREATE TABLE `mt_rd_documentation` (
`id` bigint(20) NOT NULL,
`created_by` bigint(20) DEFAULT NULL,
`created_time` datetime NOT NULL,
`last_modified_by` bigint(20) DEFAULT NULL,
`last_modified_time` datetime NOT NULL,
`filter_path` varchar(380) DEFAULT NULL,
`doc_type` varchar(50) DEFAULT NULL,
`document_number` varchar(255) DEFAULT NULL,
`is_html` bit(1) DEFAULT b'0',
`show_able` bit(1) DEFAULT b'0',
`show_order` int(11) DEFAULT NULL,
`title` varchar(200) NOT NULL,
`top_able` bit(1) DEFAULT b'0',
`unit` varchar(200) DEFAULT NULL,
`year` varchar(4) NOT NULL,
`content` bigint(20) DEFAULT NULL,
`document_category_id` bigint(20) NOT NULL,
`document_source_id` bigint(20) DEFAULT NULL, #框架自动根据关系映射成bigint字段保存外键
PRIMARY KEY (`id`),
KEY `idx_rd_documentation` (`title`),
KEY `FKb8ucjiqbadllcx19p889x7iy8` (`document_category_id`),
KEY `FKhgiblpnx645pltghu1h5yy943` (`document_source_id`),
CONSTRAINT `FKb8ucjiqbadllcx19p889x7iy8` FOREIGN KEY (`document_category_id`) REFERENCES `mt_rd_documentation_category` (`id`),
CONSTRAINT `FKhgiblpnx645pltghu1h5yy943` FOREIGN KEY (`document_source_id`) REFERENCES `mt_sys_organization` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2,文件来源表(单位表):?
public class Organization extends AbstractTreeEntity<Organization> {
@ApiModelProperty(
value = "主键,自动增长, 新建时为null",
required = true
)
@ValueCopyIgnore
@Id
@GeneratedValue(
generator = "snowflake"
)
@GenericGenerator(
name = "snowflake",
strategy = "com.matech.framework.spring.jpa.SnowflakeIdGenerator"
)
private Long id;
@Column(
name = "name",
length = 100,
nullable = false
)
private String name;
@Column(
name = "short_name",
length = 100,
nullable = false
)
private String shortName;
@Column(
name = "organization_type",
nullable = false
)
@Enumerated(EnumType.STRING)
private OrganizationType organizationType;
@Column(
name = "is_available",
nullable = false
)
private Boolean available = true;
@ManyToMany(
mappedBy = "organizations",
fetch = FetchType.LAZY
)
@OrderBy("showOrder asc")
private Set<Employee> employees = new LinkedHashSet();
@Transient
private Unit unit;
@Transient
private Department department;
@Transient
private Position position;
@Column(
name = "is_interior",
nullable = false
)
private Boolean interior = true;
@ManyToMany(
fetch = FetchType.LAZY
)
@JoinTable(
name = "mt_sys_organization_role",
joinColumns = {@JoinColumn(
name = "organization_id"
)},
inverseJoinColumns = {@JoinColumn(
name = "role_id"
)}
)
@OrderBy("name asc")
private Set<Role> roles = new LinkedHashSet();
@Transient
private String fullName;
}
?3,Query语句:
????????下面Query中可以直接根据实体的对象成员的元素进行过滤,框架通过根据@JoinColumn配置的关系,自动进行联表查询,实现简化SQL开发的工作量,使得程序员有更多的余力致力于业务代码的开发(注意:这个不是普通的SQL语句(还需进过jpa框架解析的,所以可以用实体的字段名))
????????如果某些复杂的业务场景,需要写《原生的SQL语句》这就需要在@Query注解中指定nativeQuery = true 参数,该参数默认为false,表示不使用原生SQL。值得注意的是:采用原生SQL时,不能直接指定返回一个包含子对象成员的对象结果, 因为原生SQL不经过JPA框架处理,返回对象成员时,数据库保存的是该对象在数据库的id,而实体对象成员需要的是一个对象,会导致类型转换错误。但是某些场景可以直接返回,例如:返回对象成员都是基础数据类型等。
@Repository
public interface DocumentationRepository extends JpaRepository<Documentation, Long>, JpaSpecificationExecutor<Documentation> {
@Query("select d.organization from Documentation d where d.organization.name = '研发部'")
List<Organization> getAllOrg();
@Query(value = "select org.id from mt_rd_documentation d inner JOIN" +
" mt_sys_organization org on d.document_source_id = org.id where org.name = '研发部'", nativeQuery = true)
List<Long> getAllOrg2();
}
4.2.1 方法名称查询
????????方法名称查询是 Spring Data JPA 中最简单的一种自定义查询方法,并且不需要额外的注解或 XML 配置。它通过方法名来推断出查询的条件,例如以?findBy?开头的方法表示按照某些条件查询,以?deleteBy?开头的方法表示按照某些条件删除数据。
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 根据用户名查询用户
User findByUserName(String userName);
// 根据年龄查询用户列表
List<User> findByAge(Integer age);
// 根据用户名和密码查询用户
User findByUserNameAndPassword(String userName, String password);
// 根据主键和用户名删除用户
void deleteByIdAndUserName(Long id, String userName);
}
4.2.2 查询参数设置
????????除了方法名称查询外,还可以使用参数设置方式进行自定义查询。它通过在方法上使用?@Query?注解来指定查询语句,然后使用?@Param?注解来指定方法参数与查询语句中的参数对应关系。
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 根据用户名查询用户
@Query("SELECT u FROM User u WHERE u.userName = :userName")
User findByUserName(@Param("userName") String userName);
// 根据用户名和密码查询用户
@Query("SELECT u FROM User u WHERE u.userName = :userName AND u.password = :password")
User findByUserNameAndPassword(@Param("userName") String userName, @Param("password") String password);
}
4.2.3 使用@Query注解
????????在自定义查询方法时,还可以使用?@Query?注解直接指定查询语句。@Query?注解的 value 属性表示查询语句,可以使用占位符 ?1、?2 等表示方法参数。
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 根据用户名查询用户
@Query(value = "SELECT * FROM user WHERE user_name = ?1", nativeQuery = true)
User findByUserName(String userName);
// 根据用户名和密码查询用户
@Query(value = "SELECT * FROM user WHERE user_name = ?1 AND password = ?2", nativeQuery = true)
User findByUserNameAndPassword(String userName, String password);
}
4.3 使用 Sort 和 Pageable 进行排序和分页
在查询数据时,经常需要对结果进行排序和分页操作。Spring Data JPA 提供了?Sort?和?Pageable?两个类来实现排序和分页功能。
Sort?类表示排序规则,可以使用?Sort.by()?静态方法创建实例,并指定排序属性和排序方向。常用方法如下:
| 方法名 | 描述 |
|---|---|
| static Sort by(Sort.Order... orders) | 根据排序规则创建 Sort 实例 |
| static Sort.Order by(String property) | 根据属性升序排序 |
| static Sort.Order by(String property, Sort.Direction direction) | 根据属性排序 |
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 根据年龄升序查询用户列表
List<User> findByOrderByAgeAsc();
// 根据年龄降序分页查询用户列表
Page<User> findBy(Pageable pageable);
}
Pageable?类表示分页信息,可以使用?PageRequest.of()?静态方法创建实例,并指定页码、每页数据量和排序规则。常用方法如下:
| 方法名 | 描述 |
|---|---|
| static PageRequest of(int page, int size, Sort sort) | 创建分页信息实例 |
| static PageRequest of(int page, int size, Sort.Direction direction, String... properties) | 创建分页信息实例 |
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 根据年龄降序分页查询用户列表
Page<User> findBy(Pageable pageable);
}
// 使用
Pageable pageable = PageRequest.of(0, 10, Sort.by("age").descending());
Page<User> page = userRepository.findBy(pageable);
List<User> userList = page.getContent();
4.4 使用 @Modifying 注解进行修改
在 Spring Data JPA 中,使用?update?和?delete?语句需要使用?@Modifying?注解标注,并且需要添加?@Transactional?注解开启事务。需要注意的是,@Modifying?注解只支持 DML 语句。
示例代码:
public interface UserRepository extends Repository<User, Long> {
// 更新用户密码
@Modifying
@Transactional
@Query("UPDATE User u SET u.password = :password WHERE u.id = :id")
void updatePasswordById(@Param("id") Long id, @Param("password") String password);
// 删除年龄大于等于 age 的用户
@Modifying
@Transactional
@Query("DELETE FROM User u WHERE u.age >= :age")
void deleteByAgeGreaterThanEqual(@Param("age") Integer age);
}
五、高级查询
5.1 使用 Specification 进行动态查询
在实际应用中,我们经常需要根据条件动态生成查询语句。Spring Data JPA 提供了?Specification?接口来支持动态查询,它可以通过使用匿名内部类或 Lambda 表达式来实现。
Specification?接口定义了?toPredicate()?方法,该方法接受一个?Root<T>?对象和一个?CriteriaQuery<?>?对象作为参数,并返回一个?Predicate?对象,表示查询条件。
在?toPredicate()?方法内部,可以通过?root.get()?方法获取实体属性,并使用?criteriaBuilder?构建查询条件。 1.例如,以下 Lambda 表达式表示查询?age?大于等于?18?的用户。
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
List<User> findAll(Specification<User> spec);
}
// 使用
Specification<User> spec = (root, query, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(root.get("age"), 18);
userRepostory.findAll(spec);
2.假设我们有一个 User 实体类,该实体包含了 username、age 和 email 等属性。现在我们需要根据传入的参数动态生成查询条件并查询用户列表。
首先,我们需要在?UserRepository?接口中继承?JpaSpecificationExecutor?接口,示例代码如下:
@Repository
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
然后,我们可以在?UserService?中定义一个方法来进行动态查询,示例代码如下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public List<User> findUsersByCondition(String username, String email, Integer age) {
return userRepository.findAll((root, query, criteriaBuilder) -> {
List<Predicate> list = new ArrayList<>();
if (StringUtils.isNotBlank(username)) {
list.add(criteriaBuilder.equal(root.get("username"), username));
}
if (StringUtils.isNotBlank(email)) {
list.add(criteriaBuilder.like(root.get("email"), "%" + email + "%"));
}
if (age != null) {
list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("age"), age));
}
Predicate[] predicates = list.toArray(new Predicate[list.size()]);
return criteriaBuilder.and(predicates);
});
}
}
在上述代码中,我们首先判断传入的 username、email、age 是否为空,然后通过 Lambda 表达式的方式构造查询条件,并将其作为参数传给?findAll?方法进行查询。在 Lambda 表达式中,我们使用?CriteriaBuilder?来构造查询条件,通过?root.get()?方法获取属性的值,然后使用?equal()、like()?或者其他方法创建 Predicate 对象。最后,我们将所有的 Predicate 组合起来,返回结果。
5.2 使用 QueryDSL 进行复杂查询
QueryDSL 是一种流行的 Java 查询框架,它以编程方式构建类型安全的查询。Spring Data JPA 支持 QueryDSL,可以使用 QueryDSL 提供的 API 构建复杂的查询语句。
使用 QueryDSL 前,需要添加依赖和配置。首先,我们需要在 pom.xml 文件中添加以下依赖:
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.4.0</version>
<scope>provided</scope>
</dependency>
然后,我们需要添加一个 Q 实体类,该类将用于构建 QueryDSL 查询语句。可使用官方提供的 Maven 插件生成 Q 类代码,示例代码如下:
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username")
private String username;
@Column(name = "age")
private Integer age;
@Column(name = "email")
private String email;
// 省略 getter 和 setter 方法
}
@Generated("com.querydsl.codegen.EntitySerializer")
public class QUser extends EntityPathBase<User> {
private static final long serialVersionUID = -777444987L;
public static final QUser user = new QUser("user");
public final NumberPath<Integer> age = createNumber("age", Integer.class);
public final StringPath email = createString("email");
public final NumberPath<Long> id = createNumber("id", Long.class);
public final StringPath username = createString("username");
public QUser(String variable) {
super(User.class, forVariable(variable));
}
public QUser(Path<? extends User> path) {
super(path.getType(), path.getMetadata());
}
public QUser(PathMetadata metadata) {
super(User.class, metadata);
}
}
现在我们就可以使用 QueryDSL 进行复杂查询了。以下是一个示例代码,用于查询年龄大于等于20岁并且邮箱包含 "example.com" 的用户列表:
@Repository
public interface UserRepository extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public List<User> findUsersByCondition(String email, Integer age) {
QUser qUser = QUser.user;
BooleanBuilder builder = new BooleanBuilder();
if (StringUtils.isNotBlank(email)) {
builder.and(qUser.email.endsWithIgnoreCase(email));
}
if (age != null) {
builder.and(qUser.age.goe(age));
}
return (List<User>) userRepository.findAll(builder.getValue());
}
}
在?UserServiceImpl?的?findUsersByCondition?方法中,我们首先创建了一个?QUser?对象,然后使用?BooleanBuilder?对象来构建查询条件,使用?endsWithIgnoreCase()?方法和?goe()?方法来构造查询条件。最后,我们将构建出来的查询条件传递给?findAll?方法进行查询。
5.3 使用 Native SQL 查询
在某些情况下,需要执行原生的 SQL 查询语句。Spring Data JPA 提供了?@Query?注解来支持使用原生 SQL 查询数据。在?@Query?注解中设置?nativeQuery=true?即可执行原生 SQL 语句。
以下示例代码演示了如何使用原生 SQL 查询?age?大于等于?18?的用户。
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM user WHERE age >= ?1", nativeQuery = true)
List<User> findByAgeGreaterThanEqual(Integer age);
}
// 使用
userRepository.findByAgeGreaterThanEqual(18); 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!