Populate child bean with Transformers.aliasToBean in Hibernate

后端 未结 5 1121
太阳男子
太阳男子 2020-12-10 16:53

I have the next couple of beans:

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

MyEntity {
    Address address;
    St         


        
相关标签:
5条回答
  • 2020-12-10 17:29

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

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

    }

    0 讨论(0)
  • 2020-12-10 17:30

    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

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

    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"))

    Then, just add a setter on your "MyEntity" root object: "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.

    0 讨论(0)
  • 2020-12-10 17:41

    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").

    Hope this helps.

    0 讨论(0)
  • 2020-12-10 17:47

    Code provided in Github works fine but there is change in import for new versions of hibernate. Its as follow.

    org.hibernate.property.PropertyAccessor replaced byorg.hibernate.property.access.spi.PropertyAccess

    and

    org.hibernate.property.PropertyAccessorFactory replaced by 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);
    
    0 讨论(0)
  • 2020-12-10 17:51

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

    0 讨论(0)
提交回复
热议问题