如何使HIbernate获取根实体的所有属性并仅获取关联实体的特定属性?
我有root实体Hostel
和它的单一关联User owner
。
当我获取Hostel
实体时,我需要急切地获取User owner
,但只有owner
的3个属性:userId,firstName,lastName。
现在我的标准查询是:
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);
接下来,我执行criteria.list()
并获取sql语句,该语句仅选择投影列表中指定的owner
的3个属性。 但它没有选择root 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 ?
此查询不起作用,因为它返回五个为空的Map
。 FIve地图是因为有五个Hostel
行符合条件。 我创建了简单的SQL查询,它工作正常所以问题只在这里。
如何强制hibernate获取root Hostel
实体的所有属性,只有3个属性的isociated User owner
实体?
编辑我试图使用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 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); }
请注意,我删除了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 ?
在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$$74db5b21.invoke() 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$1.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$$7af3bc10.findHostelBy() 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)
此错误意味着我在结果列表hostels
没有类型hostels
。 我甚至试图在结果列表’hostels’中找出一类元素:
List hostels = criteria.list(); System.out.println("firstRow.class=" + hostels.get(0).getClass());
它打印:
firstRow.class=class [Ljava.lang.Object;
然后我尝试为新的ProjectionList设置ALIAS_TO_ENTITY_MAP
,但结果列表’hostels’是:
[{}, {}, {}, {}, {}]
五个空地图。 五,因为db(table hostel)中有5行匹配where子句。
然后我完全删除了投影列表,并按预期检索了5个旅馆和5个相关的User owner
和owner
的图像。
问题是如何阻止hibernate检索关联的User owner
关联Image
实体。 最好的方法是只获取相关User owner
3个特定道具。
谢谢!
您可以使用直接查询来执行此操作:
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();
生成如下的SQL:
选择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_.owner_id为user_id4_0_ from Hostel hostel0_ left user1_.id = hostel0_.owner_id上的外连接用户user1_
来自旅馆的所有字段和用户只需要的字段。
使用criteria.list()
获得的列表是List
其行为[ Hostel, Integer, String, String]
您可以使用Criteria
获取某些内容,但Criteria
比查询更严格。 我找不到任何允许混合实体和字段的API。 所以据我所知,不可能获得包含实体(Hostels)的行和来自关联的单独字段(owner.userId,owner.firstName,owner.lastName)。
我能想象的唯一方法是明确列出旅馆的所有字段:
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")));
您可以使用元数据自动化它(不要忘记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();
这种方式正确生成
选择this_.id为y0_,this_.name为y1_,…,this_.user_id为y3_,owner1_.id为y4_,owner1_.firstname为y5_,owner1_.lastname为y6_来自酒店this_ left outer join users owner1_ on this_ .user_id = owner1_.id
但是您将无法使用criteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)
因为结果集不完全是来自Hostel
的字段的图像(即使没有别名)。 事实上,列表是一个List
,其中包含来自Hostel
所有单个字段的行,后跟owner
的3个必填字段。
您将不得不添加一个包含Hostel
和其他3个字段的包装类来使用AliasToBeanResultTransformer
并获取真正的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 ... }
然后你可以成功写:
criteria.setResultTransformer(new AliasToBeanResultTransformer(HostelWrapper.class)); List hostels = criteria.list(); Hostel hostel = hostels.get(0).getHostel(); String firstName = hostels.get(0).getFirstName();
我可以validation,当没有所有者hostel.getOwner()
为null,并且当有一个时, hostel.getOwner().getId()
等于getOwner_id()
,并且此访问不会生成任何额外的查询。 但是对hostel.getOwner()
的其他字段的任何访问,甚至firstName
或lastName
生成一个,因为在会话中没有加载User
实体。
最常见的用法应该是:
for (HostelWrapper hostelw: criteria.list()) { Hostel hostel = hostelw.getHostel(); // use hostel, hostelw.getOwner_firstName and hostelw.getOwner_lastName }
@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 wrappers = criteria.list();
我的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; }
HostelWrapper
使用此HostelWrapper
将hibernate结果集映射到实体。
我的最后结论是,当你想在关联上设置投影时,HQL是正确的方法。 使用AliasToBeanResultTransformer
您将绑定属性名称,并且与hql相同。 好处是HQL更容易编写。
根据我的观察,你不能按照你的要求得到结果。
我们可以使用以下步骤完成:
String[] propertyNames = sessionFactory.getClassMetadata(Hostel.class).getPropertyNames(); ProjectionList projectionList = Projections.projectionList(); for (String propString : propertyNames) { projectionList.add(Projections.property(propString)); }
请按以下更改代码
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")));
请尝试和lemme知道。