使用Jakarta Criteria关联表时遇SemanticException错误求助
问题:Jakarta Criteria表关联报错
Attribute [com.amfpromotora.entity.EmprestimosTabelas#tabcodigo(BASIC)] is not joinable 执行Jakarta Criteria表关联操作时触发如下错误:
org.hibernate.query.SemanticException: Attribute [com.amfpromotora.entity.EmprestimosTabelas#tabcodigo(BASIC)] is not joinable
预期实现的SQL关联逻辑:
FROM emprestimos_tabelas t JOIN emprestimos_bancos b ON t.banccodigo = b.banccodigo JOIN emprestimos_comiss_agenciador c ON t.tabcodigo = c.tabcodigo JOIN emprestimos_comiss_correspondente cc ON c.tabcodigo = cc.tabcodigo AND c.parmin = cc.parmin
实体类代码:
EmprestimosTabelas.java
@Entity @Table(name = "emprestimos_tabelas", schema = "public", catalog = "joinconsig") @Data public class EmprestimosTabelas { @Id @Column(name = "tabcodigo") private int tabcodigo; @ManyToOne @JoinColumn(name = "banccodigo") private EmprestimosBancos banccodigo; @Basic @Column(name = "tabnome") private String tabnome; ... }
EmprestimosComissAgenciador.java
@Entity @Table(name = "emprestimos_comiss_agenciador", schema = "public", catalog = "joinconsig") @IdClass(EmprestimosComissAgenciadorPK.class) @Data public class EmprestimosComissAgenciador { @Id @ManyToOne @JoinColumn(name = "tabcodigo") private EmprestimosTabelas tabcodigo; @Id @Column(name = "parmin") private int parmin; ... }
EmprestimosComissCorrespondente.java
@Entity @Table(name = "emprestimos_comiss_correspondente", schema = "public", catalog = "joinconsig") @IdClass(EmprestimosComissCorrespondentePK.class) @Data public class EmprestimosComissCorrespondente { @Id @Column(name = "tabcodigo") private int tabcodigo; @Id @Column(name = "parmin") private int parmin; ... }
当前CriteriaBuilder代码:
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<CommissionTable> query = cb.createQuery(CommissionTable.class); Root<EmprestimosTabelas> root = query.from(EmprestimosTabelas.class); List<Predicate> predicates = new ArrayList<>(); Join<EmprestimosTabelas, EmprestimosBancos> bancosJoin = root.join("banccodigo"); Join<EmprestimosComissAgenciador, EmprestimosTabelas> comissAgenciadorJoin = root.join("tabcodigo"); Join<EmprestimosComissAgenciador, EmprestimosComissCorrespondente> comissCorrespondenteJoin = comissAgenciadorJoin.join("tabcodigo");
解决方案
问题根源
- 错误的关联方向:你尝试从
EmprestimosTabelas的tabcodigo(基础int类型字段)执行join,但该字段是实体主键,并非关联实体属性,无法直接用于关联操作。按照你的SQL逻辑,emprestimos_comiss_agenciador通过外键关联到emprestimos_tabelas,关联方向应为EmprestimosTabelas反向关联EmprestimosComissAgenciador。 - 缺失反向关联属性:
EmprestimosTabelas中未定义与EmprestimosComissAgenciador的反向关联,导致Criteria无法识别合法的关联关系。 EmprestimosComissCorrespondente关联缺失:该实体的tabcodigo和parmin是基础类型字段,未关联到对应实体,无法直接通过Criteria进行join。
修复步骤
1. 补充实体间的关联关系
- 修改
EmprestimosTabelas.java,添加与EmprestimosComissAgenciador的反向关联:
@Entity @Table(name = "emprestimos_tabelas", schema = "public", catalog = "joinconsig") @Data public class EmprestimosTabelas { @Id @Column(name = "tabcodigo") private int tabcodigo; @ManyToOne @JoinColumn(name = "banccodigo") private EmprestimosBancos banccodigo; @Basic @Column(name = "tabnome") private String tabnome; // 新增反向关联,mappedBy对应EmprestimosComissAgenciador中的关联属性名 @OneToMany(mappedBy = "tabcodigo") private List<EmprestimosComissAgenciador> comissAgenciadores; ... }
- 修改
EmprestimosComissCorrespondente.java,将基础类型ID改为关联实体:
@Entity @Table(name = "emprestimos_comiss_correspondente", schema = "public", catalog = "joinconsig") @IdClass(EmprestimosComissCorrespondentePK.class) @Data public class EmprestimosComissCorrespondente { @Id @ManyToOne @JoinColumn(name = "tabcodigo") private EmprestimosTabelas tabcodigo; @Id @Column(name = "parmin") private int parmin; ... }
2. 修正CriteriaBuilder关联代码
基于实体关联关系,调整join的方向和属性:
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<CommissionTable> query = cb.createQuery(CommissionTable.class); Root<EmprestimosTabelas> root = query.from(EmprestimosTabelas.class); List<Predicate> predicates = new ArrayList<>(); // 关联bancos,此部分逻辑正确 Join<EmprestimosTabelas, EmprestimosBancos> bancosJoin = root.join("banccodigo"); // 通过新增的反向关联属性关联到comissAgenciador Join<EmprestimosTabelas, EmprestimosComissAgenciador> comissAgenciadorJoin = root.join("comissAgenciadores"); // 关联comissCorrespondente,同时补充parmin相等条件 Join<EmprestimosComissAgenciador, EmprestimosComissCorrespondente> comissCorrespondenteJoin = comissAgenciadorJoin.join("tabcodigo", JoinType.INNER); predicates.add(cb.equal(comissAgenciadorJoin.get("parmin"), comissCorrespondenteJoin.get("parmin"))); // 将条件加入查询 query.where(predicates.toArray(new Predicate[0]));
关键说明
- 当外键在目标表时,主表必须定义反向关联(
@OneToMany),Criteria才能识别可用于join的关联属性。 - 禁止直接使用基础类型的主键字段执行join,必须基于实体间通过
@ManyToOne/@OneToMany等注解定义的关联属性构建关联。 - 对于复合主键的关联,要么让实体关联属性覆盖所有主键字段,要么通过
Predicate补充额外的相等条件。
内容的提问来源于stack exchange,提问作者Leonardo Schmidt
相关产品推荐
相关产品推荐

