Native Query的自定义转换器

Hibernate管理的对象通常都有hbm配置文件或注解。有一些项目需要定义一些DTO对象,比如为了组装报表数据。对于这类不受hibernate管理的对象。在hibernate使用native sql的时候,可以自定义一些transformer来实现查询结果集到List<T>的自动映射,而不必再人工的去做这个转换。

下面是在泛型DAO里面定义的一个泛型方法。注意,这个方法的泛型是与泛型DAO类定义的泛型不同。

View Code
@SuppressWarnings("unchecked")
protected <T1> List<T1> getResultByNativeQuery(final String sql,
final Class<T1> targetClass, final Object[] args,final int...otherArgs) {
List
<T1> list = (List<T1>) getHibernateTemplate().executeFind(
new HibernateCallback() {
@Override
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
Query query
= session
.createSQLQuery(sql)
.setResultTransformer(
new WithoutHibernateMappingTransformer<T1>(
targetClass));
if (args != null) {
for (int i = 0; i < args.length; i++) {
query.setParameter(i, args[i]);
}
}
int position=args.length;
for (int i = 0; i < otherArgs.length; i++) {
query.setParameter(position
+i, otherArgs[i]);
}
return query.list();
}
});
return list;
}

对应的Transfer需要实现自Hibernate提供的接口:ResultTransformer

View Code
import java.math.BigDecimal;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.lang.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.property.ChainedPropertyAccessor;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter;
import org.hibernate.transform.ResultTransformer;

public class WithoutHibernateMappingTransformer<TT> implements
ResultTransformer {

private static final long serialVersionUID = -5199190581393587893L;
private final Class<TT> resultClass;
private Setter[] setters;
private PropertyAccessor propertyAccessor;

public WithoutHibernateMappingTransformer(Class<TT> resultClass) {
if (resultClass == null)
throw new IllegalArgumentException("resultClass cannot be null");
this.resultClass = resultClass;
propertyAccessor
= new ChainedPropertyAccessor(new PropertyAccessor[] {
PropertyAccessorFactory.getPropertyAccessor(resultClass,
null),
PropertyAccessorFactory.getPropertyAccessor(
"field") });
}

public Object transformTuple(Object[] tuple, String[] aliases) {
Object result;
try {
if (setters == null) {
setters
= new Setter[aliases.length];
for (int i = 0; i < aliases.length; i++) {
String alias
= convertColumnToProperty(aliases[i]);
if (alias != null) {
try {
setters[i]
= propertyAccessor.getSetter(
resultClass, alias);
}
catch (PropertyNotFoundException e) {
continue;
}
}
}
}
result
= resultClass.newInstance();
for (int i = 0; i < aliases.length; i++) {
if (setters[i] != null) {
if (tuple[i].getClass().equals(BigDecimal.class)) {
setters[i].set(result, ((BigDecimal) tuple[i])
.longValue(),
null);
}
else{
setters[i].set(result, tuple[i],
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;
}

/**
* Converts the specified 'XXX_YYY_ZZZ'-like column name to its
* 'xxxYyyZzz'-like Java property name.
*
*
@param columnName
* the column name
*
@return the Java property name
*/
public String convertColumnToProperty(String columnName) {
columnName
= columnName.toLowerCase();
StringBuffer buff
= new StringBuffer(columnName.length());
StringTokenizer st
= new StringTokenizer(columnName, "_");
while (st.hasMoreTokens()) {
buff.append(StringUtils.capitalize(st.nextToken()));
}
buff.setCharAt(
0, Character.toLowerCase(buff.charAt(0)));
return buff.toString();
}


@SuppressWarnings(
"unchecked")
public List transformList(List collection) {
return (List<TT>)collection;
}

public static void main(String args[]) {
WithoutHibernateMappingTransformer
<String> transfer = new WithoutHibernateMappingTransformer<String>(
String.
class);
System.out.println(transfer.convertColumnToProperty(
"CPCID"));
System.out.println(transfer.convertColumnToProperty(
"XXX_YYY_ZZZ"));
}
}

另外,对于Hibernate管理的类,可以使用Query query = session.createSQLQuery(sql).addEntity(entityClass)来实现映射。不过,这种方式就需要hbm文件里有相应的property映射Oracle的字段。

注意:

1。Oracle的字段别名都是大写,所以不能使用Hibernate提供的现成方式,这样会导致通过大写的别名去java类里找相应的字段,这会找不到的:

sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS") .setResultTransformer(Transformers.aliasToBean(CatDTO.class))

2. 转换器中针对Oracle返回类型与应用之间的数据类型显示进行了转换。比如Oracle返回的BigDecimal与Long之间的转换。

3.约定是Oracle中的下划线来分隔单词。具体参测试。

4.即使对于Hibernate的受管对象,也是可以使用转换器的。前提实体里定义了相应字段的set方法。

5. 注意到jdk5以后,java的返回类型值是可以被复盖的。比如 Object可以使用T这样的泛型变量替代。

6.在使用转换器的过程中,发现数据库字段如果是date类似,那么oracle在某些jdbc驱动下会将date转换为java中的java.sql.Date. 由于java.sql.Date会trunc掉时分秒,只保留年月日,这样导致应用程序中对应的属性只有年月日,而数据库中的字段即包含年月日+时分秒. 解决的办法有二种:1.将数据库的字段由date改为timestamp. oracle的所有驱动都会将timestamp转换成java中的java.sql.Timestamp类型,而不存在trunc时分秒. 缺点是这样应用程序中改动起来比较麻烦.

2.我推荐的方式: 数据库仍然使用date,但是针对datesource添加oracle.jdbc.V8Compatible=true的连接属性.

比如

<bean id="parentDataSource"
class
="org.springframework.jdbc.datasource.DriverManagerDataSource"
abstract
="true">
<property name="driverClassName"
value
="oracle.jdbc.driver.OracleDriver" />
<!--注意如何注入key-value的properties属性-->
<property name="connectionProperties">
<props>
<prop key="oracle.jdbc.V8Compatible">true</prop>
</props>
</property>
</bean>

或者在jdbc的应用中:

public static void main(String[] args) {     
    try {     
           Class.forName("oracle.jdbc.OracleDriver");     
           Properties prop=new Properties();     
           prop.setProperty("user","system");     
           prop.setProperty("password","dba");     
           prop.setProperty("oracle.jdbc.V8Compatible","true");     
           java.sql.Connection connection1 = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl", prop);     
           System.out.println(connection1);     
           System.out.println(connection1.getMetaData().getDriverName()+" "+connection1.getMetaData().getDriverVersion());     
           ResultSet rs = connection1.createStatement().executeQuery("select date1,date2 from t_test");     
    
        while (rs.next()) {     
            String value1 = rs.getString("DATE1");     
            System.out.println("DATE1=" + value1);     
            String value2 = rs.getString("DATE2");     
            System.out.println("DATE2=" + value2);     
        }     
       }     
       catch (Exception exception1) {     
           exception1.printStackTrace();     
       }     
    
} 

原文地址:https://www.cnblogs.com/highriver/p/1970241.html