0

我在使用AliasToBeanResultTransformer的Hibernate HQL 投影时遇到问题,基本上我试图返回的结果没有正确映射到 bean,情况如下:

我正在使用的 HQL 查询是这样的:

select entity.categoryTypes as categoryTypes from nz.co.doltech.ims.server.entities.IncidentEntity entity where (entity.id = :id105019)

我想从基于它的加入关系中获取CategoryType's 。IncidentEntity当我不尝试在其上使用任何变压器时,这可以正常工作。categoryTypes是一个 Set 并且转换器不断尝试检查方法的参数类型并失败,因为它没有找到 aCategoryTypeEntity而是找到 a java.util.Set,就好像它试图将单个映射CategoryTypeEntitycategoryTypes字段中一样。我本以为因为它是一个 Set ,所以它会将数据作为 a 拉出Set,然后尝试将其映射到该categoryTypes字段。显然不是。

@javax.persistence.Entity(name = "incidents")
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class IncidentEntity implements Entity {

    ...

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "incident_categorytype", joinColumns = { 
            @JoinColumn(name = "incident_id", nullable = false, updatable = false) }, 
        inverseJoinColumns = {
            @JoinColumn(name = "categorytype_id", nullable = false, updatable = false) 
    })
    private Set<CategoryTypeEntity> categoryTypes = new HashSet<CategoryTypeEntity>();

    ...

    public Set<CategoryTypeEntity> getCategoryTypes() {
        return categoryTypes;
    }
    public void setCategoryTypes(Set<CategoryTypeEntity> categoryTypes) {
        this.categoryTypes = categoryTypes;
    }
}

这是我打的电话:

Query query = session.createQuery("select entity.categoryTypes as categoryTypes from nz.co.doltech.ims.server.entities.IncidentEntity entity " + 
                                  "where (entity.id = :id105019)")

query.setResultTransformer(Transformers.aliasToBean(IncidentEntity.class));

return query.list();

我得到的例外是:

Caused by: org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of nz.co.doltech.ims.server.entities.IncidentEntity.categoryTypes
...
Caused by: java.lang.IllegalArgumentException: argument type mismatch

休眠日志消息是:

Jun 27, 2014 12:32:11 AM org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: IllegalArgumentException in class: nz.co.doltech.ims.server.entities.IncidentEntity, setter method of property: categoryTypes
Jun 27, 2014 12:32:11 AM org.hibernate.property.BasicPropertyAccessor$BasicSetter set
SEVERE: expected type: java.util.Set, actual value: nz.co.doltech.ims.server.entities.CategoryTypeEntity


使用休眠 3.6.10

谁能看到这里发生了什么?这看起来真的不像正常行为,也许我做错了什么。将不胜感激我能得到的任何帮助!

更新:这很奇怪,与问题没有直接关系。当我将休眠 use_query_cache 属性设置为 true 时,我在 AliasToBeanResultTransformer 中不断将投影结果设为 null (然后结果返回为 null (或 [null, null, null] 取决于返回的数量。我认为这可能是一个错误? 关于手头的问题,当我删除结果转换器时,它CategoryTypeEntites按预期返回 3。当它添加时,我得到一个 CategoryTypeEntity,它正在 Transformers 的 transformTuple 方法中处理。对这两个问题真的很困惑。

干杯,本

4

1 回答 1

0

通过重写 AliasToBeanResultTransformer 类设法解决此问题。如果集合类型匹配且集合泛型类型匹配,它将不会插入到集合中。我还发现了一个很棒的由samiandoni制作的嵌套 bean 转换器,它也可以让我映射嵌套的投影值 :) 以下是我为遇到同样问题的其他人实现它的方法:

@SuppressWarnings({"serial","rawtypes"})
public class AliasToBeanResultTransformer implements ResultTransformer, Serializable {

    // IMPL NOTE : due to the delayed population of setters (setters cached
    //      for performance), we really cannot properly define equality for
    //      this transformer

    private final Class resultClass;
    private boolean isInitialized;
    private String[] aliases;

    private Setter[] setters;
    private Getter[] getters;

    public AliasToBeanResultTransformer(Class resultClass) {
        if ( resultClass == null ) {
            throw new IllegalArgumentException( "resultClass cannot be null" );
        }
        isInitialized = false;
        this.resultClass = resultClass;
    }

    @Override
    public Object transformTuple(Object[] tuple, String[] aliases) {
        Object result;

        try {
            if ( ! isInitialized ) {
                initialize( aliases );
            }
            else {
                check( aliases );
            }

            result = resultClass.newInstance();

            for ( int i = 0; i < aliases.length; i++ ) {
                Setter setter = setters[i];
                if ( setter != null ) {
                    Class paramType = setter.getMethod().getParameterTypes()[0];

                    if(paramType != null) {
                        Object obj = tuple[i];
                        // Check if parameter is a collection
                        if(!obj.getClass().equals(paramType) && isCollection(paramType)) {
                            insertToList(result, obj, getters[i], aliases[i]);
                        }
                        else {
                            setter.set( result, obj, null );
                        }
                    }
                }
            }
        }
        catch ( InstantiationException e ) {
            throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
        }
        catch ( IllegalAccessException e ) {
            throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
        }

        return result;
    }

    @Override
    public List transformList(List collection) {
        return collection;
    }

    @SuppressWarnings("unchecked")
    private boolean insertToList(Object result, Object obj, Getter getter, String alias) {
        Class genClass;
        try {
            genClass = ReflectUtils.getGenericType(resultClass.getDeclaredField(alias));

            // Check if the collection can take the obj
            if(genClass.equals(obj.getClass())) {
                Collection collection = (Collection) getter.get(result);
                collection.add(obj);
                return true;
            }
        } catch (NoSuchFieldException | SecurityException e) {}

        return false;
    }

    private void initialize(String[] aliases) {
        PropertyAccessor propertyAccessor = new ChainedPropertyAccessor(
                new PropertyAccessor[] {
                        PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
                        PropertyAccessorFactory.getPropertyAccessor( "field" )
                }
        );
        this.aliases = new String[ aliases.length ];
        setters = new Setter[ aliases.length ];
        getters = new Getter[ aliases.length ];
        for ( int i = 0; i < aliases.length; i++ ) {
            String alias = aliases[ i ];
            if ( alias != null ) {
                this.aliases[ i ] = alias;
                setters[ i ] = propertyAccessor.getSetter( resultClass, alias );
                getters[ i ] = propertyAccessor.getGetter( resultClass, alias );
            }
        }
        isInitialized = true;
    }

    private void check(String[] aliases) {
        if ( ! Arrays.equals( aliases, this.aliases ) ) {
            throw new IllegalStateException(
                    "aliases are different from what is cached; aliases=" + Arrays.asList( aliases ) +
                            " cached=" + Arrays.asList( this.aliases ) );
        }
    }

    private boolean isCollection(Class clazz) {
        return Collection.class.isAssignableFrom(clazz);
    }

    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }
        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }

        AliasToBeanResultTransformer that = ( AliasToBeanResultTransformer ) o;

        if ( ! resultClass.equals( that.resultClass ) ) {
            return false;
        }
        if ( ! Arrays.equals( aliases, that.aliases ) ) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        int result = resultClass.hashCode();
        result = 31 * result + ( aliases != null ? Arrays.hashCode( aliases ) : 0 );
        return result;
    }
}

