我正在创建一个包含多个表的复杂查询,需要列出结果。通常,我使用EntityManager
并将结果映射到JPA-Representation:
UserEntity user = em.find(UserEntity.class, "5");
然后我可以访问所有值,因为用户UserEntity
类定义了它。但是,如何访问本机多表查询返回的字段值?我得到的是一个对象列表。到目前为止这很好,但对象是什么“是”?阵列?地图?采集? ...
//simpleExample
Query query = em.createNativeQuery("SELECT u.name,s.something FROM user u, someTable s WHERE s.user_id = u.id");
List list = query.getResultList();
//do sth. with the list, for example access "something" for every result row.
我想答案很简单,但是大多数示例只显示了直接转换为targetClass时的用法。
PS:在示例中,我当然可以使用类映射。但在我的情况下,someTable
不是由JPA管理的,因此我没有该实体,也没有它的类表示,因为我加入了20个表,我不想要创建所有类只是为了访问值。
答案 0 :(得分:52)
一般规则如下:
select
包含单个表达式并且它是一个实体,那么结果就是该实体select
包含单个表达式并且它是基元,那么结果就是原始select
包含多个表达式,则结果为Object[]
,其中包含相应的基元/实体因此,在您的情况下,list
是List<Object[]>
。
答案 1 :(得分:36)
由于JPA 2.0可以使用TypedQuery
:
TypedQuery<SimpleEntity> q =
em.createQuery("select t from SimpleEntity t", SimpleEntity.class);
List<SimpleEntity> listOfSimpleEntities = q.getResultList();
for (SimpleEntity entity : listOfSimpleEntities) {
// do something useful with entity;
}
答案 2 :(得分:8)
以上查询返回Object []列表。因此,如果您想从列表中获取u.name和s.something,则需要迭代并为相应的类转换这些值。
答案 3 :(得分:6)
如果您需要一种更方便的方式来访问结果,可以将任意复杂的SQL查询的结果转换为Java类,而且麻烦最小:
Query query = em.createNativeQuery("select 42 as age, 'Bob' as name from dual",
MyTest.class);
MyTest myTest = (MyTest) query.getResultList().get(0);
assertEquals("Bob", myTest.name);
该类需要声明为@Entity,这意味着您必须确保它具有唯一的@Id。
@Entity
class MyTest {
@Id String name;
int age;
}
答案 4 :(得分:0)
这是关于什么对我有用的样本。我认为在实体类中需要put方法来将sql列映射到java类属性。
//simpleExample
Query query = em.createNativeQuery(
"SELECT u.name,s.something FROM user u, someTable s WHERE s.user_id = u.id",
NameSomething.class);
List list = (List<NameSomething.class>) query.getResultList();
实体类:
@Entity
public class NameSomething {
@Id
private String name;
private String something;
// getters/setters
/**
* Generic put method to map JPA native Query to this object.
*
* @param column
* @param value
*/
public void put(Object column, Object value) {
if (((String) column).equals("name")) {
setName(String) value);
} else if (((String) column).equals("something")) {
setSomething((String) value);
}
}
}
答案 5 :(得分:0)
我遇到了同样的问题,发现的一个简单解决方案是:
with all_dates as (
select datefromparts(year(getdate()), month(getdate()), 1) dt, 0 lvl
union all
select dateadd(month, - lvl - 1, dt), lvl + 1
from all_dates
where lvl < 3
)
select * from all_dates
This gives me following result:
2020-11-01 0
2020-10-01 1
2020-08-01 2
2020-05-01 3
I want only:
2020-11-01 0
2020-10-01 1
2020-09-01 2
我知道这绝对不是最优雅的解决方案,也不是最佳实践,但它至少对我有用。
答案 6 :(得分:0)
如果您创建一个具有所有必需属性的 bean 并使用 Java 8+ 流转换结果会怎样?
像这样:
public class Something {
private String name;
private String something;
// getters and setters
}
然后:
import javax.persistence.Query;
...
Query query = em.createNativeQuery("SELECT u.name,s.something FROM user u, someTable s WHERE s.user_id = u.id", Something.class);
List<?> list = query.getResultList();
return list
.stream()
.map(item -> item instanceof Something ? (Something) item : null)
.collect(Collectors.toList());
这样,您就无需返回 List<Object[]>
或使用 @SuppressWarnings("unchecked")
附言:
1 - 我知道这篇文章很老了。但是......我在 2021 年来到这里,所以其他人也会来这里 =)
2 - 这是错误的还是不好的做法?让我知道:D
答案 7 :(得分:0)
您还可以将您的 hibernate 更新到高于 5.4.30.final 的版本