복붙노트

[SPRING] Projection List에 의해 중첩 된 객체를 가져 오기위한 Hibernate Criteria를 작성하는 방법?

SPRING

Projection List에 의해 중첩 된 객체를 가져 오기위한 Hibernate Criteria를 작성하는 방법?

최대 절전 모드 프로젝션 목록에서 중첩 된 개체 값을 가져 오려고합니다. 나는 OneToMany와 ManyToOne 관계로 Pojo 'Charge'와 'Tariff'클래스를 가지고 있습니다.

내 샘플 코드는 다음과 같습니다.

요금

private String id;              
private Tariff tariff;
private String name;

@OneToMany(cascade=   {CascadeType.ALL},fetch=FetchType.EAGER,mappedBy="charge")
public Tariff getTariff() {
    return tariff;
}
public void setTariff(Tariff tariff) {
    this.tariff = tariff;
}

관세

private String id;
private String amount;
private Charge charge;

@ManyToOne(cascade={CascadeType.PERSIST},fetch=FetchType.EAGER)
@JoinColumn(name="charge_id")
public Charge getCharge() {
    return charge;
}
public void setCharge(Charge charge) {
    this.charge = charge;
}

요금 모델로 요금에서 금액 가치를 가져 가고 싶습니다.

내가 작동하는 SQL 표준을 작성합니다.

나는 다음과 같은 기준으로 시도했다.

Criteria cr = getSession().createCriteria(Charge.class,"charge")
    .createAlias("charge.tariff","tariff")
    .setProjection(Projections.projectionList()
    .add(Projections.property("chargeName"),"chargeName")
    .add(Projections.property("id"),"id")
    .add(Projections.property("tariff.amount"),"amount"))
    .add(Restrictions.like("chargeName", name+"%"))
    .setResultTransformer(Transformers.aliasToBean(Charge.class));
     return  cr.list(); 

난 그냥 restclient에 null 값을 반환하는지 확인합니다. 이 SQL 쿼리에 대한 조건 작성 방법

