Spring Data Jpa速通(1)

1 结构关系

1.1 Repositry<>

package org.springframework.data.repository;

import java.io.Serializable;

/**
 * Repository 是所有特定 Repository 的顶层接口,仅用于标记。
 */
@NoRepositoryBean
public interface Repository<T, ID extends Serializable> {
}

@NoRedpository注解表示该接口不会被spring容器做为Bean实例化

1.2 CurdRepository<T, ID>

package org.springframework.data.repository;

import java.util.Optional;

public interface CrudRepository<T, ID> extends Repository<T, ID> {

    <S extends T> S save(S entity); // 保存一个实体,要先判断实体是否存在,再决定插入或更新

    Optional<T> findById(ID id);    // 根据主键查找

    boolean existsById(ID id);      // 是否存在

    Iterable<T> findAll();          // 查询所有

    Iterable<T> findAllById(Iterable<ID> ids); // 根据多个ID查询

    long count();                   // 总记录数

    void deleteById(ID id);         // 根据ID删除

    void delete(T entity);          // 删除实体

    void deleteAll(Iterable<? extends T> entities); // 删除多个

    void deleteAll();               // 删除全部
}

公共基础的增 删 改 查

1.3 PagingAndSortingRepository<T, ID>

package org.springframework.data.repository;

import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Page;

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

    Iterable<T> findAll(Sort sort);        // 支持排序

    Page<T> findAll(Pageable pageable);    // 支持分页
}

1.4 JpaRepository

package org.springframework.data.jpa.repository;

import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
import java.util.Optional;

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID> {

    List<T> findAll(); // 返回 List 而非 Iterable

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

    <S extends T> List<S> saveAll(Iterable<S> entities); // 批量保存

    void flush(); // 刷新持久化上下文

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

    void deleteInBatch(Iterable<T> entities); // 批量删除

    void deleteAllInBatch();

    T getOne(ID id); // 获取实体的引用(懒加载)

    Optional<T> findById(ID id);
}

1.5 SimpleJpaRepository

package org.springframework.data.jpa.repository.support;

import jakarta.persistence.EntityManager;
import jakarta.persistence.LockModeType;
import jakarta.persistence.TypedQuery;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepository<T, ID> {

    private final EntityManager em;
    private final JpaEntityInformation<T, ?> entityInformation;

    public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager em) {
        this.entityInformation = entityInformation;
        this.em = em;
    }

    public SimpleJpaRepository(Class<T> domainClass, EntityManager em) {
        this.entityInformation = JpaEntityInformationSupport.getEntityInformation(domainClass, em);
        this.em = em;
    }

    // === 查询操作 ===

    @Override
    public Optional<T> findById(ID id) {
        return Optional.ofNullable(em.find(entityInformation.getJavaType(), id));
    }

    @Override
    public List<T> findAll() {
        String jpql = "SELECT e FROM " + entityInformation.getEntityName() + " e";
        return em.createQuery(jpql, entityInformation.getJavaType()).getResultList();
    }

    @Override
    public Page<T> findAll(Pageable pageable) {
        List<T> content = getQuery(pageable).getResultList();
        return new PageImpl<>(content, pageable, count());
    }

    protected TypedQuery<T> getQuery(Pageable pageable) {
        String jpql = "SELECT e FROM " + entityInformation.getEntityName() + " e";
        TypedQuery<T> query = em.createQuery(jpql, entityInformation.getJavaType());

        if (pageable.isPaged()) {
            query.setFirstResult((int) pageable.getOffset());
            query.setMaxResults(pageable.getPageSize());
        }

        return query;
    }

    @Override
    public long count() {
        String jpql = "SELECT COUNT(e) FROM " + entityInformation.getEntityName() + " e";
        return em.createQuery(jpql, Long.class).getSingleResult();
    }

    // === 保存操作 ===

    @Transactional
    @Override
    public <S extends T> S save(S entity) {
        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }

    @Transactional
    @Override
    public <S extends T> List<S> saveAll(Iterable<S> entities) {
        List<S> result = new java.util.ArrayList<>();
        for (S entity : entities) {
            result.add(save(entity));
        }
        return result;
    }

    @Transactional
    @Override
    public void deleteById(ID id) {
        delete(findById(id).orElseThrow(() -> new IllegalArgumentException("Entity not found")));
    }

    @Transactional
    @Override
    public void delete(T entity) {
        em.remove(em.contains(entity) ? entity : em.merge(entity));
    }

    @Transactional
    @Override
    public void deleteAll() {
        for (T entity : findAll()) {
            delete(entity);
        }
    }

    @Transactional
    @Override
    public void flush() {
        em.flush();
    }

    @Transactional
    @Override
    public <S extends T> S saveAndFlush(S entity) {
        S result = save(entity);
        flush();
        return result;
    }

    @Transactional
    @Override
    public void deleteInBatch(Iterable<T> entities) {
        for (T entity : entities) {
            delete(entity);
        }
    }

    // 其他方法略...
}

注意save方法, save方法会判断是否存在来选择调用插入还是更新。
注意delete方法,先查询,查出对象后, 不存在就抛异常, 存在才会删除。

1.6 PagingAndSortingRepository

package org.springframework.data.repository;

import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Page;

import java.util.List;

/**
 * 继承自 CrudRepository,提供分页和排序的功能扩展。
 *
 * @param <T>  实体类型
 * @param <ID> 实体ID类型
 */
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

    /**
     * 返回所有实体,并按提供的排序条件排序。
     *
     * @param sort 排序参数
     * @return 排好序的实体集合
     */
    Iterable<T> findAll(Sort sort);

    /**
     * 返回分页的实体数据。
     *
     * @param pageable 分页参数(页码、大小、排序)
     * @return 包含分页信息的 Page 对象
     */
    Page<T> findAll(Pageable pageable);
}

