Java 如何编写 Hibernate Criteria 以通过投影列表获取嵌套对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28970214/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to write Hibernate Criteria to take nested objects by Projection List?
提问by Aravinthan K
I want to take Nested object values in Hibernate Projection List. I having Pojo 'Charge' and 'Tariff' class with OneToMany and ManyToOne relations.
我想在 Hibernate Projection List 中获取嵌套对象值。我有 Pojo 'Charge' 和 'Tariff' 类,具有 OneToMany 和 ManyToOne 关系。
My sample code is as following:
我的示例代码如下:
Charge
收费
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;
}
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;
}
I want to take amount value from tariff by charge model.
我想按收费模式从关税中获取金额值。
I write sql criteria that works ie.
我编写了有效的 sql 标准,即。
SELECT tariff.amount,charge.name FROM charge,tariff WHERE charge.name LIKE 's%';
SELECT tax.amount,charge.name FROM charge,tariff WHERE charge.name LIKE 's%';
and i tried with following criteria.
我尝试了以下标准。
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();
I just check with restclient it returns null Value. How to write for Criteria for this sql Query ?
我只是检查restclient它返回空值。如何为这个 sql 查询编写 Criteria ?
采纳答案by The Coder
I've experienced this kind of requirement. I tried to get nested objects as nested objects using Transformers.aliasToBean
, which will not work. By default, Transformers.aliasToBean
don't have the capability to select nested object as nested object.
我经历过这种要求。我尝试使用 将嵌套对象作为嵌套对象获取Transformers.aliasToBean
,但这是行不通的。默认情况下,Transformers.aliasToBean
没有选择嵌套对象作为嵌套对象的能力。
You can take a look at my question
你可以看看我的问题
Using Projecions to fetch a particular column from child table
To get Nested object as nested object, you need a Custom Transformer which is capable of doing that.
要将嵌套对象作为嵌套对象,您需要一个能够做到这一点的自定义转换器。
Here's a Custom Transformer written by samiandoni
这是 samiandoni 编写的自定义变压器
https://github.com/samiandoni/AliasToBeanNestedResultTransformer
https://github.com/samiandoni/AliasToBeanNestedResultTransformer
From the provided Readme in that link
来自该链接中提供的自述文件
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
The above transformer is capable of Fetching first level Nested object as Nested object and it doesn't support further deep nested objects. So after some digging I've found another Custom transformer which is capable of Fetching deep Nested objects as Nested objects
上述转换器能够将第一级嵌套对象作为嵌套对象获取,并且不支持更深的嵌套对象。因此,经过一番挖掘,我发现了另一个自定义转换器,它能够将深层嵌套对象作为嵌套对象获取
Note:
笔记:
Author:Miguel Resendiz
作者: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<?>>();
}
}
Just replace samiandoni's Transformer with the above transformer. It's capable of fetching further deep Nested Objects as respective Objects.
只需将 samiandoni's Transformer 替换为上述变压器即可。它能够获取更深的嵌套对象作为相应的对象。
回答by Ronny Shibley
The AliasToBeanNestedResultTransformer does not handle Multilevel Nested DTO's. Meaning, you won't be able to do company.employee.location each in its own DTO.
AliasToBeanNestedResultTransformer 不处理多级嵌套 DTO。这意味着,您将无法在自己的 DTO 中分别执行 company.employee.location。
Here is a Transformer I wrote that handles Multilevel Nested DTOs. You may used it by calling:
这是我编写的处理多级嵌套 DTO 的 Transformer。您可以通过调用来使用它:
criteria.setResultTransformer( AliasToBeanNestedMultiLevelResultTransformer(mappingBean));
标准.setResultTransformer(AliasToBeanNestedMultiLevelResultTransformer(mappingBean));
Hope it helps.
希望能帮助到你。
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));
}
}
}
}
}
回答by otonglet
My solution is very basic. It's not as clean as a proper result transformer but it's useful when you just need to do a quick projection for a few properties.
我的解决方案非常基础。它不像正确的结果转换器那么干净,但是当您只需要对几个属性进行快速投影时它很有用。
Instead of .add(Projections.property("tariff.amount"),"amount"))
type .add(Projections.property("tariff.amount"),"tariffAmount"))
而不是.add(Projections.property("tariff.amount"),"amount"))
类型.add(Projections.property("tariff.amount"),"tariffAmount"))
Then, just add a setter on your root object "setTariffAmount".
然后,只需在您的根对象“setTariffAmount”上添加一个 setter。
public void setTariffAmount(String tariffAmount) {
this.tariff = (this.tariff==null) ? new Tariff() : tariff;
tariff.setAmount(tariffAmount);
}
The drawback is that it "dirties" your object with extra methods.
缺点是它用额外的方法“弄脏”了你的对象。