使用查询时,Hibernate生成的列别名会破坏AliasToBeanResultTransformer

时间:2012-04-04 09:38:16

标签: java hibernate alias nativequery

我想要实现的是在以下列方式定义的查询上设置结果转换器:

String hqlQueryString = "select o.id as id, o.name as objectName from MyObject"; 
Class resultClass = MyObject.class;
Query query = session.createQuery(hqlQueryString).setResultTransformer(
        new new AliasToBeanResultTransformer(resultClass));
List result = query.list();

MyObject看起来像这样:

public class MyObject {
    private int id;
    private String objectName;

    public int getId() {
        return id;
    }
    public void setId(int value) {
        this.id = value;
    }

    public String getObjectName() {
        return objectName;
    }
    public void setobjectName(String value) {
        this.objectName = value;
    }
}

问题是,虽然我已将idobjectName指定为我的别名,但正在执行的实际查询使用不同的别名。这导致我的AliasToBeanResultTransformer无法构造MyObject,因为别名与属性名称不匹配。

是否有可能以编程方式获取hibernate生成的查询的别名(我可以将它们设置为bean结果转换器的别名)?我尝试使用query.getReturnAliases(),但它返回我在HQL中定义的别名,而不是Hibernate实际使用的别名。

我可以在createQuery语句中明确指定别名吗?目前我正在尝试不使用这个标准来工作,所以我很欣赏使用查询对象的方法,如果存在的话。


更新

虽然上述问题对于标准HQL查询无效(请参阅注释),但在执行本机查询时它是有效的。具体而言 - 本机查询似乎将所有别名视为低位字符串(尽管可能在查询中引入了特定的大小写)。在大写问题很重要的情况下,这会导致AliasToBeanResultTransformer在设置属性时失败。

2 个答案:

答案 0 :(得分:7)

实际上不需要实现另一个AliasToBeanResultTransformer,您可以使用addScalar(String columnAlias, Type type)显式别名本机SQL的列:

String nativeSQL = "select o.id as id, o.name as objectName from MyObject"; 
List<MyObject> resultList = session.createSQLQuery(nativeSQL)
        .addScalar("id" ,StandardBasicTypes.INTEGER)
        .addScalar("objectName",StandardBasicTypes.STRING)
        .setResultTransformer(new AliasToBeanResultTransformer(MyObject.class))
        .list();

变换器将查找MyObject类并期望它具有setter setId()setObjectName(),以便将返回的值填充到MyObject实例

答案 1 :(得分:1)

对于本机查询,没有涉及简单的解决方案。我不得不研究AliasToBeanResultTransformer类的实现并在那里修复。我通过创建AliasToBeanResultTransformer类的副本并按以下方式修改该类的私有initialize方法来解决该问题:

public class CaseInsensitiveAliasToBeanResultTransformer {
    private void initialize(String[] aliases) {
        this.aliases = new String[ aliases.length ];
        setters = new Setter[aliases.length];
        for ( int i = 0; i < aliases.length; i++ ) {
            String alias = aliases[i];
            if (alias != null) {
                this.aliases[i] = alias;
                setters[i] = CaseInsensitiveSetter.getSetter(resultClass, alias);
            }
        }
        isInitialized = true;
    }
}

此代码主要区别于CaseInsensitiveSetter.getSetter(resultClass, alias)行,我在其中介绍了我将在下面介绍的CaseInsensitiveSetter类。这个类实现了Setter接口,允许使用不区分大小写的匹配来检索类的setter方法 - 这样我就可以将低级查询别名绑定到结果类的正确成员。以下是自定义setter的代码(为简洁起见,仅显示重要的行):

public class CaseInsensitiveSetter {

    public static Setter getSetter(Class<?> theClass, String propertyName) {

        Setter setter;

        if (theClass == Object.class || theClass == null) {
            setter = null;
        } else {
            setter = doGetSetter(theClass, propertyName);

            if (setter != null) {
                if (!ReflectHelper.isPublic(theClass, setter.getMethod())) {
                    setter.getMethod().setAccessible(true);
                }
            } else {
                setter = doGetSetter(theClass.getSuperclass(), propertyName);
                if (setter == null) {
                    Class<?>[] interfaces = theClass.getInterfaces();
                    for (int i = 0; setter == null && i < interfaces.length; i++) {
                        setter = doGetSetter( interfaces[i], propertyName);
                    }
                }
            }
            if (setter == null) {
                throw new PropertyNotFoundException( 
                    "Could not find a setter for property " + 
                    propertyName + " in class " + theClass.getName());
            }
        }
        return setter;
    }

    // The actual work is done here
    private static Setter doGetSetter(Class<?> resultClass, String propertyName) {

        Method[] methods = resultClass.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            // only carry on if the method has 1 parameter
            if ( methods[i].getParameterTypes().length == 1 ) {
                String methodName = methods[i].getName();

                if (methodName.startsWith("set")) {
                    String testStdMethod = methodName.substring(3);
                    if (testStdMethod.equalsIgnoreCase(propertyName)) {
                        Setter result = new CustomSetter(
                            resultClass, methods[i], propertyName);
                        return result;
                    }
                }
            }
        }
        return null;
    }
}

此源代码基于Hibernate附带的BaseSetter类,但更改为支持不区分大小写的匹配。尽管如此,由于反射的大量使用,这个以及Hibernate使用的原始类缺乏性能。

另外,请记住,如果结果类包含名称在不区分大小写的比较中相同的不同属性,则当前代码中只会选择其中一个属性,并且它可能无法按预期工作。