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.hqwc.cn/news/326140.html

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

相关文章

wordpress个人博客/杂志主题Pin Premium

Pin Premium WordPress主题是针对博主的时尚且自适应的Pinterest风格主题。使用HTML5和CSS3技术创建&#xff0c;带有有效代码(两个演示)&#xff0c;完全响应&#xff0c;在所有移动设备上看起来完美&#xff0c;可在任何设备和 PC 上轻松使用。 响应式设计针对平板电脑和智能…

伦茨科技Apple Find My认证芯片-ST17H6x芯片

深圳市伦茨科技有限公司&#xff08;以下简称“伦茨科技”&#xff09;发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家&#xff0c;该平台提供可通过Apple Find My认证的Apple查找&#xff08;Find My&#xff09;功能集成解决方案。…

护眼台灯是智商税吗?眼科医生告诉你哪款护眼台灯最好

青少年近视发病率高达67%&#xff0c;如今&#xff0c;人们都被屏幕包围着&#xff0c;电脑、手机和电视已经成为最重要的信息手段&#xff0c;我们周围的屏幕也隐藏着有害的光污染。 对于4-15岁年龄段的孩子而言&#xff0c;除了学习本身带来的视力损伤外&#xff0c;每天接触…

本地计算机 上的 My5OL808 服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止

客户反馈说mysql启动不了&#xff0c;报错信息&#xff1a; 本地计算机 上的 My5OL808 服务启动后停止&#xff0c;某些服务在未由其他服务或程序使用时将自动停止。 查了不少资料&#xff0c;最后分析问题是这样的&#xff0c;手动或者重复安装mysql时&#xff0c;创建了多个…

安防监控EasyCVR视频融合/汇聚平台大华热成像摄像机智能告警上报配置步骤

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

【AI视野·今日Sound 声学论文速览 第三十八期】Mon, 1 Jan 2024

AI视野今日CS.Sound 声学论文速览 Mon, 1 Jan 2024 Totally 5 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers The Arrow of Time in Music -- Revisiting the Temporal Structure of Music with Distinguishability and Unique Orientability as the …

安科瑞汽车充电桩在西北地区的应用——安科瑞 顾烊宇

摘要&#xff1a;“十四五”是经济发展追赶超越的关键时期&#xff0c;将进一步促进电动汽车及充电市场的繁荣发展。目前我国正处于转型发展的关键时期&#xff0c;在“新基建”的推动下&#xff0c;新能源汽车充电桩的建设将迎来发展机遇。本文首先通过统计、分析西部城市某辖…

图神经网络入门

图神经网络&#xff08;GNN&#xff09;是一组在图领域工作的深度学习方法。 这些网络最近已应用于多个领域&#xff0c;包括&#xff1a; 组合优化、推荐系统、计算机视觉—仅举几例。 这些网络还可用于对大型系统进行建模&#xff0c;例如社交网络、蛋白质-蛋白质相互作用网络…

el 消除inpu输入框内容和下拉内容

输入这个就好了,clearable @clear="getList()" 非常简单 <span class="type-box"><span class="label">订单状态</span><el-select v-model="params.orderStatus" placeholder="请选择" class=&…

【QT】自定义代理类

目录 1 我们为什么要使用自定义代理类&#xff1f; 2 自定义代理类的基本设计要求 3 自定义代理的功能 4 基于QSpinBox的自定义代理类 5 自定义代理类的使用 1 我们为什么要使用自定义代理类&#xff1f; 传统的模型-视图框架可以让我们实现逻辑展示相分离&#xff0c;我们…

CTFhub-Web-Web前置技能-“302跳转“

题目信息 HTTP临时重定向&#xff0c;题目截图如下所示&#xff1a; 分析过程 看到跳转路径为&#xff1a;http://challenge-d1a96d97eaecf029.sandbox.ctfhub.com:10800/index.html 分析可能存在重定向问题&#xff0c;如果要想获得flag&#xff0c;则可能存在http://chal…

我的2023年总结:往前看,别回头

2023年已经结束&#xff0c;我借此机会回顾一下我的2023年&#xff0c;同时也为2024年立好flag。 文章目录 2023印象深刻的实战经历技术成长与规划技术分享与交流CSDN博客参加百度apollo技术讨论会 深入学习Redis源码多彩的生活张杰演唱会《漫长的季节》&#xff1a;往前看&am…