一尘不染

如何编写Hibernate Criteria以通过投影列表获取嵌套对象?

hibernate

我想在Hibernate Projection List中使用嵌套对象值。我有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标准,即工作。

选择charge.amount,charge.name从charge,tariff那里的charge.name喜欢’s%’;

我尝试了以下标准。

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检查它返回空值。如何编写此sql查询的条件?


阅读 320

收藏
2020-06-20

共1个答案

一尘不染

我已经经历过这种要求。我尝试使用来将嵌套对象作为嵌套对象获取Transformers.aliasToBean,这将无法正常工作。默认情况下,Transformers.aliasToBean没有能力选择嵌套对象作为嵌套对象。

要将嵌套对象作为嵌套对象,您需要一个能够执行此操作的自定义变压器。

这是samiandoni撰写的Custom Transformer

https://github.com/samiandoni/AliasToBeanNestedResultTransformer

从该链接中提供的自述文件中

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的Transformer替换为上述变压器即可。它能够获取更深的嵌套对象作为各自的对象。

2020-06-20