如何使用Hibernate按投影分组

3

我需要找到一个特定组内和特定地址中的学生名单以及他们所在位置的电话号码。

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

假设我有

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<Group> groups = new HashSet<Group>(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<Phone> phones = new HashSet<Phone>();


        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<AllStudents> 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<String> 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<String> nestedAliases = new ArrayList<String>();
    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<Object> list = new ArrayList<Object>();
            list.add(new ArrayList<Object>());
            list.add(new ArrayList<String>());
            list.add(FIELDNAME_INDEX, subclass);
            subEntities.put(fieldName, list);
        }
        ((List<Object>) subEntities.get(fieldName).get(TUPE_INDEX))
        .add(tupleValue);
        ((List<String>) 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<Object>) subEntities.get(fieldName).get(0))
                        .toArray(),
                        ((List<Object>) 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<String>();
        listFields = new HashMap<String, Class<?>>();
    }

}
1个回答

1

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

您可以将 AllStudent DTO 更改为:

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<String> 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<String> getPhones() {
        return phones;
    }
}

现在当您运行查询时,您会得到一个名为AllStudents的列表:
List<AllStudents> allStudents = ...

您可以像这样简单地分组:

which you simply group like this:

LinkedHashMap<Long, StudentDTO> 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<StudentDTO> studentDTOs = new ArrayList<>(studentMap.values());

谢谢,我觉得你误解了我的意思。我不想要一个带有学生电话的地图。我只想要一个学生对象的集合和每个人在他/她各自的对象中的电话列表。问题已更新。非常抱歉给您带来任何不便。 - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接