我想要实现的是在以下列方式定义的查询上设置结果转换器:
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;
}
}
问题是,虽然我已将id
和objectName
指定为我的别名,但正在执行的实际查询使用不同的别名。这导致我的AliasToBeanResultTransformer
无法构造MyObject
,因为别名与属性名称不匹配。
是否有可能以编程方式获取hibernate生成的查询的别名(我可以将它们设置为bean结果转换器的别名)?我尝试使用query.getReturnAliases()
,但它返回我在HQL中定义的别名,而不是Hibernate实际使用的别名。
我可以在createQuery
语句中明确指定别名吗?目前我正在尝试不使用这个标准来工作,所以我很欣赏使用查询对象的方法,如果存在的话。
更新
虽然上述问题对于标准HQL查询无效(请参阅注释),但在执行本机查询时它是有效的。具体而言 - 本机查询似乎将所有别名视为低位字符串(尽管可能在查询中引入了特定的大小写)。在大写问题很重要的情况下,这会导致AliasToBeanResultTransformer
在设置属性时失败。
答案 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使用的原始类缺乏性能。
另外,请记住,如果结果类包含名称在不区分大小写的比较中相同的不同属性,则当前代码中只会选择其中一个属性,并且它可能无法按预期工作。