Pertanyaan Bagaimana cara mengambil satu set objek anggota menggunakan Hibernate?


Pertanyaan ini untuk menindaklanjuti dengan saya pertanyaan sebelumnya. Saya perlu mengambil daftar kelas yang rumit. Masing-masing memiliki beberapa set di dalamnya dan hanya sejumlah tertentu yang harus diambil. Saya sudah membaca jawaban dari pertanyaan-pertanyaan ini 1,2 tetapi tidak satupun dari mereka memecahkan masalah saya.

Saya perlu mencari daftar siswa yang berada dalam kelompok tertentu dan berada di lokasi tertentu, serta nomor telepon mereka di alamat mereka. Saya juga perlu menunjukkan jarak setiap siswa ke koordinat tertentu.

Kode berikut berfungsi dengan baik, satu-satunya masalah adalah saya tidak dapat mengambil daftar objek misalnya daftar email, daftar grup dan daftar telepon setiap siswa.

@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<Students> 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();

18
2018-05-01 01:34


asal


Jawaban:


Kelas default hibernate tidak mentransfer objek bertingkat, jika Anda memiliki masalah kinerja, Anda harus mencoba kode berikut.

Lihatlah ini link dan yang ini itu adalah versi perbaikan dari itu.

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<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
2018-05-31 04:07



Itu pertanyaan yang bagus. Saya menghadapi masalah serupa. Jadi AliasToBeanResultTransformer hanya mengubah objek utama sebagai entitas tetapi tidak memiliki kemampuan untuk memilih objek bersarang sebagai objek bersarang.

Untuk mendapatkan objek bertingkat, kita harus menggunakan Custom Transformer. Berikut ini contohnya:

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

Saya menghindari proyeksi dalam kasus seperti itu dan mengambil semua objek untuk membuatnya tetap sederhana, jika tidak ada dampak kinerja yang besar dan saya memerlukan hampir semua nilai.


10
2018-05-04 15:28