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 转换成本。


十三、参考文档

相关推荐
Anastasiozzzz1 天前
Java Lambda 揭秘:从匿名内部类到底层原理的深度解析
java·开发语言
骇客野人1 天前
通过脚本推送Docker镜像
java·docker·容器
铁蛋AI编程实战1 天前
通义千问 3.5 Turbo GGUF 量化版本地部署教程:4G 显存即可运行,数据永不泄露
java·人工智能·python
晚霞的不甘1 天前
CANN 编译器深度解析:UB、L1 与 Global Memory 的协同调度机制
java·后端·spring·架构·音视频
SunnyDays10111 天前
使用 Java 冻结 Excel 行和列:完整指南
java·冻结excel行和列
摇滚侠1 天前
在 SpringBoot 项目中,开发工具使用 IDEA,.idea 目录下的文件需要提交吗
java·spring boot·intellij-idea
云姜.1 天前
java多态
java·开发语言·c++
李堇1 天前
android滚动列表VerticalRollingTextView
android·java
泉-java1 天前
第56条:为所有导出的API元素编写文档注释 《Effective Java》
java·开发语言
zfoo-framework1 天前
帧同步和状态同步
java