java 如何让 HIbernate 获取根实体的所有属性和关联实体的特定属性?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25337884/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make HIbernate fetch all properties of root entity and only specific properties of associated entity?
提问by Volodymyr Levytskyi
I have root entity Hostel
and its single association User owner
.
我有根实体Hostel
及其单一关联User owner
。
When I fetch Hostel
entity I need to eagerly fetch User owner
, but only owner
's 3 properties : userId,firstName,lastName.
当我获取Hostel
实体时,我需要急切地获取User owner
,但只有owner
3 个属性:userId、firstName、lastName。
For now my criteria query is :
现在我的标准查询是:
Criteria criteria = currenSession().createCriteria(Hostel.class);
criteria.add(Restrictions.ge("endDate", Calendar.getInstance()));
if (StringUtils.notNullAndEmpty(country)) {
criteria.add(Restrictions.eq("country", country));
}
Long count = (Long) criteria
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.setProjection(Projections.rowCount()).uniqueResult();
criteria.setFetchMode("owner", FetchMode.SELECT);
criteria.addOrder(Order.desc("rating"));
// needed to reset previous rowCount projection
criteria.setProjection(null);
// retrieve owner association
criteria.createAlias("owner", "owner", JoinType.LEFT_OUTER_JOIN)
.setProjection(
Projections.projectionList()
.add(Projections.property("owner.userId"))
.add(Projections.property("owner.firstName"))
.add(Projections.property("owner.lastName")));
criteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
Next, I do criteria.list()
and I get sql statement which selects only owner
's 3 properties as specified in projection list. But it doesn't select any property of root Hostel
entity.
Generated query is:
接下来,我执行criteria.list()
并得到 sql 语句,该语句仅选择owner
投影列表中指定的 3 个属性。但它不选择根Hostel
实体的任何属性。生成的查询是:
select
owner1_.user_id as y0_,
owner1_.firstName as y1_,
owner1_.lastName as y2_
from
HOSTEL this_
left outer join
USER owner1_
on this_.owner_fk=owner1_.user_id
where
this_.end_date>=?
and this_.country=?
order by
this_.rating desc limit ?
This query doesn't work because it returns five Map
s which are empty. FIve maps are because there are five Hostel
rows that match where condition. I created simple sql query and it works fine so the problem is only here.
此查询不起作用,因为它返回五个Map
空的 s。五张地图是因为有五行Hostel
匹配 where 条件。我创建了简单的 sql 查询,它运行良好,所以问题仅出在这里。
How to force hibernate to fetch all properties of root Hostel
entity and only 3 properties of asociated User owner
entity?
如何强制休眠获取根Hostel
实体的所有属性和关联User owner
实体的仅 3 个属性?
EDITI tried to use getSessionFactory().getClassMetadata(Hostel.class)
but it gave error about mapping enum in Hostel
. So I fallback to list Hostel
properties manually. For now my criteria query is:
编辑我尝试使用getSessionFactory().getClassMetadata(Hostel.class)
但它给出了关于在Hostel
. 所以我回退到Hostel
手动列出属性。现在我的标准查询是:
// retrieve owner association
criteria.createAlias("owner", "owner", JoinType.LEFT_OUTER_JOIN);
criteria.setProjection(Projections.projectionList()
.add(Projections.property("hostelId"))
.add(Projections.property("address"))
.add(Projections.property("country"))
.add(Projections.property("region"))
.add(Projections.property("gender"))
.add(Projections.property("owner.userId"))
.add(Projections.property("owner.firstName"))
.add(Projections.property("owner.lastName")));
List<Hostel> hostels = criteria.list();
for (Hostel hostel : hostels) { // at this line I get error java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.home.hostme.entity.Hostel
User owner = hostel.getOwner();
System.out.println("owner=" + owner);
}
Note that I removed ALIAS_TO_ENTITY_MAP
result transformer. This generated such mysql query :
请注意,我删除了ALIAS_TO_ENTITY_MAP
结果转换器。这生成了这样的 mysql 查询:
select
this_.hostel_id as y0_,
this_.address as y1_,
this_.country as y2_,
this_.region as y3_,
this_.gender as y4_,
owner1_.user_id as y5_,
owner1_.firstName as y6_,
owner1_.lastName as y7_
from
HOSTEL this_
left outer join
USER owner1_
on this_.owner_fk=owner1_.user_id
where
this_.end_date>=?
and this_.country=?
order by
this_.rating desc limit ?
At for-each loop get such an error:
在 for-each 循环中得到这样的错误:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.home.hostme.entity.Hostel
at com.home.hostme.dao.impl.HostelDaoImpl.findHostelBy(HostelDaoImpl.java:168)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at com.sun.proxy.$Proxy64.findHostelBy(Unknown Source)
at com.home.hostme.service.HostelService.findHostelBy(HostelService.java:27)
at com.home.hostme.service.HostelService$$FastClassByCGLIB$db5b21.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:96)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at com.home.hostme.service.HostelService$$EnhancerByCGLIB$af3bc10.findHostelBy(<generated>)
at com.home.hostme.web.hostel.HostelController.doSearch(HostelController.java:94)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
This error means that I don't have type Hostel
in resulting list hostels
.
I even tried to find out class of elements in resulting list 'hostels' with this:
这个错误意味着我Hostel
在结果列表中没有类型hostels
。我什至试图在结果列表“旅馆”中找出元素类别:
List hostels = criteria.list();
System.out.println("firstRow.class=" + hostels.get(0).getClass());
It printed:
它打印:
firstRow.class=class [Ljava.lang.Object;
Then I tried to set ALIAS_TO_ENTITY_MAP
for new ProjectionList, but resulting list 'hostels' was:
然后我尝试设置ALIAS_TO_ENTITY_MAP
新的 ProjectionList,但结果列表 'hostels' 是:
[{}, {}, {}, {}, {}]
Five empty maps. Five because there are 5 rows in db(table hostel) matching where clause.
五张空地图。五个是因为 db(table hostel) 中有 5 行匹配 where 子句。
Then I deleted projection list completely and hibernate retrieved 5 hostels and 5 associated User owner
s and owner
's images as expected.
然后我完全删除了投影列表,并且 hibernate按预期检索了 5 个旅馆和 5 个相关的User owner
s 和owner
's 图像。
THE PROBLEM is how to stop hibernate retrieve associated Image
entity of associated User owner
. The best would be to fetch only 3 specific props of associated User owner
.
问题是如何停止 hibernate 检索关联的关联Image
实体User owner
。最好的方法是只获取相关的 3 个特定道具User owner
。
Thank you!
谢谢!
回答by Serge Ballesta
You can do it with a direct query :
您可以通过直接查询来实现:
Query query = session.createQuery("SELECT hostel, owner.id, owner.firstname, "
+"owner.lastname FROM Hostel hostel LEFT OUTER JOIN hostel.ower AS owner");
List list = query.list();
generates a SQL like :
生成一个 SQL,如:
select hostel0_.id as col_0_0_, user1_.id as col_1_0_, user1_.firstname as col_2_0_, user1_.lastname as col_3_0_, hostel0_.id as id1_0_, hostel0_.name as name2_0_, ..., hostel0_.owner_id as user_id4_0_ from Hostel hostel0_ left outer join User user1_ on user1_.id=hostel0_.owner_id
选择 hostel0_.id 为 col_0_0_,user1_.id 为 col_1_0_,user1_.firstname 为 col_2_0_,user1_.lastname 为 col_3_0_,hostel0_.id 为 id1_0_,hostel0_.name 为 name2_0_,...,hostel0_.4host_id 为左 user_id Hostel_0外部加入用户 user1_ 在 user1_.id=hostel0_.owner_id
with all the fields from Hostel and only required fields from User.
包含来自 Hostel 的所有字段和来自 User 的仅必填字段。
The list obtained with criteria.list()
is a List<Object[]>
whose rows are
[ Hostel, Integer, String, String]
获得的列表criteria.list()
是一个,List<Object[]>
其行是
[ Hostel, Integer, String, String]
You can obtain something using Criteria
, but Criteria
are more strict than queries. I could not find any API that allows to mix entities and fields. So as far as I know, it is not possible to get rows containing an entity (Hostels) and separate fields from an association (owner.userId, owner.firstName, owner.lastName).
您可以使用 获得一些东西Criteria
,但Criteria
比查询更严格。我找不到任何允许混合实体和字段的 API。因此,据我所知,不可能从关联中获取包含实体 (Hostels) 和单独字段的行(owner.userId、owner.firstName、owner.lastName)。
The only way I can imagine would be to explicitely list all fields from Hostels :
我能想象的唯一方法是明确列出 Hostels 的所有字段:
criteria.createAlias("owner", "owner", JoinType.LEFT_OUTER_JOIN)
.setProjection(
Projections.projectionList()
.add(Projections.property("hostelId"))
.add(Projections.property("country"))
.add(Projections.property("endDate"))
...
... all other properties from Hostel
...
.add(Projections.property("owner.userId"))
.add(Projections.property("owner.firstName"))
.add(Projections.property("owner.lastName")));
You can automate it a little by using Metadata (don't forget the id ...) - note : I use aliased projection only to be able later to use a wrapper class, if you directly use the scalar values, you can safely omit the Projection.alias
:
您可以使用元数据将其自动化一点(不要忘记 id ...)-注意:我使用别名投影只是为了以后能够使用包装类,如果您直接使用标量值,则可以安全地省略的Projection.alias
:
ProjectionList hostelProj = Projections.projectionList();
String id = sessionFactory.getClassMetadata(Hostel.class)
.getIdentifierPropertyName();
hostelProperties.add(Projections.alias(Projections.property(id),id));
for (String prop: sessionFactory.getClassMetadata(Hostel.class).getPropertyNames()) {
hostelProperties.add(Projections.alias(Projections.property(prop), prop));
}
Criteria criteria = session.createCriteria(Hostel.class);
criteria.createAlias("owner", "owner", JoinType.LEFT_OUTER_JOIN);
criteria.setProjection(
Projections.projectionList()
.add(hostelProj)
.add(Projections.property("owner.id"))
.add(Projections.property("owner.firstName"))
.add(Projections.property("owner.lastName")));
List list = criteria.list();
That way correctly generates
这样正确生成
select this_.id as y0_, this_.name as y1_, ..., this_.user_id as y3_, owner1_.id as y4_, owner1_.firstname as y5_, owner1_.lastname as y6_ from hotels this_ left outer join users owner1_ on this_.user_id=owner1_.id
选择 this_.id 为 y0_,this_.name 为 y1_,...,this_.user_id 为 y3_,owner1_.id 为 y4_,owner1_.firstname 为 y5_,owner1_.lastname 为 y6_ 来自酒店 this_ 离开外部加入用户 owner1_ on this_ .user_id=owner1_.id
But you will not be able to use criteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
because the resultset is not exactly the image of the fields from Hostel
(even without the aliases).
In fact the list is a List<Object[]>
with rows containing all individual fields from Hostel
followed by the 3 required fields from owner
.
但是您将无法使用,criteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
因为结果集并不完全是来自字段的图像Hostel
(即使没有别名)。事实上,该列表是一个List<Object[]>
包含所有单独字段的行,Hostel
然后是 3 个必填字段owner
。
You will have to add a wrapper class containing a Hostel
and the 3 other fields to make use of an AliasToBeanResultTransformer
and get true Hostel
objects :
您必须添加一个包含 aHostel
和其他 3 个字段的包装类,以使用 anAliasToBeanResultTransformer
并获取真正的Hostel
对象:
public class HostelWrapper {
private Hostel hostel;
private int owner_id;
private String owner_firstName;
private String owner_lastName;
public HostelWrapper() {
hostel = new Hostel();
}
public Hostel getHostel() {
return hostel;
}
public void setId(int id) {
hostel.setId(id);
}
public void setOwner(User owner) {
hostel.setOwner(owner);
}
// other setters for Hostel fields ...
public int getOwner_id() {
return owner_id;
}
public void setOwner_id(Integer owner_id) {
// beware : may be null because of outer join
this.owner_id = (owner_id == null) ? 0 : owner_id;
}
//getters and setters for firstName and lastName ...
}
And then you can successfully write :
然后你可以成功地写:
criteria.setResultTransformer(new AliasToBeanResultTransformer(HostelWrapper.class));
List<HostelWrapper> hostels = criteria.list();
Hostel hostel = hostels.get(0).getHostel();
String firstName = hostels.get(0).getFirstName();
I could verify that when there is no owner hostel.getOwner()
is null, and when there is one, hostel.getOwner().getId()
is equal to getOwner_id()
and that this access does not generate any extra query. But any access to an other field of hostel.getOwner()
, even firstName
or lastName
generates one because the User
entity was not loaded in session.
我可以验证当没有所有者时hostel.getOwner()
为空,当有一个时,hostel.getOwner().getId()
等于getOwner_id()
并且此访问不会生成任何额外的查询。但是,任何访问的其他领域hostel.getOwner()
,甚至firstName
或lastName
产生,因为一个User
实体并没有在会议上加载。
The most common usage should be :
最常见的用法应该是:
for (HostelWrapper hostelw: criteria.list()) {
Hostel hostel = hostelw.getHostel();
// use hostel, hostelw.getOwner_firstName and hostelw.getOwner_lastName
}
回答by Volodymyr Levytskyi
@Serge Ballesta solved my issue but here is my final working code:
@Serge Ballesta 解决了我的问题,但这是我的最终工作代码:
Criteria criteria = currenSession().createCriteria(Hostel.class);
criteria.add(Restrictions.ge("endDate", Calendar.getInstance()));
if (StringUtils.notNullAndEmpty(country)) {
criteria.add(Restrictions.eq("country", country));
}
Long count = (Long) criteria
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.setProjection(Projections.rowCount()).uniqueResult();
// mark query as readonly
criteria.setReadOnly(true);
// descendingly sort result by rating property of Hostel entity
criteria.addOrder(Order.desc("rating"));
// reset rowCount() projection
criteria.setProjection(null);
ProjectionList hostelProjList = Projections.projectionList();
ClassMetadata hostelMetadata = getSessionFactory().getClassMetadata(
Hostel.class);
// add primary key property - hostelId
hostelProjList.add(Projections.property(hostelMetadata
.getIdentifierPropertyName()), "hostelId");
// add all normal properties of Hostel entity to retrieve from db
for (String prop : hostelMetadata.getPropertyNames()) {
//skip associations
if (!prop.equals("owner") && !prop.equals("images")
&& !prop.equals("requests") && !prop.equals("feedbacks"))
hostelProjList.add(Projections.property(prop), prop);
}
// add properties of User owner association to be retrieved
hostelProjList
.add(Projections.property("owner.userId"), "owner_id")
.add(Projections.property("owner.firstName"), "owner_firstName")
.add(Projections.property("owner.lastName"), "owner_lastName");
// create alias to retrieve props of User owner association
criteria.createAlias("owner", "owner", JoinType.LEFT_OUTER_JOIN);
criteria.setProjection(hostelProjList);
criteria.setResultTransformer(new AliasToBeanResultTransformer(
HostelWrapper.class));
List<HostelWrapper> wrappers = criteria.list();
And my HostelWrapper
is :
而我的HostelWrapper
是:
public class HostelWrapper {
private Hostel hostel;
private int owner_id;
private String owner_firstName;
private String owner_lastName;
public HostelWrapper() {
hostel = new Hostel();
}
public Hostel getHostel() {
return hostel;
}
public void setHostelId(Integer hostelId) {
this.hostel.setHostelId(hostelId);
}
public void setCountry(String country) {
this.hostel.setCountry(country);
}
public int getOwner_id() {
return owner_id;
}
public void setOwner_id(Integer owner_id) {
this.owner_id = owner_id == null ? 0 : owner_id;
}
public String getOwner_firstName() {
return owner_firstName;
}
public void setOwner_firstName(String owner_firstName) {
this.owner_firstName = owner_firstName;
}
public String getOwner_lastName() {
return owner_lastName;
}
public void setOwner_lastName(String owner_lastName) {
this.owner_lastName = owner_lastName;
}
This HostelWrapper
is used by AliasToBeanResultTransformer
to map hibernate resultset to entities.
这HostelWrapper
用于AliasToBeanResultTransformer
将休眠结果集映射到实体。
My final conclusion is that HQL is right way to go when you want to set projection on association.
With AliasToBeanResultTransformer
you are bound to properties names and with hql is the same. Benefit is that HQL is much easier to write.
我的最终结论是,当您想在关联上设置投影时,HQL 是正确的方法。与AliasToBeanResultTransformer
您绑定到属性名称和 hql 是相同的。好处是 HQL 更容易编写。
回答by Siva Kumar
As per my observation you cant get result as per you require.
根据我的观察,您无法获得所需的结果。
We can done using the following steps:
我们可以使用以下步骤完成:
String[] propertyNames = sessionFactory.getClassMetadata(Hostel.class).getPropertyNames();
ProjectionList projectionList = Projections.projectionList();
for (String propString : propertyNames) {
projectionList.add(Projections.property(propString));
}
Please change the code as per below
请按照以下更改代码
criteria.createAlias("owner", "owner", JoinType.LEFT_OUTER_JOIN)
.setProjection(
projectionList
.add(Projections.property("owner.userId"))
.add(Projections.property("owner.firstName"))
.add(Projections.property("owner.lastName")));
Please try and lemme know.
请尝试让我知道。