Spring Data JPA用法浅析

一:介绍

Spring Data JPA是Spring框架的一部分,它为Java Persistence API (JPA) 提供了一套高度抽象的数据访问层实现。它的目的是使得开发者能够更加方便快捷地实现对数据库的操作,而不需要编写大量的重复性SQL语句或者复杂的JPA查询。

二:环境准备

1,添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

2,配置数据库连接

spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=mysecretpassword
spring.jpa.hibernate.ddl-auto=update

3,创建实体类

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String email;

    // Getter and Setter methods
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

4,定义Repository接口

import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends JpaRepository<T, I>, JpaSpecificationExecutor<T> {
    // Custom query method example
    List<User> findByUsername(String username);
}

5,使用repository接口

在服务类中注入Repository并使用它进行数据库操作:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(User user) {
        return userRepository.save(user);
    }

    public List<User> getUsersByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    // Add more methods for update, delete, etc.
}

三,用法

1,根据方法名自动生成查询:
1)JpaRepository提供部分默认实现方法,我们可以直接使用。

package org.springframework.data.jpa.repository;

import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort sort);

    List<T> findAllById(Iterable<ID> ids);

    <S extends T> List<S> saveAll(Iterable<S> entities);

    void flush();

    <S extends T> S saveAndFlush(S entity);

    <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

    /** @deprecated */
    @Deprecated
    default void deleteInBatch(Iterable<T> entities) {
        this.deleteAllInBatch(entities);
    }

    void deleteAllInBatch(Iterable<T> entities);

    void deleteAllByIdInBatch(Iterable<ID> ids);

    void deleteAllInBatch();

    /** @deprecated */
    @Deprecated
    T getOne(ID id);

    /** @deprecated */
    @Deprecated
    T getById(ID id);

    T getReferenceById(ID id);

    <S extends T> List<S> findAll(Example<S> example);

    <S extends T> List<S> findAll(Example<S> example, Sort sort);
}

 2)如果默认未实现的,我们可以按照字段名称去查询。

List<User> findByUsernameAndEmail(String username, String email); //根据用户的名称和邮箱查询
List<User> findByUsernameIn(List<String> usernames); //查询用户名在某个集合中的数据

2,使用Example API查询

1)创建一个User实例作为查询模板,并使用Example.of()方法配合Repository执行查询。

public List<User> findUsersByExample() {
        User exampleUser = new User();
        exampleUser.setUsername("John Doe");
        exampleUser.setEmail("2531323@qq.com");
        Example<User> example = Example.of(exampleUser); // 创建Example实例
        return userRepository.findAll(example); // 执行查询
}

2)使用ExampleMatcher来定制查询条件的匹配方式。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 精确匹配
    public List<User> findUsersByUsernameExact(String username) {
        User probe = new User();
        probe.setUsername(username);
        
        ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("username", ExampleMatcher.GenericPropertyMatchers.exact());
        Example<User> example = Example.of(probe, matcher);
        
        return userRepository.findAll(example);
    }

    // 模糊匹配(like %keyword%)
    public List<User> findUsersByUsernameLike(String keyword) {
        User probe = new User();
        probe.setUsername(keyword);
        
        ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains());
        Example<User> example = Example.of(probe, matcher);
        
        return userRepository.findAll(example);
    }

    // 忽略大小写的精确匹配
    public List<User> findUsersByUsernameIgnoreCase(String username) {
        User probe = new User();
        probe.setUsername(username);
        
        ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreCase().withMatcher("username", ExampleMatcher.GenericPropertyMatchers.exact());
        Example<User> example = Example.of(probe, matcher);
        
        return userRepository.findAll(example);
    }
}

3,手动写sql语句

import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface UserRepository extends CrudRepository<User, Long>, JpaSpecificationExecutor<User> {

    // 自定义查询方法,使用@Query注解来指定SQL查询语句
    @Query("SELECT u FROM User u WHERE u.username IN (:usernames)")
    List<User> findByUsernamesIn(List<String> usernames);
}

4,使用Specification结构化查询

1)Specification的用法主要是依赖于JpaSpecificationExecutor接口

package org.springframework.data.jpa.repository;

import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;

public interface JpaSpecificationExecutor<T> {
    Optional<T> findOne(@Nullable Specification<T> spec);

    List<T> findAll(@Nullable Specification<T> spec);

    Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);

    List<T> findAll(@Nullable Specification<T> spec, Sort sort);

    long count(@Nullable Specification<T> spec);

    boolean exists(Specification<T> spec);
}

2)一般用于构建分页查询:

public Page<User> getEndpointPage(String username, String email, Pageable pageable) {
        Page<User> userPage = userRepository.findAll((Specification<User>) (root, query, criteriaBuilder) -> {
      Predicate predicate = criteriaBuilder.conjunction();
// name模糊查询 if (StringUtils.hasText(username)) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.like(root.get("username"), username)); } if (StringUtils.hasText(email)) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("email"), email)); }return predicate; // 查询username包含xxx且emial为xxx的用户 }, pageable); }
public Page<User> getEndpointPage(String username, String email, Pageable pageable) {
        Page<User> userPage = userRepository.findAll((Specification<User>) (root, query, criteriaBuilder) -> {
         Predicate predicate = criteriaBuilder.conjunction();
            // name模糊查询
            if (StringUtils.hasText(username)) {
                predicateUsername = criteriaBuilder.like(root.get("username"), "%".concat(username).concat("%"));
            }
            if (StringUtils.hasText(email)) {
                predicateEmail = criteriaBuilder.like(root.get("email"), "%".concat(email).concat("%"));
            }
            return criteriaBuilder.add(predicate, criteriaBuilder.or(predicateUsername, predicateEmail));   // 查询username包含xxx或者emial包含xxx的用户
        }, pageable);
}

 3)使用CriteriaBuilder和Predicate还可以这样使用:

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;

public List<User> searchUsers(Long id, String username, String email, Boolean active) {
    EntityManager entityManager = // 获取EntityManager实例,通常是通过注入的方式获得

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
    Root<User> userRoot = criteriaQuery.from(User.class);

    // 初始化一个Predicate列表,用于存放所有条件
    List<Predicate> predicates = new ArrayList<>();

    // 添加ID条件,如果id不为null
    if (id != null) {
        Predicate idPredicate = criteriaBuilder.equal(userRoot.get("id"), id);
        predicates.add(idPredicate);
    }

    // 添加用户名条件,如果username不为空
    if (StringUtils.hasText(username)) {
        Predicate usernamePredicate = criteriaBuilder.like(userRoot.get("username"), "%" + username + "%");
        predicates.add(usernamePredicate);
    }

    // 添加邮箱条件,如果email不为空
    if (StringUtils.hasText(email)) {
        Predicate emailPredicate = criteriaBuilder.equal(userRoot.get("email"), email);
        predicates.add(emailPredicate);
    }

    // 添加激活状态条件,如果active不为null
    if (active != null) {
        Predicate activePredicate = criteriaBuilder.equal(userRoot.get("active"), active);
        predicates.add(activePredicate);
    }

    // 使用Predicate数组组合所有条件,这里使用逻辑与(AND)连接
    Predicate finalPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));

    // 将最终的Predicate应用到查询中
    criteriaQuery.where(finalPredicate);

    // 执行查询并返回结果
    TypedQuery<User> typedQuery = entityManager.createQuery(criteriaQuery);
    return typedQuery.getResultList();
}

4)当然,PredicateCriteriaBuilder的使用远不止上述示例所展示的那样。它们可以用于构建非常复杂和灵活的查询逻辑。以下是几个额外的用法示例:

1. 逻辑或(OR)条件:

// 如果你想根据多个条件中的任意一个进行查询(逻辑“或”),可以使用criteriaBuilder.or()来组合Predicate。
Predicate usernameOrEmailPredicate = criteriaBuilder.or(
    criteriaBuilder.like(userRoot.get("username"), "%" + username + "%"),
    criteriaBuilder.like(userRoot.get("email"), "%" + email + "%")
);

2. 分组和优先级:

// 使用criteriaBuilder.and()和criteriaBuilder.or()可以组合多个Predicate,并使用括号来改变逻辑优先级。
Predicate groupPredicate = criteriaBuilder.and(
    criteriaBuilder.equal(userRoot.get("active"), true),
    criteriaBuilder.or(
        criteriaBuilder.equal(userRoot.get("username"), username),
        criteriaBuilder.equal(userRoot.get("email"), email)
    )
);

3. 排序

// 可以使用criteriaQuery.orderBy()方法来添加排序条件。
criteriaQuery.orderBy(criteriaBuilder.desc(userRoot.get("creationDate")));

4. 分页查询

// 在TypedQuery上调用setFirstResult()和setMaxResults()来实现分页。
typedQuery.setFirstResult(pageNumber * pageSize);
typedQuery.setMaxResults(pageSize);

5. 聚合函数

// 使用CriteriaQuery的multiselect和criteriaBuilder.count()等方法来实现聚合查询。
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(countQuery.from(User.class)));
Long totalUsers = entityManager.createQuery(countQuery).getSingleResult();

6. 子查询

// 可以构造子查询来作为查询条件的一部分。
Subquery<Long> subquery = criteriaQuery.subquery(Long.class);
Root<User> subqueryRoot = subquery.from(User.class);
subquery.select(subqueryRoot.get("id"));
subquery.where(criteriaBuilder.equal(subqueryRoot.get("email"), email));
Predicate inSubquery = criteriaBuilder.in(userRoot.get("id")).value(subquery);

以上主要是JPA对单表操作的常见用法,多表之间的操作请见下一篇解析。

posted on 2024-06-25 18:14  MrQuan  阅读(27)  评论(0编辑  收藏  举报