如何使用Hibernate进行投影分组

我需要查找特定群组中的学生列表,并列出特定地址以及他们所在位置的电话号码。

我的主要问题是我无法检索每个学生的电话号码作为集合。 例如,如果我有student1,student2。 电话1111用于位置1中的student1和电话2222,电话3333用于位置1中的student2,电话444用于位置2中的student2。

可以说我有

Student1 Alex group1 1111 Location1 Street1 Student3 Jack group1 93939 Location2 Street4 Student7 Joe group2 22223 Location4 Street8 Student2 John group1 2222 3333 Location1 Street1 Student2 John group1 4444 Location1 Street2 Student12 Mark group1 4423 Location9 Street9 

带数据的示例输出

  User asks for all students in group1 and location1 Student1 Alex Street1 phone 1111 distance30 Student2 John Street1 phones 22222,3333 distance30 Student2 John Street2 phone 4444 distance40 

换句话说,我想有一个学生名单,以及他们在所选地点的位置和电话。

Hibernate针对我当前的代码返回以下错误消息

 org.springframework.orm.hibernate4.HibernateSystemException: IllegalArgumentException occurred while calling setter for property [com.example.Address.phones (expected type = java.util.List)]; target = [com.example.results.AllStudents@6deeac0], property value = [11111111] setter of com.example.results.AllStudents.phones; nested exception is IllegalArgumentException occurred while calling setter for property [com.example.results.AllStudents.phones (expected type = java.util.List)]; target = [com.example.results.AllStudents@6deeac0], property value = [11111111] 

学生

 @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<AllStudents) 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 AliasToBeanNestedResultTransformer(AllStudents.class)) .list(); 

结果课

 public class AllStudents { List phones; ... } 

AliasToBeanNestedResultTransformer

 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<String, Class> fieldToClass = new HashMap<String, Class>(); private Map<String, List> subEntities = new HashMap<String, List>(); private List nestedAliases = new ArrayList(); private Map<String, Class> listFields = new HashMap<String, Class>(); 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<String, Class>(); subEntities = new HashMap<String, List>(); nestedAliases = new ArrayList(); listFields = new HashMap<String, Class>(); } } 

AliasToBeanNestedResultTransformer支持嵌套的DTO,但它不支持DTO集合。

您可以将AllStudent DTS更改为:

 public class AllStudents { Student student; String phone; Location location; public AllStudents(Student student, String phone, Location location) { this.student = student; this.phone = phone; this.location = location; } public Student getStudent() { return student; } public String getPhone() { return phone; } public Location getLocation() { return location; } } 

并且您需要添加StudentDTO来保存聚合结果:

 public class StudentDTO { private final Student student; private String location; private List phones = new ArrayList<>(); public StudentDTO(Student student) { this.student = student; } public Student getStudent() { return student; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public List getPhones() { return phones; } } 

现在,当您运行查询时,您将获得AllStudents列表:

 List allStudents = ... 

您只需像这样分组:

 LinkedHashMap studentMap = new LinkedHashMap<>(); for(AllStudents all : allStudents) { StudentDTO studentDTO = studentMap.get(all.getStudent().getId()); if(studentDTO == null) { studentDTO = new StudentDTO(all.getStudent()); studentMap.put(all.getStudent().getId(), studentDTO); } if(all.getPhone() != null) { studentDTO.getPhones().add(all.getPhone()); } studentDTO.setLocation(all.getLocation()); } List studentDTOs = new ArrayList<>(studentMap.values());