hibernate部分源码解析and解决工作上关于hibernate的一个问题例子(包含oracle中新建表为何列名全转为大写且通过hibernate取数时如何不用再次遍历将列名(key)值转为小写)

  最近在研究系统启动时将数据加载到内存非常耗时,想着是否有办法优化!经过日志打印测试发现查询时间(查询时间:将数据库数据查询到系统中并转为List<Map>或List<*.Class>,下面将全部针对转化类型为List<Map>进行分析)居然和数据加载时间一样长(加载时间:将查询到的数据组装成系统中业务所需要的数据模型,基本调用了所有key为get/set方法)。由此我觉得系统查询时间是有优化的空间的,并通过两个周末对此进行了研究学习并优化此问题,一下是整体流程:

  1、疑问点

    查询时间为何耗时如此耗时

    1.1、分析问题

      系统DAO层使用的是hibernate,但是经过查看代码后发现与我想象中不一样的代码,如下:

Connection con = arg0.connection();//此处调用来了connection方法,而查看源码后发现此方法已经被弃用了(只是取connection的方法换了一种不影响)
Statement state = con.createStatement();
ResultSet resultSet=null;
try{
  resultSet = state.executeQuery(sql[0]);
  //遍历resultset,去除columnName和value——在遍历取columnName时将其转为小写,统一代码中map的key值,方便在加载数据时取数,此处的解释在后边分析中会用到

      此处通过session的到jdbc的连接connection,然后通过jdbc从数据库取数(感觉因为再次需要单独遍历一遍result,所以耗时多)!!!接下来疑问产生了,既然用了hibernate为什么还要通过jdbc取,为什么不直接通过session.createsqlquery的得到query,然后通过query.list直接获取所需list<Map>hibernate方式取数

    1.2、解决并验证问题  

        接下来我通过修改代码使用两种方式同时进行取数并打印取数时间,发现通过hibernate方式取数耗时仅仅是通过jdbc取数耗时的一半,本来问题就此解决了,但新的一个问题出现了!!!!

  2、新问题出现

    通过hibernate方式取出的list<Map>的key值全为大写!!!

    2.1、分析下问题_1

      list<Map>的key值全为大写不符合系统现在已有的取数逻辑,修改已有的取数代码会很耗时切不好,重新遍历使用String.tolower方法的话岂不是和通过jdbc取一样需要重新遍历,耗时的问题就决绝不了!那此时我想到的决绝方案是:重写hibernate的createsqlquery方法,让其在调用源码中原有的遍历方法时对其进行转换。

    2.2、解决并验证问题

      2.2.1 关于oracle建表时列名写的小写

        oracle建表时列名写的小写,为何建表后列名变为大写!且通过hibernate或jdbc取数时并没有做关于列名大小写转换的控制。

            之前没有注意到这类问题,在新建表时我是不会关注列名大小写的,大牛们应该会关注吧!!其实很疑问,以我写sql的习惯,我的列名应该都是小写,为什么建好后查询时列名全为大写了!此处我查了相关资料,发现如果sql中列名没有加引号,oracle会默认将列名转为大写!!!!

      2.2.2 关于重写SqlQueryImpl.createsqlquery的一个内部类_在此内部类中遍历取出的数据进行封装的

        我重写了此方法,红色部分为修改处。如下:

/**
     * Warning: adds new parameters to the argument by side-effect, as well as
     * mutating the query string!
     */
    protected String expandParameterLists(Map namedParamsCopy) {
        String query = this.queryString;
        Iterator iter = namedParameterLists.entrySet().iterator();
        while ( iter.hasNext() ) {
            Map.Entry me = (Map.Entry) iter.next();
            query = expandParameterList( query, ((String) me.getKey()).tolower(), (TypedValue) me.getValue(), namedParamsCopy );
        }
        return query;
    }

        在此处我重写了这个内部类,但我想要调用我重写的内部类,我必须new我自己重写的SqlQueryImpl类;但是如果new SqlQueryImpl的话需要很多hibernate的内部类型参数,对于这些参数我可以说是 非常不了解!但如果不想new SqlQueryImpl的话,我发现就必须重写整个hibernatejar包,从最开始的session就调用自己重写的!!这是不可行的,那我就只能研究new SqlQueryImpl的所需参数了,这里我看了一下午hibernate源码,可以说也提升了我对hibernate源码的整体框架有了部分的了解,在下篇博客中我会对x下午所了解到的hibernate源码涉及到的框架进行分析。

        在研究过源码后我发现了query的一个变量ResultTransformer,这事我才想到调用query.list前是需要设置query.setResultTransformer;变量ResultTransformer顾顾名思义应该就是控制查询结果的类型的,如下:

          Query query = session.createSQLQuery(sql);
                query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
//              query.setResultTransformer(CriteriaSpecificationAddMapToLower.ALIAS_TO_ENTITY_MAP_KEY_TOLOWER);//此处是我自己封装的类型
                list = query.list();

        经过查看源码发现就是此参数对返回值类型进行控制的(这里的推论还没来得及验证,是根据源码得到了理论!)。那我们研究下这个参数吧:

/**
 * @author Gavin King
 */
public class AliasToEntityMapResultTransformer implements ResultTransformer {

    public Object transformTuple(Object[] tuple, String[] aliases) {
        Map result = new HashMap(tuple.length);
        for ( int i=0; i<tuple.length; i++ ) {
            String alias = aliases[i];
            if ( alias!=null ) {
                result.put( alias.tolower, tuple[i] );
            }
        }
        return result;
    }

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

         这是这个类型参数的一个实现类,是将结果转为标准list<Map>型!那我们只需要重写此方法就可以了~~~~~~~~红色部分为重写部分

           理论上这个问题就得到解决了,但实际中并未验证,需要大数据量才能验证,到工作日时我会利用下班时间进行测试,并将测试结果添加到下方。

 


  由于对hibernate源码没有研究过,所以在解决此问题时走了很多弯路,但也在弯路中更加细致的了解了hibernate源码相关知识和部分源码框架以及已经部分方法为何启用,新替代方法是什么等知识点,在下片博客中我会记录下自己一步步走来的脚印~

                                                                      加油~Mr.liu

      

    

    

  

posted @ 2017-11-19 17:36  Mr.Liu_Blog  阅读(537)  评论(0编辑  收藏  举报