해결법

  1. ==============================

    1.나는 이런 종류의 요구 사항을 경험했다. 나는 중첩 된 객체를 Transformers.aliasToBean을 사용하여 중첩 된 객체로 만들려고했으나 작동하지 않습니다. 기본적으로 Transformers.aliasToBean에는 중첩 된 객체를 중첩 된 객체로 선택할 수있는 기능이 없습니다.

    나는 이런 종류의 요구 사항을 경험했다. 나는 중첩 된 객체를 Transformers.aliasToBean을 사용하여 중첩 된 객체로 만들려고했으나 작동하지 않습니다. 기본적으로 Transformers.aliasToBean에는 중첩 된 객체를 중첩 된 객체로 선택할 수있는 기능이 없습니다.

    내 질문 좀 봐.

    프로젝션을 사용하여 자식 테이블에서 특정 열을 가져옵니다.

    중첩 된 객체를 중첩 된 객체로 가져 오려면 해당 객체를 수행 할 수있는 Custom Transformer가 필요합니다.

    samiandoni가 작성한 사용자 정의 변압기는 다음과 같습니다.

    https://github.com/samiandoni/AliasToBeanNestedResultTransformer

    해당 링크의 제공된 Readme에서

    class Person {
      private Long id;
      private String name;
      private Car car;
      // getters and setters
    }
    
    class Car {
      private Long id;
      private String color;
      // getters and setters
    }
    
    List<Person> getPeople() {
      ProjectionList projections = Projections.projectionList()
        .add(Projections.id().as("id"))
        .add(Projections.property("name").as("name"))
        .add(Projections.property("c.id").as("car.id"))
        .add(Projections.property("c.color").as("car.color"));
    
      Criteria criteria = getCurrentSession().createCriteria(Person.class)
        .createAlias("car", "c")
        .setProjection(projections)
        .setResultTransformer(new AliasToBeanNestedResultTransformer(Person.class));
    
      return (List<Person>) criteria.list();
    }
    
    // each car of Person will be populated
    

    위의 변환기는 첫 번째 레벨 중첩 된 객체를 중첩 된 객체로 가져올 수 있으며 더 깊이 중첩 된 객체를 지원하지 않습니다. 그래서 일부 중첩 된 개체를 중첩 된 개체를 중첩 된 개체로 가져올 수있는 다른 사용자 지정 변환기를 발견했습니다.

    노트 :

    저자 : Miguel Resendiz

    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<?>>();
        }
    
    }
    

    samiandoni의 변압기를 위의 변압기로 교체하면됩니다. 더 깊은 중첩 된 객체를 각각의 객체로 가져올 수 있습니다.

  2. ==============================

    2.내 솔루션은 매우 기본입니다. 적절한 결과 변환기만큼 깨끗하지는 않지만 몇 가지 속성에 대한 빠른 투영을해야 할 때 유용합니다.

    내 솔루션은 매우 기본입니다. 적절한 결과 변환기만큼 깨끗하지는 않지만 몇 가지 속성에 대한 빠른 투영을해야 할 때 유용합니다.

    .add (Projections.property ( "tariff.amount"), "amount")) 대신 .add (Projections.property ( "tariff.amount"), "tariffAmount"))를 입력하십시오.

    그런 다음 루트 객체 "setTariffAmount"에 setter를 추가하십시오.

    public void setTariffAmount(String tariffAmount) {
      this.tariff = (this.tariff==null) ? new Tariff() : tariff;
      tariff.setAmount(tariffAmount);
    }
    

    단점은 추가 메서드로 개체를 "더러워"한다는 것입니다.

  3. ==============================

    3.AliasToBeanNestedResultTransformer는 다중 레벨 중첩 DTO를 처리하지 않습니다. 즉, 자신의 DTO에서 company.employee.location을 각각 수행 할 수 없습니다.

    AliasToBeanNestedResultTransformer는 다중 레벨 중첩 DTO를 처리하지 않습니다. 즉, 자신의 DTO에서 company.employee.location을 각각 수행 할 수 없습니다.

    다음은 다중 레벨 중첩 DTO를 처리하는 쓴 Transformer입니다. 다음과 같이 호출하여 사용할 수 있습니다.

    희망이 도움이됩니다.

    public class AliasToBeanNestedMultiLevelResultTransformer extends AliasedTupleSubsetResultTransformer {
    
    private static final long serialVersionUID = -8047276133980128266L;
    
    public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
        return false;
    }
    
    private boolean initialized;
    private Class<?> resultClass;
    private Map<String,Class<?>> clazzMap = new HashMap<>();
    private Map<String,Setter> settersMap = new HashMap<>();
    
    public AliasToBeanNestedMultiLevelResultTransformer(Class<?> resultClass) {
        this.resultClass = resultClass;
    }
    
    public Object transformTuple(Object[] tuples, String[] aliases) {
    
        Map<String,Object> nestedObjectsMap = new HashMap<>();
    
        Object result;
        try {
            result = resultClass.newInstance();
    
            if (!initialized){
                initialized = true;
                initialize(aliases);
            }
    
            for (int a=0;a<aliases.length;a++){
    
                String alias = aliases[a];
                Object tuple = tuples[a];
    
                Object baseObject = result;
    
                int index = alias.lastIndexOf(".");
                if(index>0){
                    String basePath = alias.substring(0, index);
                    baseObject = nestedObjectsMap.get(basePath);
                    if (baseObject == null){
                        baseObject = clazzMap.get(basePath).newInstance();
                        nestedObjectsMap.put(basePath, baseObject);
                    }
                }
    
                settersMap.get(alias).set(baseObject, tuple,null);
    
            }
    
            for (Entry<String,Object> entry:nestedObjectsMap.entrySet()){
                Setter setter = settersMap.get(entry.getKey());
                if (entry.getKey().contains(".")){
    
                    int index = entry.getKey().lastIndexOf(".");
                    String basePath = entry.getKey().substring(0, index);
                    Object obj = nestedObjectsMap.get(basePath);
    
                    setter.set(obj, entry.getValue(), null);
                }
                else{
                    setter.set(result, entry.getValue(), null);
                }
            }
    
        }catch ( InstantiationException | IllegalAccessException e) {
            throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
        }
    
        return result;
    }
    
    
    private void initialize(String[] aliases) {
    
        PropertyAccessor propertyAccessor = new ChainedPropertyAccessor(
                new PropertyAccessor[] {
                        PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
                        PropertyAccessorFactory.getPropertyAccessor( "field" )
                }
        );
    
        for (int a=0;a<aliases.length;a++){
    
            String alias = aliases[a];
    
            Class<?> baseClass = resultClass;
    
            if (alias.contains(".")){
    
                String[] split = alias.split("\\.");
    
                StringBuffer res = new StringBuffer();
    
                for (int i=0;i<split.length;i++){
    
                    if (res.length()>0) res.append(".");
    
                    String item = split[i];
                    res.append(item);
    
                    String resString = res.toString();
    
                    if (i==split.length-1){
                        clazzMap.put(resString,baseClass);
                        settersMap.put(resString, propertyAccessor.getSetter(baseClass, item));
                        break;
                    }
    
                    Class<?> clazz = clazzMap.get(resString);
                    if (clazz==null){
                        clazz = propertyAccessor.getGetter(baseClass,item).getReturnType();
                        settersMap.put(resString, propertyAccessor.getSetter(baseClass, item));
                        clazzMap.put(resString,clazz);
                    }
                    baseClass = clazz;
                }
            }
            else{
                clazzMap.put(alias, resultClass);
                settersMap.put(alias, propertyAccessor.getSetter(resultClass, alias));
            }
    
        }
    
    }
    

    }

  4. from https://stackoverflow.com/questions/28970214/how-to-write-hibernate-criteria-to-take-nested-objects-by-projection-list by cc-by-sa and MIT license