你也需要实现这个 RefectUtil 方法:

public static Class<?> getGenericType(Field field) {
    ParameterizedType type = (ParameterizedType) field.getGenericType();
    return (Class<?>) type.getActualTypeArguments()[0];
}

然后你也可以让它与 samiandoni 的转换器一起工作(只要确保它使用你编辑的 AliasToBeanResultTransformer 类):

/**
 * @author samiandoni
 *
 */
@SuppressWarnings("rawtypes")
public class AliasToBeanNestedResultTransformer implements ResultTransformer, Serializable {

    private static final long serialVersionUID = -8047276133980128266L;

    private final Class<?> resultClass;

    public AliasToBeanNestedResultTransformer(Class<?> resultClass) {
        this.resultClass = resultClass;
    }

    @SuppressWarnings("unchecked")
    public Object transformTuple(Object[] tuple, String[] aliases) {

        Map<Class<?>, List<?>> subclassToAlias = new HashMap<Class<?>, List<?>>();
        List<String> nestedAliases = new ArrayList<String>();

        try {
            for (int i = 0; i < aliases.length; i++) {
                String alias = aliases[i];
                if (alias.contains(".")) {
                    nestedAliases.add(alias);

                    String[] sp = alias.split("\\.");
                    String fieldName = sp[0];
                    String aliasName = sp[1];

                    Class<?> subclass = resultClass.getDeclaredField(fieldName).getType();

                    if (!subclassToAlias.containsKey(subclass)) {
                        List<Object> list = new ArrayList<Object>();
                        list.add(new ArrayList<Object>());
                        list.add(new ArrayList<String>());
                        list.add(fieldName);
                        subclassToAlias.put(subclass, list);
                    }
                    ((List<Object>)subclassToAlias.get(subclass).get(0)).add(tuple[i]);
                    ((List<String>)subclassToAlias.get(subclass).get(1)).add(aliasName);
                }
            }
        }
        catch (NoSuchFieldException e) {
            throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
        }

        Object[] newTuple = new Object[aliases.length - nestedAliases.size()];
        String[] newAliases = new String[aliases.length - nestedAliases.size()];
        int i = 0;
        for (int j = 0; j < aliases.length; j++) {
            if (!nestedAliases.contains(aliases[j])) {
                newTuple[i] = tuple[j];
                newAliases[i] = aliases[j];
                ++i;
            }
        }

        ResultTransformer rootTransformer = new AliasToBeanResultTransformer(resultClass);
        Object root = rootTransformer.transformTuple(newTuple, newAliases);

        for (Class<?> subclass : subclassToAlias.keySet()) {
            ResultTransformer subclassTransformer = new AliasToBeanResultTransformer(subclass);
            Object subObject = subclassTransformer.transformTuple(
                ((List<Object>)subclassToAlias.get(subclass).get(0)).toArray(), 
                ((List<Object>)subclassToAlias.get(subclass).get(1)).toArray(new String[0])
            );

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

        return root;
    }

    @Override
    public List transformList(List collection) {
        return collection;
    }
}
于 2014-06-28T07:59:36.510 回答