Java 在 Hibernate 中使用 Transformers.aliasToBean 填充子 bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18713235/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 10:24:12  来源:igfitidea点击:

Populate child bean with Transformers.aliasToBean in Hibernate

javahibernate

提问by maqjav

I have the next couple of beans:

我有接下来的几个豆子:

Address {
    String name;
    String number;
    String zipcode;
    String town;
}

MyEntity {
    Address address;
    String value1;
    String value2;
}

I'm trying to do the next Hibernate query:

我正在尝试执行下一个 Hibernate 查询:

private final List<String> propertiesDistinct = Arrays.asList("address.name");
private final List<String> properties = Arrays.asList("address.number",
        "address.zipcode", "address.town")

ProjectionList projectionList = Projections.projectionList();

if (propertiesDistinct != null) {
    ProjectionList projectionListDistinct = Projections.projectionList();
for (String propertyDistinct : propertiesDistinct)
         projectionListDistinct.add(Projections.property(propertyDistinct).as(propertyDistinct));

    projectionList.add(Projections.distinct(projectionListAgrupar));
}

if (properties != null)
    for (String property : properties)
         projectionList.add(Projections.property(property).as(property));
criterio.setProjection(projectionList);

// MORE FILTERS ON MyEntity FIELDS
//... criterio.add(Restrinctions...);

// I want to recover the results on my bean MyEntity so I don't have to create a new one
criterio.setResultTransformer(Transformers.aliasToBean(MyEntity.class));

Problem:

问题:

Caused by: org.hibernate.PropertyNotFoundException: Could not find setter for address.name on class com.entities.MyEntity

I understand that Hibernate is looking for something like:

我知道 Hibernate 正在寻找类似的东西:

public String getAddressName() {} // This should be in MyEntity

Instead of:

代替:

public String getName() {} // In my Address bean

Ideas about how can I fix this without creating a new bean?

关于如何在不创建新 bean 的情况下解决此问题的想法?

Thanks!

谢谢!

采纳答案by Sami Andoni

I wrote a ResultTransformer that can fix your problem. It's name is AliasToBeanNestedResultTransformer, check it out on github.

我写了一个 ResultTransformer 可以解决您的问题。它的名字是 AliasToBeanNestedResultTransformer,在github上查看。

回答by Ean V

Try creating an alias like criterio.createAlias("address", "add");and then edit your properties to be like Arrays.asList("add.number","add.zipcode", "add.town").

尝试创建别名 likecriterio.createAlias("address", "add");然后将您的属性编辑为 like Arrays.asList("add.number","add.zipcode", "add.town")

Hope this helps.

希望这可以帮助。

回答by Vicky Thakor

Code provided in Githubworks fine but there is change in importfor new versions of hibernate. Its as follow.

Github 中提供的代码工作正常,但import新版本的 hibernate发生了变化。它如下。

org.hibernate.property.PropertyAccessorreplaced byorg.hibernate.property.access.spi.PropertyAccess

org.hibernate.property.PropertyAccessor取而代之org.hibernate.property.access.spi.PropertyAccess

and

org.hibernate.property.PropertyAccessorFactoryreplaced by org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl

org.hibernate.property.PropertyAccessorFactory取而代之 org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl

So you'll have change the code from

所以你必须改变代码

PropertyAccessor accessor = PropertyAccessorFactory.getPropertyAccessor("property");
accessor.getSetter(resultClass, (String)subclassToAlias.get(subclass).get(2)).set(root, subObject, null);

to

PropertyAccess propertyAccess = PropertyAccessStrategyBasicImpl.INSTANCE.buildPropertyAccess(resultClass, (String)subclassToAlias.get(subclass).get(2));
propertyAccess.getSetter().set(root, subObject, null);

回答by Ronny Shibley

AliasToBeanNestedResultTransformer does not handle Nested Multi Level DTOs, so I rewrote one that handles n-level dtos.

AliasToBeanNestedResultTransformer 不处理嵌套多级 DTO,所以我重写了一个处理 n 级 dto。

Hope this 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.

我的解决方案非常基础。它不像正确的结果转换器那么干净,但是当您只需要对几个属性进行快速投影时它很有用。

If you get Could not find setter for address.name on class com.entities.MyEntity

如果你得到 Could not find setter for address.name on class com.entities.MyEntity

It doesn't mean Hibernate is looking for public String getAddressName() {}. Instead it looks for a setter with the impossible "setAddress.name" name.

这并不意味着 Hibernate 正在寻找public String getAddressName() {}. 相反,它寻找具有不可能的“setAddress.name”名称的 setter。

Instead of .add(Projections.property("address.name"),"address.name"))type a proper setter name as second argument to the .add() method as follows .add(Projections.property("address.name"),"addressName"))

而不是.add(Projections.property("address.name"),"address.name"))键入正确的 setter 名称作为 .add() 方法的第二个参数,如下所示.add(Projections.property("address.name"),"addressName"))

Then, just add a setter on your "MyEntity" root object: "setAddressName".

然后,只需在“MyEntity”根对象上添加一个 setter:“setAddressName”。

public void setAddressName(String addressName) {
  this.address= (this.address==null) ? new Address() : address;
  this.address.setName(addressName);
}

The drawback is that it "dirties" your object with extra methods.

缺点是它用额外的方法“弄脏”了你的对象。

Also posted here.

也贴在这里