1.7 QueryByExampleExecutor

package org.springframework.data.repository.query;

import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.Optional;

/**
 * 通过示例实体进行查询的接口(Query by Example)。
 *
 * @param <T> 实体类型
 */
public interface QueryByExampleExecutor<T> {

    /**
     * 查找与给定示例匹配的单个实体。
     *
     * @param example 查询条件(封装成 Example)
     * @return 匹配的实体(如果存在)
     */
    <S extends T> Optional<S> findOne(Example<S> example);

    /**
     * 查找与给定示例匹配的所有实体。
     *
     * @param example 查询条件
     * @return 匹配的实体集合
     */
    <S extends T> Iterable<S> findAll(Example<S> example);

    /**
     * 查找与示例匹配的所有实体,并排序。
     *
     * @param example 查询条件
     * @param sort 排序条件
     * @return 排序后的结果集
     */
    <S extends T> Iterable<S> findAll(Example<S> example, Sort sort);

    /**
     * 查找与示例匹配的所有实体,分页返回。
     *
     * @param example 查询条件
     * @param pageable 分页参数
     * @return 分页结果
     */
    <S extends T> Page<S> findAll(Example<S> example, Pageable pageable);

    /**
     * 返回与示例匹配的记录数量。
     *
     * @param example 查询条件
     * @return 匹配数量
     */
    <S extends T> long count(Example<S> example);

    /**
     * 判断是否存在与示例匹配的记录。
     *
     * @param example 查询条件
     * @return 是否存在
     */
    <S extends T> boolean exists(Example<S> example);
}

这个接口提供了“示例实体”进行查询的功能

1.8 JpaSpecificationExecutor

package org.springframework.data.jpa.repository;

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 java.util.List;
import java.util.Optional;

/**
 * 提供使用 JPA Criteria API(即 Specification)进行动态查询的能力。
 *
 * @param <T> 实体类型
 */
public interface JpaSpecificationExecutor<T> {

    /**
     * 根据 Specification 返回符合条件的单个实体(最多一个)。
     *
     * @param spec 查询条件
     * @return 匹配的 Optional 实体
     */
    Optional<T> findOne(Specification<T> spec);

    /**
     * 返回所有符合 Specification 的实体列表。
     *
     * @param spec 查询条件
     * @return 匹配的实体集合
     */
    List<T> findAll(Specification<T> spec);

    /**
     * 返回排序后的结果列表。
     *
     * @param spec 查询条件
     * @param sort 排序条件
     * @return 排序后的匹配结果
     */
    List<T> findAll(Specification<T> spec, Sort sort);

    /**
     * 返回分页的查询结果。
     *
     * @param spec 查询条件
     * @param pageable 分页条件
     * @return 分页结果
     */
    Page<T> findAll(Specification<T> spec, Pageable pageable);

    /**
     * 返回符合 Specification 条件的记录总数。
     *
     * @param spec 查询条件
     * @return 匹配数量
     */
    long count(Specification<T> spec);
}

用法示例:

Specification<User> spec = (root, query, cb) -> 
    cb.equal(root.get("username"), "张三");

List<User> result = userRepository.findAll(spec);

public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {}
  • 多条件动态查询(表单筛选、后台搜索)
  • 多表关联查询时,配合Join
  • 条件复杂四时优于@Query和方法名查询

1.9 QuerydslPredicateExecutor

···
package org.springframework.data.querydsl;

import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.Optional;

public interface QuerydslPredicateExecutor {

/**
 * 查找符合给定 Predicate 条件的单个实体。
 *
 * @param predicate 查询条件
 * @return 匹配的实体(如果存在)
 */
Optional findOne(Predicate predicate);

/**
 * 查找符合给定 Predicate 条件的所有实体。
 *
 * @param predicate 查询条件
 * @return 匹配的所有实体
 */
Iterable findAll(Predicate predicate);

/**
 * 按照指定的排序规则返回匹配的所有实体。
 *
 * @param predicate 查询条件
 * @param sort 排序规则
 * @return 排序后的匹配实体集合
 */
Iterable findAll(Predicate predicate, Sort sort);

/**
 * 按 Querydsl 的 OrderSpecifier 排序。
 *
 * @param predicate 查询条件
 * @param orders 排序规则(Querydsl 专用)
 * @return 排序后的匹配实体集合
 */
Iterable findAll(Predicate predicate, OrderSpecifier... orders);

/**
 * 分页查询。
 *
 * @param predicate 查询条件
 * @param pageable 分页参数
 * @return 分页结果
 */
Page findAll(Predicate predicate, Pageable pageable);

/**
 * 计算符合查询条件的记录数。
 *
 * @param predicate 查询条件
 * @return 记录数量
 */
long count(Predicate predicate);

/**
 * 判断是否存在符合查询条件的记录。
 *
 * @param predicate 查询条件
 * @return 是否存在
 */
boolean exists(Predicate predicate);

/**
 * 返回当前实体的 PathBuilder(用于构建 Predicate)
 *
 * @return 路径构建器
 */
PathBuilder getPathBuilder();

}

···

基于Querydsl的类型安群查询机制,功能类似于JpaSpecifiactionExecutor, 但使用更了流畅、链式的查询方式。

QUser user = QUser.user;

Predicate predicate = user.age.gt(18).and(user.status.eq("ACTIVE"));

Page<User> result = userRepository.findAll(predicate, PageRequest.of(0, 10));

public interface UserRepository
    extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> {}

发表回复

您的电子邮箱地址不会被公开。