如何使用Hibernate检索一组成员对象?

这个问题是跟进我之前的问题 。 我需要检索复杂类的列表。 每个都有几个集合,只应检索其中的特定数量。 我已经阅读了这些问题1,2的答案,但没有一个能解决我的问题。

我需要查找特定群组中位于特定位置的学生列表,以及他们在其地址中的电话号码。 我还需要显示每个学生到特定坐标的距离。

以下代码工作正常,唯一的问题是我无法检索对象列表,例如电子邮件列表,组列表和每个学生的电话列表。

@Entity public class Student implements java.io.Serializable { private static final long serialVersionUID = -23949494858373847L; @Id @GeneratedValue String id; String name; @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinTable(name = "student_groups", joinColumns = { @JoinColumn(name = "id", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "groupId", nullable = false, updatable = false) }) Set groups = new HashSet(0); .. } @Entity public class Address implements java.io.Serializable { private static final long serialVersionUID = -274634747474623637L; @Id @GeneratedValue String addId; @Id @ManyToOne @JoinColumn(name = "id", nullable = false) Student student; @ManyToOne @JoinColumn(name = "locId", nullable = false) Location location; double latitude; double longitude; String address; @OneToMany(mappedBy = "phoneOwner", fetch = FetchType.EAGER) Set phones = new HashSet(); String formula = "( 6371 * acos ( cos ( radians(" + lat + ") ) * cos( radians( this_.latitude ) ) * cos( radians( this_.longitude ) - radians(" + lan + ") ) +" + "sin ( radians(" + lat + ") ) * sin( radians( this_.latitude ) ) ) ) as distance"; Session session = sessionFactory.getCurrentSession(); ProjectionList pl = Projections .projectionList() .add(Projections.property("std.id").as("id")) .add(Projections.property("std.name").as("name")) .add(Projections.property("addr.address").as( "address")) .add(Projections.property("location.name").as("location")) .add(Projections.property("location.city").as("city")) .add(Projections.property("location.latitude").as("latitude")) .add(Projections.property("location.longitude").as("longitude")) .add(Projections.sqlProjection(formula, new String[] { "distance" }, new Type[] { new DoubleType() })); List students = (List<Students) session .createCriteria(Address.class, "addr") .createAlias("addr.student", "std") .createAlias("std.groups", "group") .createAlias("addr.location", "location") .setProjection(pl) .setFetchMode("group", FetchMode.JOIN) .add(Restrictions.ilike("group.name", groupName)) .add(Restrictions.eq("location.id", locId)) .setResultTransformer( new AliasToBeanResultTransformer(Students.class)) .list(); 

默认的hibernate类不会传输嵌套对象,如果遇到性能问题,应该尝试使用代码。

看看这个链接和这个改进版本的链接 。

