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 {@Autowiredprivate UserRepository userRepository;public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Overridepublic 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 {@Idprivate 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,特点:
  1. 提供了基本的CRUD操作:JpaRepository接口继承自PagingAndSortingRepository和CrudRepository接口,因此提供了基本的CRUD操作,包括保存、更新、删除、查询等。

  2. 支持分页和排序:JpaRepository接口继承自PagingAndSortingRepository接口,因此支持分页和排序功能,可以方便地进行分页查询和结果排序。

  3. 自动生成查询方法:JpaRepository接口支持使用方法名来自动生成查询方法,无需手动编写SQL语句,可以根据方法名的规则来自动生成查询方法。

  4. 提供了一些其他便利方法:JpaRepository接口还提供了一些其他便利方法,如批量操作、统计记录数等。 

2,父接口继承结构:

 4.1.3 动态查询接口:JpaSpecificationExecutor

1,介绍

JpaSpecificationExecutor接口定义了一些方法,用于执行基于JPA规范的动态查询操作,包括:

  1. <T> List<T> findAll(Specification<T> spec):根据给定的Specification对象进行查询,返回符合条件的实体对象列表。

  2. <T> List<T> findAll(Specification<T> spec, Sort sort):根据给定的Specification对象进行查询,并按照指定的排序规则返回结果。

  3. <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();@Transientprivate Unit unit;@Transientprivate Department department;@Transientprivate 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();@Transientprivate 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 {@Autowiredprivate UserRepository userRepository;@Overridepublic 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 {@Autowiredprivate UserRepository userRepository;@Overridepublic 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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/600249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker-compose常用命令及.yaml配置模板

1、docker-compose常用命令&#xff1a; docker-compose -f mysql-docker-compose.yaml up -d docker-compose -f mysql-docker-compose.yaml downdocker-compose的常用命令包括&#xff1a; docker-compose up&#xff1a;启动并运行Compose文件中的服务。 docker-compose st…

英飞凌TC3xx之一起认识GTM(九)GTM相关知识简述(CMU,CCM,TBU,MON)

英飞凌TC3xx之一起认识GTM(九)GTM相关知识简述(CMU,CCM,TBU,MON) 1 时钟管理单元(CMU)2 集群配置模块(CCM)3 时基单元(TBU)4 监控单元(MON)5 总结由前文的各篇内容,开发者已经知道如何使用GTM的大部分功能,在这些功能中,都需要一个信息就是fGTM 的数据,我们在前…

Vue2和Vue3框架介绍以及使用

Vue2和Vue3框架介绍&#xff1a; Vue2是Vue.js的早期版本&#xff0c;它引入了组件化的开发方式&#xff0c;使得开发人员可以更加模块化地构建单页应用程序。Vue2还提供了响应式的数据绑定和指令&#xff0c;使得开发人员可以更加高效地开发动态的Web界面。 Vue2的特点&…

【源码预备】Calcite基础知识与概念:关系代数概念、查询优化、sql关键字执行顺序以及calcite基础概念

文章目录 一. 关系代数的基本知识二. 查询优化三. SQL语句的解析顺序1. FROM2. WHERE3. GROUP BY4. HAVING5. SELECT 四. Apache Calcite中的基本概念1. Adapter2. Calcite中的关系表达式2.1. 关系表达式例子2.2. 源码底层结构 3. Calcite的优化规则4. Calcite的Trait--算子物理…

使用群晖docker将小爱音箱接入chatgpt

文章目录 开源项目地址获取设备did&#xff0c;设备hardware配置文件分享上传配置文件群晖后台启动服务检查服务状态&#xff0c;测试 开源项目地址 获取设备did&#xff0c;设备hardware 使用Yonsm/MiService项目获取did和hardware。 找个Ubuntu环境安装python环境&#xff…

音乐制作软件Studio One mac有哪些特点

Studio One mac是一款专业的音乐制作软件&#xff0c;该软件提供了全面的音频编辑和混音功能&#xff0c;包括录制、编曲、合成、采样等多种工具&#xff0c;可用于制作各种类型的音乐&#xff0c;如流行音乐、电子音乐、摇滚乐等。 Studio One mac软件特点 1. 直观易用的界面&…

Golang switch 语句

简介 switch 语句提供了一种简洁的方式来执行多路分支选择 基本使用 基本语法如下&#xff1a; switch expression { case value1:// 当 expression 的值等于 value1 时执行 case value2:// 当 expression 的值等于 value2 switch 的每个分支自动提供了隐式的 break&#x…

x-cmd pkg | pdfcpu - 强大的 PDF 处理工具

目录 简介首次用户多功能支持性能表现安全的加密处理进一步阅读 简介 pdfcpu 是一个用 Go 编写的 PDF 处理库。同时它也提供 API 和 CLI。pdfcpu 提供了丰富的 PDF 操作功能&#xff0c;用户还能自己编写配置文件&#xff0c;用来管理和使用各种自定义字体并存储有效的默认配置…

苹果紧急修复两大零日漏洞,影响iPhone、iPad 和 Mac 设备

内容概述&#xff1a; 近日&#xff0c;苹果公司发布紧急安全更新&#xff0c;此次更新修复了两个在攻击中被利用并影响 iPhone、iPad 和 Mac 设备的零日漏洞。据统计&#xff0c;自今年年初以来已修复的零日漏洞数量已达到 20 个。其中提到此次发现的零日漏洞很可能已被iOS 1…

uniapp 【专题详解 -- 时间】云数据库时间类型设计,时间生成、时间格式化渲染(uni-dateformat 组件的使用)

云数据表的时间类型设计 推荐使用时间戳 timestamp "createTime": {"bsonType": "timestamp","label": "创建时间&#xff1a;" }时间生成 获取当前时间 Date.now() .add({createTime: Date.now() })时间格式化渲染 下载安…

音量控制软件sound control mac功能亮点

sound control mac可以帮助用户控制某个独立应用程序的音量&#xff0c;通过每应用音量&#xff0c;均衡器&#xff0c;平衡和音频路由独立控制每个应用的音频&#xff0c;还有整个系统的音量。 sound control mac功能亮点 每个应用程序的音量控制 独立控制应用的数量。 键盘音…

发票信息提取v1.2.0

程序介绍 “发票信息提取”是一款用于提取电子发票的PDF、XML文件中的开票信息到excel表格的软件&#xff0c;无需联网及进行复杂配置&#xff0c;打开即用。目前支持增值税电子发票&#xff08;非数电票&#xff09;原始PDF文件&#xff0c;及数电票的XML文件。 更新内容 增加…

PHP运行环境之宝塔Web站点部署

目录 Web站点部署流程(部署聚合支付后台) 项目文件 将上传的文件解压文件至根目录 修改站点关键信息 设置伪静态,选择:thinkphp,并保存 设置PHP,选择:70+版本即可。 安装网站 1设置运行目录

RMAN-03002 RMAN-06059 ORA-19625

有个现场经理反馈&#xff0c;每天的rman备份异常&#xff0c;登录系统查看rman的log日志&#xff0c;报错信息如下 RMAN> run{ 2> backup filesperset 50 archivelog all format /backup/ARCHBAK_%d_%T_%s tag arch_bak delete all input; 3> } 4> Starting …

用于查询性能预测的计划结构深度神经网络模型--大数据计算基础大作业

用于查询性能预测的计划结构深度神经网络模型 论文阅读和复现 24.【X1.1】 在关系数据库查询优化领域&#xff0c;对查询时间的估计准确性直接决定了查询优化结果&#xff0c;进而影响到数据库整体的查询效率。但由于数据库自身的复杂性&#xff0c;查询时间受到数据分布、数据…

SwiftUI之深入解析如何使用accessibilityChartDescriptor视图修饰符为视图构建音频图表

一、DataPoint 结构体 在 SwiftUI 中构建一个简单的条形图视图开始&#xff0c;该视图使用垂直条形显示一组数据点。如下所示&#xff0c;有一个 DataPoint 结构&#xff0c;用于描述条形图视图中的条形&#xff0c;它具有 id、标签、数值和填充颜色&#xff1a; struct Data…

Flutter GetX 之 路由管理

路由管理是插件GetX常用功能之一&#xff0c;为什么说之一呢&#xff1f;因为GetX的功能远不止路由管理这么简单。 GetX的重要功能如下&#xff1a; 1、路由管理2、状态管理3、国际化4、主题5、GetUtil工具6、dialog 弹框7、snackbar 其实上面功能介绍的还是不够详细&#xff…

CentOS 7.8 安装 Docker

1.卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine2.安装依赖 sudo yum -y install gcc sudo yum -y install gcc-c3.安装软件包 sudo yum inst…

Sentinel 使用

使用教程&#xff1a;Sentinel 使用教程

格密码基础:对偶格(超全面)

目录 一. 对偶格的格点 1.1 基本定义 1.2 对偶格的例子 1.3 对偶格的图形理解 二. 对偶格的格基 2.1 基本定义 2.2 对偶格的格基证明 三. 对偶格的行列式 3.1 满秩格 3.2 非满秩格 四. 重复对偶格 五. 对偶格的转移定理&#xff08;transference theorem&#xff…