1、概览 SpEL 是 Spring Expression Language(Spring 表达式语言)的缩写,它是一种强大的工具,能显著增强与 Spring 的交互,并为配置、属性设置和查询操作提供额外的抽象。
本文将带你了解如何使用该工具使自定义查询更具动态性,通过 @Query 注解,可以使用 JPQL 或原生 SQL 来定制与数据库的交互。
2、访问参数 首先先看看如何使用 SpEL 处理方法参数。
2.1、通过索引访问 通过索引访问参数并不是最佳选择,因为这可能会给代码带来难以调试的问题。尤其是当参数类型相同时。
同时,这也提供了更大的灵活性,特别是在开发阶段,当参数的名称经常发生变化时。IDE 可能无法正确处理代码和查询的更新。
JDBC 提供了 ? 占位符,可以用它来确定参数在查询中的位置。Spring 支持这一约定,并允许以如下方式来访问参数:
@Modifying @Transactional @Query(value = "INSERT INTO articles (id, title, content, language) " + "VALUES (?1, ?2, ?3, ?4)", nativeQuery = true) void saveWithPositionalArguments(Long id, String title, String content, String language); 到目前为止,使用的方法与之前在 JDBC 应用中使用的方法相同。注意,在数据库中进行更改的任何查询都需要 @Modifying 和 @Transactional 注解,INSERT 就是其中之一。所有 INSERT 的示例都将使用原生查询,因为 JPQL 不支持。
1、概览 本文将带你了解使用 Spring Data JPA 获取最后一条记录的多种方式。
2、初始设置 首先,创建并初始化要查询的表。
创建一个 Post 实体类:
@Entity public class Post { @Id private Long id; private String title; private LocalDate publicationDate; // get、set 方法 } @Entity 表示注解的类代表数据库中的一个表。@Id 注解定义了主键。
为了简单起见,这里使用的是 H2 内存数据库。
添加一个基本的 SQL 脚本,创建 Post 类对应的 post 表:
DROP TABLE IF EXISTS post; CREATE TABLE post( id INT PRIMARY KEY, title VARCHAR(200), publication_date DATE ) 接着,添加一些数据:
INSERT INTO post (id, title, publication_date) VALUES(1, 'Facebook post', '2020-11-10'); INSERT INTO post (id, title, publication_date) VALUES(2, 'Instagram post', '2020-12-24'); INSERT INTO post (id, title, publication_date) VALUES(3, 'Twitter post', '2023-01-10'); INSERT INTO post (id, title, publication_date) VALUES(4, 'tiktok post', '2023-03-18'); INSERT INTO post (id, title, publication_date) VALUES(5, 'Pinterest post', '2023-09-09'); 如你所见,最后一条记录的 ID 是 5。
1、概览 在前两篇文章中,我们使用 JPA Criteria 和 Spring Data JPA Specification 构建了相同的搜索/过滤功能。
本文将带你了解如何使用 Spring Data JPA 和 Querydsl 构建 REST API 查询语言。
2、Querydsl 配置 首先,在 pom.xml 中添加以下依赖:
<dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>4.2.2</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>4.2.2</version> </dependency> 还需要配置 APT(注解处理工具)插件:
<plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> 该插件会根据实体类生成 Q 开头的查询类。
关于如何在 Spring Boot 中使用 QueryDSL,你可以参阅 这篇文章。
3、MyUser 实体 定义 MyUser 实体,用于在 API 中进行搜索:
@Entity public class MyUser { @Id @GeneratedValue(strategy = GenerationType.
1、概览 上一篇文章 介绍了一个基于 JPA Criteria 的查询语言解决方案。
本文将带你了解如何使用 Spring Data JPA 和 Specification 构建一个搜索/过滤 REST API。
2、 User 实体 创建一个 User 实体类,用于检索 API:
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; private String email; private int age; // get、set 方法 } 3、使用 Specification 进行过滤 来看看如何使用自定义 Spring Data JPA Specification 进行查询。
创建一个实现 Specification 接口的 UserSpecification,并传入自己的约束条件来构建实际查询:
public class UserSpecification implements Specification<User> { private SearchCriteria criteria; @Override public Predicate toPredicate (Root<User> root, CriteriaQuery<?
1、概览 在接下来的一系列文章中,我将带你了解一种用于 REST API 的简单查询语言。
为什么要使用查询语言(Query Language)?因为对于任何足够复杂的 API 来说,仅仅通过简单的字段来搜索/过滤资源是远远不够的。查询语言更加灵活,可以准确过滤选出所需的资源。
2、User 实体 首先,创建一个 User 实体,用于在过滤/搜索 API 中使用:
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; private String email; private int age; } 3、使用 CriteriaBuilder 过滤 构建查询抽象是一个平衡问题。一方面,需要足够的灵活性,另一方面,需要保持可控的复杂性。高级查询的功能很简单 - 输入一些约束条件,然后得到一些结果。
来看看如何使用:
@Repository public class UserDAO implements IUserDAO { @PersistenceContext private EntityManager entityManager; @Override public List<User> searchUser(List<SearchCriteria> params) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> query = builder.
简介 本文将会带你了解 Spring Data JPA 对锁的支持,以及如何在检索时使用共享锁或者独占锁。
JPA LockModeType JPA 提供了几种加锁的选项,可以在获取实体时通过 LockModeType 枚举来指定。
如下:
LockModeType.PESSIMISTIC_READ 可对相关表记录加共享锁或读锁。 LockModeType.PESSIMISTIC_WRITE 可对相关表记录加速独占锁或写锁。 如果底层数据库不支持共享锁,那么 PESSIMISTIC_READ 策略将退回到 PESSIMISTIC_WRITE,因为该选项已被广泛支持。
根据 ID 获取实体时加锁 JPA EntityManager 提供了一个 find 方法的重载,可以传递 LockModeType 采参数。用于在获取实体时上锁:
Post post = entityManager.find(Post.class, id, lockMode); Spring Data JpaRepository 未提供此选项,但我们可以使用自定义 Spring Data Repository 来实现。
例如,可以让 PostRepository 继承 JpaRepository,CustomPostRepository:
@Repository public interface PostRepository extends JpaRepository<Post, Long>, CustomPostRepository { } In the CustomPostRepository interface, we can define a lockById method like this:
简介 本文将带你了解,当无法依赖 CascadeType 机制来将状态转换从父实体传播到子实体时,如何使用 Spring Data JPA 级联删除单向关联,
Domain Model 假设我们的系统中有以下实体:
Post 实体是该实体层次结构的根(root),子实体只使用单向 @ManyToOne 或 @OneToOne 关联,用于映射一个一对多或一对一表关系的底层外键。
Post root 实体如下:
@Entity @Table(name = "post") public class Post { @Id private Long id; private String title; // getter/setter 方法省略 } 请注意,没有双向的 @OneToMany 或 @OneToOne 关联,可以允许我们从父级 Post 级联 DELETE 操作到 PostComment、PostDetails 或 PostTag 子实体。
PostComment 实体使用单向 @ManyToOne 关联映射 post_id 外键列:
@Entity @Table(name = "post_comment") public class PostComment { @Id @GeneratedValue private Long id; @ManyToOne(fetch = FetchType.
Spring Data JPA 是 Spring 框架提供的一个模块,用于简化与关系型数据库的交互和数据访问。它基于JPA(Java Persistence API)标准,并提供了一组易于使用的API和工具,帮助开发人员更轻松地进行数据库操作。通过Spring Data JPA,开发人员可以通过编写简洁的代码来执行常见的 CRUD 操作,同时还支持高级查询、分页、事务管理等功能。它的目标是提供一种更简单、更高效的方式来处理数据库操作,减少开发人员的工作量,并提高应用程序的可维护性和可扩展性。
本文将会指导你如何在 Spring Boot 应用中整合、使用 Spring Data Jpa。
软件版本:
Java:17 Spring Boot:3.1.3 MySQL:8.0.27 创建工程 点击 start.springboot.io 快速创建 Spring Boot 整合 Spring Data Jpa 的示例应用。
我们选择了 spring-boot-starter-web、spring-boot-starter-data-jpa、mysql-connector-j 和 spring-boot-starter-test 依赖。
pom.xml 如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> spring-boot-starter-data-jpa 默认使用 Hibernate 作为 JPA 实现。本文使用 MYSQL 数据库进行演示,如果你使用其他数据库需要修改驱动。
1、概览 在本教程中,我们将学习在 JPA 中,为什么实体类必须要有默认无参数构造函数?
为了理解无参数构造函数的意义,我们将使用一个简单的 Employee 实体示例。我们将观察缺少默认构造函数是如何导致编译时错误的。我们将深入探讨 JPA 在实体实例化中对 Reflection 的使用。此外,我们还将简要介绍这些类中可能需要该构造函数的其他原因。
2、示例 让我们举一个简单的例子,建立一个名为 Employee 的实体类,其中包含 name、department 和自动生成的 id。让我们定义一个包含所有三个字段的构造函数:
@Entity public class Employee { @Id private Long id; private String name; private int age; public Employee(Long id, String name, int age) { this.id = id; this.name = name; this.age = age; } // get 和 set } 不过,此时我们会发现 Employee 类无法编译:Class'Employee' should have [public, protected] no-arg constructor。
显然,在这里我们可以看到,我们定义了一个带有全参构造函数的 Entity 类,但却没有无参数构造函数。在这种情况下,会出现一个编译时错误,提示我们除了现有的构造函数外,还需要一个无参数构造函数。
在接下来的两节中,我们将围绕 Entity 类中的构造函数了解 JPA 规范。我们将了解如何修复错误,以及 JPA 施加这些约束的根本原因。
1、概览 Spring Data Repository 提供了大量可简化数据访问逻辑实现的方法。然而,选择合适的方法并不总是像我们想象的那么容易。
以 findBy 和 findOneBy 为前缀的方法就是一个例子。尽管从名称上看,它们似乎做着同样的事情,但其实还是有一些区别的。
2、Spring Data 中的派生查询方法 Spring Data JPA 的派生查询方法功能经常受到称赞。这些方法提供了一种从方法名称派生特定查询的方法。例如,如果我们想通过 foo 属性检索数据,只需写 findByFoo() 即可。
通常,我们可以使用多个前缀来构建派生查询方法。这些前缀包括 findBy 和 findOneBy。下面,我们就来看看它们的实际应用。
3、实例 首先,我们来看看 Person 实体类:
@Entity public class Person { @Id private int id; private String firstName; private String lastName; // 标准的 get 和 set 方法 } 在这里,我们将使用 H2 作为数据库。让我们使用一个基本的 SQL 脚本为数据库添加数据:
INSERT INTO person (id, first_name, last_name) VALUES(1, 'Azhrioun', 'Abderrahim'); INSERT INTO person (id, first_name, last_name) VALUES(2, 'Brian', 'Wheeler'); INSERT INTO person (id, first_name, last_name) VALUES(3, 'Stella', 'Anderson'); INSERT INTO person (id, first_name, last_name) VALUES(4, 'Stella', 'Wheeler'); 最后,让我们创建一个 JPA repository 来管理我们的 Person 实体: