简体   繁体   English

JPA CriteriaQuery联接三个表不能直接导航

[英]JPA CriteriaQuery join three tables not directly navigable

i need to translate this sql query to jpa criteria: 我需要将此SQL查询转换为jpa条件:

SELECT tbl1.id_t1, tbl2.name, tbl3.name, tbl4.symbol, tbl1.limit, tbl1.value, tbl1.uncertainty 
FROM table_1 AS tbl1
JOIN table_2 AS tbl2 ON tbl2.id_t2=tbl1.id_t2
JOIN table_3 AS tbl3 ON tbl3.id_t3=tbl1.id_t3
JOIN table_4 AS tbl4 ON tbl4.id_t4=tbl1.id_t4
WHERE (tbl2.id_l=1 AND tbl3.id_l=1) AND tbl1.id_s=1;

my mapping between pojo and database table are as follows: 我在pojo和数据库表之间的映射如下:

Table_1 表格1

@Entity
@Table("table_1")
public class Table1 {
 @Id
 @Column(name="id_t1")
 private Long idRowT1
 @ManyToOne
 @JoinColumn(name="id_t2")
 private Table2 tbl2;
 @ManyToOne
 @JoinColumn(name="id_t3")
 private Table3 tbl3;
 @ManyToOne
 @JoinColumn(name="id_t4")
 private Table4 tbl4;
 @Column(name="limit")
 private String limit;
 @Column(name="value")
 private String value;
 @Column(name="uncertainty")
 private String uncertainty;

 // getter and setter
}

Table_2 表_2

@Entity
@Table("table_2")
public class Table2 {
 @Id
 @Column(name="id_t2")
 private Long idT2;

 // getter and setter
}

Table_2_lang 表_2_lang

@Entity
@Table("table_2_lang")
@IdClass(Table2LangPK.class)
public class Table2Lang {
 @Id
 @Column(name="id_t2")
 private Long idT2;
 @Id
 @Column(name="id_l")
 private Lang l;
 @Column(name="name")
 private String name;

 // getter and setter
}

Table_3 表3

@Entity
@Table("table_3")
public class Table3 {
 @Id
 @Column(name="id_t3")
 private Long idT3;

 // getter and setter
}

Table_3_lang Table_3_lang

@Entity
@Table("table_3_lang")
@IdClass(Table3LangPK.class)
public class Table3Lang {
 @Id
 @Column(name="id_t3")
 private Long idT3;
 @Id
 @Column(name="id_l")
 private Lang l;
 @Column(name="name")
 private String name;

 // getter and setter
}

Table_4 表_4

@Entity
@Table("table_4")
public class Table4 {
 @Id
 @Column(name="id_t4")
 private Long idT4;
 @Column(name="name")
 private String name;

 // getter and setter
}

To send data from business layer to front-end i'm using value objects defined as follows: 要将数据从业务层发送到前端,我正在使用如下定义的值对象:

Simple entity 简单实体

public class SimpleEntityVO {
 private Long entityId;
 private String name;

 // getter and setter
}

Complex Entity 复杂实体

public class SimpleEntityVO {
 private Long entityId;
 private SimpleEntityVO tbl2VO;
 private SimpleEntityVO tbl3VO;
 private SimpleEntityVO tbl4VO;
 // ... other field of table_1

 // getter and setter
}

In my EJB i need to implement a method that return a list of ComplexEntityVO starting from Table_1 在我的EJB中,我需要实现一个从Table_1开始返回ComplexEntityVO列表的方法。

...

private CriteriaBuilder cB = eM.getCriteriaBuilder();

public List<ComplexEntityVO> findAll(Long id_s, Long id_l) {
 CriteriaQuery<ComplexEntityVO> cQ = cB.createQuery(ComplexEntityVO.class)
 Root<Table1> tbl1Root = cQ.from(Table1.class);

 // UPDATE BEGIN
 Root<Table2Lang> tbl2Root = cQ.from(Table2Lang.class);
 ...

 Selection<SimpleEntityVO> sESTbl2 = cB.construct(SimpleEntityVO.class, tbl2Root.get(Table2Lang_.id_t2), tbl2Root.get(Table2Lang_.name));
  // The selection for table_3_lang and table_4 are the same
 // UPDATE END

 TypedQuery<ComplexEntityVO> tQ = eM.createQuery(cQ);
}

...

To achieve the results i've tried with join betwen Table1 and Table2Lang, tried with selection like the one exposed below 为了达到我尝试将Table1和Table2Lang连接起来的结果,并尝试了如下所示的选择

`Selection<SimpleEntityVO> sES = cB.construct(SimpleEntityVO.class, ...);`

using Root for lang table, tried with solution exposed here 使用lang表的Root,尝试在此处公开的解决方案

https://community.oracle.com/message/10795956#10795956 https://community.oracle.com/message/10795956#10795956

but when i try to execute this statement 但是当我尝试执行此语句时

`cQ.select(cB.construct(ComplexEntityVO.class, id_t1, SimpleEntityVO)`

or this 或这个

`cQ.multiselect(...)`

i get the: IllegalArgumentException 我得到了: IllegalArgumentException

Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: , near line 1, column 64
[select new com.example.vo.ComplexEntityVO(generatedAlias0.id_t1,
 new com.example.labims.vo.SimpleEntityVO(generatedAlias1.table2.id_t2, generatedAlias1.name),
 new com.example.vo.SimpleEntityVO(generatedAlias2.table_3.id_t3, generatedAlias2.name),
 new com.example.vo.SimpleEntityVO(generatedAlias3.id_t4, generatedAlias3.name),
 generatedAlias0.limit, generatedAlias0.value, generatedAlias0.uncertainty)
 from com.example.Table1 as generatedAlias0, 
 com.example.model.Table2Lang as generatedAlias1, 
 com.example.model.Table3Lang as generatedAlias2,
 com.example.model.Table4 as generatedAlias3
 where ( generatedAlias0.id_s=:param0 ) and ( ( generatedAlias1.lang.id_l=:param1 ) and ( generatedAlias2.lang.id_l=:param1 ) )]

From the cause of execption understanded that i can't instanciate new object inside select or multiselect statement, but i don't find a way to achieve the original SQL query using criteria API. 从执行的原因了解到,我无法实例化selectmultiselect语句内的新对象,但是我找不到使用标准API实现原始SQL查询的方法。

UPDATE i've added an excerpt of what i've tried to achieve the result between //UPDATE BEGIN and //UPDATE END UPDATE我添加了我试图实现//UPDATE BEGIN//UPDATE END之间结果的摘录

我认为让hibernate显示sql == true并通过控制台进行查询,测试显示查询您的数据库并发现错误hbernate无法生成查询正确

There are two approaches to solve this problem. 有两种方法可以解决此问题。

  1. Add a constructor method to ComplexEntityVO like this: 像这样向ComplexEntityVO添加一个构造函数方法:

     public ComplexEntityVO(Long id, Long simpleId2, String simpleName2 /* etc ... */) { this.simpleEntityVo = new SimpleEntityVO(simpleId2, simpleName2); // etc for other associations } 
  2. add a ProjectionList to your query, return a List<Object[]> instead of a List<ComplexEntityVO> and then iterate over the results like so 向您的查询中添加一个ProjectionList,返回List<Object[]>而不是List<ComplexEntityVO> ,然后像这样遍历结果

     for(Object[] o: results) { ComplexEntityVO cvo = new ComplexEntityVO((Long)o[0]); new SimpleEntityVO vo2 = new SimpleEntityVO((Long) o[1], (String) o[2]); cvo.setTbl2VO(vo2); // ... etc for other associations } 

Although the second is uglier I would prefer it, since it is more flexible, and allows more opportunities for debugging, logging etc. 尽管第二个比较丑陋,但我更喜欢它,因为它更灵活,并且为调试,日志记录等提供了更多机会。

See AliasToBeanResultTransformer(MyDTO.class) fails to instantiate MyDTO 请参见AliasToBeanResultTransformer(MyDTO.class)无法实例化MyDTO。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM