jpqlQueryFactory.selectFrom和select详解
jpqlQueryFactory.selectFrom 查询多个表的字段如何处理
在 JPA 的
JPQLQueryFactory(QueryDSL 整合 JPA 时的核心类)中查询多个表的字段,核心是 先通过关联关系(Join)关联多表,再通过 select() 指定需要查询的字段,最终通过 fetch() 或 fetchOne() 获取结果。根据需求不同,可分为「查询指定字段(投影查询)」「查询关联实体」「复杂关联查询」三种场景,以下是详细实操方案(基于 QueryDSL 5.x + JPA 2.2+):一、前置准备(必要前提)
1. 依赖与基础配置
确保项目引入 QueryDSL 相关依赖(以 Maven 为例):
xml
<!-- QueryDSL 核心 -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
并通过 APT 插件生成 QueryDSL 对应的查询实体(如
QUser、QOrder,前缀为 Q,与 JPA 实体类对应)。2. 示例实体与关联关系
假设存在两个关联表:
User(用户表)和 Order(订单表),关联关系为「一对多」(一个用户可拥有多个订单):java
运行
// User 实体(一的一方)
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String username;
private Integer age;
// 一对多关联 Order
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;
// getter/setter
}
// Order 实体(多的一方)
@Entity
@Table(name = "t_order") // 避免与数据库关键字冲突
public class Order {
@Id
private Long id;
private String orderNo;
private BigDecimal amount;
// 多对一关联 User(外键)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
// getter/setter
}
生成的 QueryDSL 查询实体:
QUser(对应 User)、QOrder(对应 Order)。二、核心场景:多表关联 + 投影查询(指定字段)
最常用场景:关联多表后,只查询需要的字段(而非整个实体),避免冗余数据。QueryDSL 支持两种投影方式:「Tuple 投影」(灵活,无需自定义类)和「DTO 投影」(类型安全,推荐)。
场景 1:Tuple 投影(无需自定义类,快速查询)
适合临时查询少量字段,结果存储在
Tuple 中,通过字段名或索引获取值。代码示例(查询用户名称 + 订单号 + 订单金额):
java
运行
import com.querydsl.core.Tuple;
import com.querydsl.jpa.JPQLQueryFactory;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserOrderService {
private final JPQLQueryFactory jpqlQueryFactory;
private final QUser qUser = QUser.user; // QueryDSL 生成的查询实体
private final QOrder qOrder = QOrder.order;
// 注入 EntityManager,初始化 JPQLQueryFactory
public UserOrderService(EntityManager entityManager) {
this.jpqlQueryFactory = new JPQLQueryFactory(entityManager);
}
// 多表查询:用户名称 + 订单号 + 订单金额(Tuple 投影)
public List<Tuple> queryUserOrderTuple() {
return jpqlQueryFactory
.select(
qUser.username, // User 表字段
qOrder.orderNo, // Order 表字段
qOrder.amount // Order 表字段
)
.from(qUser) // 主表:User
.join(qUser.orders, qOrder) // 关联 Order(一对多关联,join 等价于 inner join)
.where(qUser.age.gt(18)) // 筛选条件:用户年龄 > 18
.fetch(); // 执行查询,返回 Tuple 列表
}
// 解析 Tuple 结果(转为业务数据)
public List<UserOrderVO> parseTupleResult(List<Tuple> tuples) {
return tuples.stream().map(tuple -> {
UserOrderVO vo = new UserOrderVO();
// 方式 1:通过查询字段获取(类型安全,推荐)
vo.setUsername(tuple.get(qUser.username));
vo.setOrderNo(tuple.get(qOrder.orderNo));
vo.setAmount(tuple.get(qOrder.amount));
// 方式 2:通过索引获取(需与 select 字段顺序一致)
// vo.setUsername(tuple.get(0, String.class));
// vo.setOrderNo(tuple.get(1, String.class));
// vo.setAmount(tuple.get(2, BigDecimal.class));
return vo;
}).collect(Collectors.toList());
}
}
// 业务 VO(仅用于接收结果)
class UserOrderVO {
private String username;