 import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.property.PropertyAccessor; import org.hibernate.property.PropertyAccessorFactory; import org.hibernate.property.Setter; import org.hibernate.transform.AliasToBeanResultTransformer; import org.hibernate.transform.AliasedTupleSubsetResultTransformer; import org.hibernate.transform.ResultTransformer; /** * Help to transform alises with nested alises * * @author Miguel Resendiz * */ public class AliasToBeanNestedResultTransformer extends AliasedTupleSubsetResultTransformer { private static final long serialVersionUID = -8047276133980128266L; private static final int TUPE_INDEX = 0; private static final int ALISES_INDEX = 1; private static final int FIELDNAME_INDEX = 2; private static final PropertyAccessor accessor = PropertyAccessorFactory .getPropertyAccessor("property"); private final Class resultClass; private Object[] entityTuples; private String[] entityAliases; private Map> fieldToClass = new HashMap>(); private Map> subEntities = new HashMap>(); private List nestedAliases = new ArrayList(); private Map> listFields = new HashMap>(); public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) { return false; } public AliasToBeanNestedResultTransformer(Class resultClass) { this.resultClass = resultClass; } public Object transformTuple(Object[] tuple, String[] aliases) { handleSubEntities(tuple, aliases); cleanParams(tuple, aliases); ResultTransformer rootTransformer = new AliasToBeanResultTransformer( resultClass); Object root = rootTransformer.transformTuple(entityTuples, entityAliases); loadSubEntities(root); cleanMaps(); return root; } private void handleSubEntities(Object[] tuple, String[] aliases) throws HibernateException { String fieldName = ""; String aliasName = ""; try { for (int i = 0; i < aliases.length; i++) { String alias = aliases[i]; if (alias.contains(".")) { String[] sp = alias.split("\\."); StringBuilder aliasBuilder = new StringBuilder(); for (int j = 0; j < sp.length; j++) { if (j == 0) { fieldName = sp[j]; } else { aliasBuilder.append(sp[j]); aliasBuilder.append("."); } } aliasName = aliasBuilder.substring(0, aliasBuilder.length() - 1); nestedAliases.add(alias); manageEntities(fieldName, aliasName, tuple[i]); } } } catch (NoSuchFieldException e) { throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName() + " for field name: " + fieldName + " and alias name:" + aliasName); } } private Class findClass(String fieldName) throws NoSuchFieldException, SecurityException { if (fieldToClass.containsKey(fieldName)) { return fieldToClass.get(fieldName); } else { Class subclass = resultClass.getDeclaredField(fieldName) .getType(); if (subclass.equals(List.class) || subclass.equals(Set.class)) { if (subclass.equals(List.class)) { listFields.put(fieldName, LinkedList.class); } else { listFields.put(fieldName, HashSet.class); } Field field = resultClass.getDeclaredField(fieldName); ParameterizedType genericType = (ParameterizedType) field .getGenericType(); subclass = (Class) genericType.getActualTypeArguments()[0]; } fieldToClass.put(fieldName, subclass); return subclass; } } @SuppressWarnings("unchecked") private void manageEntities(String fieldName, String aliasName, Object tupleValue) throws NoSuchFieldException, SecurityException { Class subclass = findClass(fieldName); if (!subEntities.containsKey(fieldName)) { List list = new ArrayList(); list.add(new ArrayList()); list.add(new ArrayList()); list.add(FIELDNAME_INDEX, subclass); subEntities.put(fieldName, list); } ((List) subEntities.get(fieldName).get(TUPE_INDEX)) .add(tupleValue); ((List) subEntities.get(fieldName).get(ALISES_INDEX)) .add(aliasName); } private void cleanParams(Object[] tuple, String[] aliases) { entityTuples = new Object[aliases.length - nestedAliases.size()]; entityAliases = new String[aliases.length - nestedAliases.size()]; for (int j = 0, i = 0; j < aliases.length; j++) { if (!nestedAliases.contains(aliases[j])) { entityTuples[i] = tuple[j]; entityAliases[i] = aliases[j]; ++i; } } } @SuppressWarnings({ "unchecked", "rawtypes" }) private void loadSubEntities(Object root) throws HibernateException { try { for (String fieldName : subEntities.keySet()) { Class subclass = (Class) subEntities.get(fieldName).get( FIELDNAME_INDEX); ResultTransformer subclassTransformer = new AliasToBeanNestedResultTransformer( subclass); Object subObject = subclassTransformer.transformTuple( ((List) subEntities.get(fieldName).get(0)) .toArray(), ((List) subEntities.get(fieldName).get(1)) .toArray(new String[0])); Setter setter = accessor.getSetter(resultClass, fieldName); if (listFields.containsKey(fieldName)) { Class collectionClass = listFields.get(fieldName); Collection subObjectList = (Collection) collectionClass .newInstance(); subObjectList.add(subObject); setter.set(root, subObjectList, null); } else { setter.set(root, subObject, null); } } } catch (Exception e) { throw new HibernateException(e); } } private void cleanMaps() { fieldToClass = new HashMap>(); subEntities = new HashMap>(); nestedAliases = new ArrayList(); listFields = new HashMap>(); } } 

这是个好问题。 我遇到过类似的问题。 因此,AliasToBeanResultTransformer仅将主对象转换为实体,但它无法选择嵌套对象作为嵌套对象。

要获取嵌套对象,我们应该使用Custom Transformer。 这是一个例子:

https://github.com/madhupathy/Hibernate-Custom-Transformer

我避免在这种情况下进行投影并获取所有对象以保持简单,如果没有巨大的性能影响,我几乎需要所有值。