JPA Projection 详解(接口投影 / 类投影 / 动态投影 / 原生SQL映射)

JPA Projection 详解(接口投影 / 类投影 / 动态投影 / 原生SQL映射)

作者:职业帅哥

适用技术栈:Spring Data JPA / Hibernate

适用场景:大数据量查询优化、DTO 映射、接口隔离、微服务接口瘦身


一、什么是 Projection(投影)?

Projection 是指: > 只查询和返回实体的一部分字段,而不是整个 Entity对象。 这类似于SQL中的 SELECT column1, column2 而非 SELECT *

🎯 为什么要使用 Projection?

优点 说明


减少 IO 避免加载无用字段

提升性能 降低 Hibernate 实体管理成本

避免懒加载陷阱 不触发关联加载

更适合接口返回 天然 DTO

安全 防止敏感字段泄露


二、Projection 分类总览

Spring Data JPA 支持多种投影方式:

类型 特点 推荐指数
Interface Projection 自动代理、零侵入 ⭐⭐⭐⭐⭐
Class Projection (DTO) 构造器映射 ⭐⭐⭐⭐
Dynamic Projection 运行时切换返回类型 ⭐⭐⭐
Native SQL Projection 原生 SQL 映射 ⭐⭐⭐

三、准备示例实体

java 复制代码
@Entity
@Table(name = "user")
public class UserEntity {

    @Id
    private Long id;

    private String username;
    private String email;
    private Integer age;

    private String password;
}

四、接口投影(Interface Projection)⭐⭐⭐⭐⭐

4.1 定义投影接口

java 复制代码
public interface UserSimpleView {

    Long getId();
    String getUsername();
    Integer getAge();
}

✔ 方法名必须与 Entity 字段一致(getter 规则)


4.2 Repository 使用

java 复制代码
public interface UserRepository extends JpaRepository<UserEntity, Long> {

    List<UserSimpleView> findByAgeGreaterThan(Integer age);
}

执行 SQL:

sql 复制代码
select id, username, age from user where age > ?

4.3 嵌套投影

java 复制代码
public interface OrderView {
    Long getId();
    UserView getUser();

    interface UserView {
        String getUsername();
    }
}

4.4 SpEL 计算字段

java 复制代码
public interface UserView {

    String getUsername();

    @Value("#{target.username + ' (' + target.age + ')'}")
    String getDisplayName();
}

五、DTO 类投影(Class Projection)⭐⭐⭐⭐

5.1 DTO 定义

java 复制代码
public class UserDTO {

    private Long id;
    private String username;

    public UserDTO(Long id, String username) {
        this.id = id;
        this.username = username;
    }
}

5.2 JPQL 构造器查询

java 复制代码
@Query("select new com.demo.dto.UserDTO(u.id, u.username) from UserEntity u")
List<UserDTO> findUserDTOs();

必须使用全类名。


5.3 优缺点

优点 缺点
类型安全 需要维护构造器
可复杂计算 JPQL 可读性较差
可脱离实体 修改字段需同步修改 DTO

六、动态投影(Dynamic Projection)⭐⭐⭐

6.1 Repository 定义

java 复制代码
<T> List<T> findByAgeGreaterThan(Integer age, Class<T> type);

6.2 调用示例

java 复制代码
List<UserSimpleView> views =
    repo.findByAgeGreaterThan(18, UserSimpleView.class);

List<UserDTO> dtos =
    repo.findByAgeGreaterThan(18, UserDTO.class);

6.3 使用场景

  • 同一接口支持多个返回结构
  • 后台管理 / 前台接口共用查询

七、原生 SQL Projection

7.1 接口映射

java 复制代码
public interface UserNativeView {
    Long getId();
    String getUsername();
}
java 复制代码
@Query(value = "select id as id, username as username from user", nativeQuery = true)
List<UserNativeView> findNativeUsers();

SQL 别名必须与接口方法名一致。


7.2 DTO 映射

java 复制代码
@SqlResultSetMapping(...)

(略,复杂项目才建议使用)


八、分页 + Projection

java 复制代码
Page<UserSimpleView> findByAgeGreaterThan(Integer age, Pageable pageable);

分页仍然有效。


九、排序 + Projection

java 复制代码
findByAgeGreaterThan(Integer age, Sort sort);

十、常见坑总结

❌ 1. 字段名不一致

接口方法名必须与字段名匹配。


❌ 2. 使用 Lombok Getter 命名异常

确保生成 getXxx() 方法。


❌ 3. JSON 序列化失败

Projection 是代理对象,建议直接返回给前端无问题。


❌ 4. N+1 问题

嵌套投影仍可能触发延迟加载。


十一、性能建议

建议 原因
优先使用接口投影 最轻量
避免返回 Entity 避免脏数据
只查必要字段 减少网络 IO
大列表必须分页 防止 OOM
SQL 日志观察字段 验证投影是否生效

十二、与你现有项目的结合建议

结合你目前的:

  • ✅ 微服务架构
  • ✅ 大量统计查询
  • ✅ 自定义参数转义模块
  • ✅ 多表统计分析

推荐:

Controller 层返回 Projection 接口,而不是 Entity 或 VO。

示例:

java 复制代码
public interface AlarmStatView {
    String getProjectName();
    Long getTotalCount();
}

Mapper:

java 复制代码
List<AlarmStatView> statByProject(...);

直接用于图表接口,零 DTO 转换成本。


十三、参考文档

相关推荐
J2虾虾1 分钟前
Java Lambda 表达式详解文档
java·开发语言
longxibo6 分钟前
【第1章 环境搭建与项目结构解析】
java·后端·流程图
a***72897 分钟前
Java进阶(ElasticSearch的安装与使用)
java·elasticsearch·jenkins
Java成神之路-12 分钟前
面试题:Spring AOP底层实现原理
java·spring aop
Python私教15 分钟前
如意Agent日志系统重构:从 print() 大海捞针到结构化可观测性栈
java·前端·重构
jieyucx20 分钟前
Go 零基础数据结构:顺序表(像「排抽屉」一样学增删改查)
java·数据结构·golang
曦夜日长21 分钟前
C++ STL容器string(一):string的变量细节、默认函数的认识以及常用接口的使用
java·开发语言·c++
北山有鸟27 分钟前
IS_ERR 判断出错后,再用 PTR_ERR 把它强制转换回 int 型的错误码作为函数的返回值。
java·开发语言
phltxy33 分钟前
深度解析:Spring Cloud Gateway 从入门到实战
java·开发语言
HAPPY酷38 分钟前
从Public到Private:UE5 C++类创建路径差异全解析
java·c++·ue5