
本文旨在解决在使用 JPA Criteria 查询时,如何正确获取 `ManyToOne` 关联实体中的数据。通过 `Join` 操作,我们可以访问关联实体中的属性,并将其应用于查询条件中,避免常见的 "Unable to locate Attribute" 错误。同时,建议使用元模型(Metamodel)进行类型安全的查询。
在使用 Hibernate JPA 进行数据查询时,经常会遇到需要访问关联实体属性的场景,特别是当实体之间存在 ManyToOne 关系时。直接在 Criteria 查询中使用关联实体的属性,会导致 Unable to locate Attribute 错误。本文将详细介绍如何通过 Join 操作正确地访问 ManyToOne 关联实体中的数据,并提供示例代码和注意事项。
问题分析
假设我们有两个实体 GroupEntity 和 UserEntity,其中 GroupEntity 通过 ManyToOne 关系关联到 UserEntity。GroupEntity 包含 name 和 status 属性,而 UserEntity 包含 userCode 属性。如果我们需要查询 userCode 匹配特定值,或者 name 匹配其他特定值的 GroupEntity 列表,直接在 GroupEntity 的 Root 上使用 userCode 属性会抛出异常,因为 userCode 实际上是 UserEntity 的属性。
解决方案:使用 Join 操作
要解决这个问题,我们需要使用 Join 操作将 GroupEntity 和 UserEntity 连接起来。通过 Join 操作,我们可以获得 UserEntity 的引用,从而访问其属性。
以下是修改后的 JPA Criteria 查询代码示例:
public ListgetData(final String userCode, final String currentGroupName, final String newGroupName) { final EntityManager entityManager = entityManagerProvider.get(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaQuery cq = cb.createQuery(GroupEntity.class); final Root root = cq.from(GroupEntity.class); // 使用 Join 操作连接 GroupEntity 和 UserEntity Join user = root.join("userEntity"); cq.select(root).where(cb.or( cb.equal(user.get("userCode"), userCode), // 使用 user.get("userCode") 访问 UserEntity 的 userCode 属性 cb.equal(root.get("name"), currentGroupName), cb.equal(root.get("name"), newGroupName))); return entityManager.createQuery(cq).getResultList(); }
代码解释:
- Join
user = root.join("userEntity");:这行代码创建了一个 Join 对象,将 GroupEntity 的 root 和 UserEntity 连接起来。"userEntity" 是 GroupEntity 中 @ManyToOne 注解指定的属性名称。 - cb.equal(user.get("userCode"), userCode):使用 user.get("userCode") 可以访问 UserEntity 的 userCode 属性,并将其用于查询条件。
示例实体类
为了更好地理解上述代码,以下是 GroupEntity 和 UserEntity 的示例代码:
本软件完全免费,无任何bug。用户可放心使用,网关需单独注册,请联系软件作者。1、关于接口设置:721K 卡易智能点卡接口,易宝支付网银接口。2、关于账户功能:商户信息管理、玩家留言信箱、网关下载、资金管理。3、关于游戏管理:分区管理、添加分区、分组管理、比例模板、补发管理、获取代码。4、关于订单管理:订单查询、渠道管理、结算统计。5、关于数据统计:玩家排名、分区排名、渠道统计。6、程序是 .NE
@Entity
@Table(name = "groups")
public class GroupEntity extends BaseEntity {
@Id
@SequenceGenerator(name = "beta_group_seq", sequenceName = "group_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "group_seq")
@Column(name = "id")
private Long id;
@Column(name = "name", length = 1400)
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private UserEntity userEntity;
@Column(name = "status")
private String status;
// Getters and setters
}
@Entity
@Table(name = "users")
public class UserEntity extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "user_code", nullable = false, unique = true)
private String userCode;
// Getters and setters
}
@Getter
@MappedSuperclass
public abstract class BaseEntity {
@Column(name = "CREATED_BY", length = 8, updatable = false)
private String createdBy;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CREATION_DATE", updatable = false)
private Date creationDate;
@Column(name = "LAST_UPDATED_BY", length = 8, updatable = false)
private String lastUpdatedBy;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "LAST_UPDATED_DATE", updatable = false)
private Date lastUpdatedDate;
}使用元模型(Metamodel)进行类型安全查询
虽然使用字符串指定属性名称可以解决问题,但存在类型安全问题。如果属性名称发生变化,编译器无法检测到错误。为了解决这个问题,可以使用元模型(Metamodel)进行类型安全的查询。
首先,需要配置 Maven 插件来生成元模型类。在 pom.xml 文件中添加以下插件:
org.hibernate.orm.tooling hibernate-jpamodelgen ${hibernate.version} generate src/main/java
然后,重新编译项目,Hibernate 将会自动生成元模型类,例如 GroupEntity_ 和 UserEntity_。
修改后的 JPA Criteria 查询代码如下:
public ListgetData(final String userCode, final String currentGroupName, final String newGroupName) { final EntityManager entityManager = entityManagerProvider.get(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaQuery cq = cb.createQuery(GroupEntity.class); final Root root = cq.from(GroupEntity.class); // 使用 Join 操作连接 GroupEntity 和 UserEntity Join user = root.join(GroupEntity_.userEntity); cq.select(root).where(cb.or( cb.equal(user.get(UserEntity_.userCode), userCode), // 使用 UserEntity_.userCode 访问 UserEntity 的 userCode 属性 cb.equal(root.get(GroupEntity_.name), currentGroupName), cb.equal(root.get(GroupEntity_.name), newGroupName))); return entityManager.createQuery(cq).getResultList(); }
代码解释:
- Join
user = root.join(GroupEntity_.userEntity);:使用 GroupEntity_.userEntity 代替字符串 "userEntity",可以获得类型安全的 Join 对象。 - cb.equal(user.get(UserEntity_.userCode), userCode):使用 UserEntity_.userCode 代替字符串 "userCode",可以访问 UserEntity 的 userCode 属性,并进行类型安全的比较。
注意事项
- 确保 @ManyToOne 注解中的 fetch 属性设置为 FetchType.LAZY,以避免不必要的性能开销。
- 在使用 Join 操作时,确保连接的属性名称与实体类中的属性名称一致。
- 推荐使用元模型(Metamodel)进行类型安全的查询,以提高代码的可维护性和可靠性。
总结
通过本文,我们学习了如何使用 JPA Criteria 查询获取 ManyToOne 关联实体中的数据。通过 Join 操作,我们可以访问关联实体中的属性,并将其应用于查询条件中。同时,建议使用元模型(Metamodel)进行类型安全的查询。掌握这些技巧可以帮助我们编写更高效、更可靠的 JPA 查询代码。









