hibernate动态sql查询(仿ibatis查询)

hibernate动态sql查询(仿ibatis查询),让你sql代码再也木有if else 了

项目中使用hibernate作为数据持久层框架,主要考虑hibernate在进行一些简单的crud操作时非常便利,不需要和ibatis似的为每个sql操作都写一堆文件,但是同时也带来了一些局限性,如类似ibatis强大的动态查询功能用不了了,看着别人用hibernate拼sql语句一串串if else,吓死个人,此时想到了ibatis强大的动态语句查询,一咬牙,百度之,自己思考测试一下,仿制ibatis查询,麻麻再也不用看那么多if else 吓人


1)

用到的技术dom4j解析xml

2)

Freemark生成模板

 

正文来了(感谢百度,谷歌获得灵感,修改,整合就是自己的)

设计思路
  先看一下ibatis的动态查询时怎么做的
     <select id="getUserList"resultMap="user">
         select * from user
           <isGreaterThan prepend="and" property="id"compareValue="0">
                  where user_id = #userId#
           </isGreaterThan>
             orderby createTime desc
    </select>
  ibatis在程序实现内部实现原理:

 我猜是这样 首先加载xml文件 然后解析xml文件,最后生成标准sql文件。

 那我何不自己伪造一个呢,要想当年freemaker的能力,那解析模板技术啥子都可以,呵呵。

a.首先自创xml文件

Ibatis不是把sql放到xml里吗,小弟也依葫芦画瓢也来一下,不过简单点:

 在web-inf 下新建dynamic_hibernate_sql.xml内容如下

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!DOCTYPE dynamic-hibernate-statement PUBLIC "-//Haier/HOP Hibernate Dynamic Statement DTD 1.0//EN"  
  3. "http://www.haier.com/dtd/dynamic-hibernate-statement-1.0.dtd">  
  4. <dynamic-hibernate-statement>  
  5.     <!-- 查询某个资源下的直接子节点 -->  
  6.     <hql-query name="resource.getChildren">  
  7.     <![CDATA[ 
  8.         from Resource where parent.id=${parentId} and parent.id != id 
  9.     ]]>   
  10.     </hql-query>  
  11.     <!-- 查询系统中所有的root资源 -->  
  12.     <hql-query name="resource.getRoots">  
  13.     <![CDATA[ 
  14.         from Resource where parent.id = id order by orderIndex 
  15.     ]]>   
  16.     </hql-query>  
  17.     <!-- 获取某个用户可访问的某个资源下的所有子资源 -->  
  18.     <sql-query name="resource.getDescendants">  
  19.     <![CDATA[ 
  20.         select distinct t.id, 
  21.                         t.name, 
  22.                         t.description, 
  23.                         t.url, 
  24.                         t.type, 
  25.                         t.status, 
  26.                         t.code, 
  27.                         t.configuration, 
  28.                         t.module_name, 
  29.                         t.gmt_create, 
  30.                         t.gmt_modified, 
  31.                         t.create_by, 
  32.                         t.last_modified_by, 
  33.                         t.order_index, 
  34.                         t.parent_id 
  35.           from resource_info t 
  36.          inner join role_resource rr 
  37.             on t.id = rr.resource_id 
  38.          inner join user_role ur 
  39.             on rr.role_id = ur.role_id 
  40.          where ur.user_id = ${userId} 
  41.          <#if type == '1'> 
  42.             and t.type=1 
  43.             <#else> 
  44.             and t.type=0 
  45.          </#if> 
  46.            and t.type =  ${type} 
  47.            and t.status =  ${status} 
  48.          start with t.code = '${code}' 
  49.         connect by nocycle prior t.id = t.parent_id 
  50.     ]]>   
  51.     </sql-query>  
  52. </dynamic-hibernate-statement> 
  53.  

b.系统加载文件

  这个阶段程序负责将指定路径下的动态sql文件加载到内存中,一次性缓存起来,没错,这些东西只需要加载一次,以后直接读取就行了,没必要每次去查找,缓存也非常简单,一个Map<String,String>就搞定,key是sql-query或hql-query元素的name属性,value就是与其对应的sql/hql语句

我用的是spring框架,那么用什么类可以再它加载是,运行配置文件里,没错,大家想到了

<!-- Spring环境加载 -->

 <listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

ContextLoaderListener这个类,我也可以继承他啊,自己来加载,呵呵

 

<!-- 动态sql/hql语句加载器 -->

 <listener>

        <listener-class>com.deal.util.DsqlContextLoaderListener</listener-class>

    </listener>

DsqlContextLoaderListener类如下

/**

 * @author cooly

 * 动态sql/hql语句加载器

 */

public classDsqlContextLoaderListenerextendsContextLoaderListener {

 

    @Override

    public voidcontextInitialized(ServletContextEvent event) {

        ApplicationContextac =  WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());

        DefaultDynamicSqlBuilderdynamicSqlBuilder = (DefaultDynamicSqlBuilder)ac.getBean("dynamicSqlBuilder");//后文有

        try {

            dynamicSqlBuilder.init();

             

        }catch(Exception e) {

            // TODO: handle exception

        }

       

    }

 

}

dynamicSqlBuilder需要spring配置

<!-- 动态dynamicStatement sql -->

     <bean id="dynamicSqlBuilder"class="com.deal.util.DefaultDynamicSqlBuilder"> 

        <property name="fileNames"> 

            <list> 

                <value>classpath*:dynamic_hibernate_*.xml</value><!--这里我们指定要加载某个文件夹下所有以- dynamic_hibernate_.xml结尾的文件 --> 

            </list> 

        </property> 

    </bean> 

b.xml解析

重点来了,解析xml放入map里

public classDefaultDynamicSqlBuilder implements ResourceLoaderAware {

     private staticfinalLogger LOGGER= Logger.getLogger(DefaultDynamicSqlBuilder.class);

        private static Map<String, String> namedHQLQueries;//hql语句缓存

        private static Map<String, String> namedSQLQueries//sql语句缓存

        private String[] fileNames = new String[0]; //set 注入

        private ResourceLoader resourceLoader;   //set 注入

      

        /**

         * 查询语句名称缓存,不允许重复

         */ 

        private Set<String> nameCache = newHashSet<String>(); 

     

        public void setFileNames(String[] fileNames) { 

            this.fileNames = fileNames; 

        } 

     

       

        public static Map<String, String>getNamedHQLQueries() { 

            return namedHQLQueries

        } 

     

       

        public static Map<String, String>getNamedSQLQueries() { 

            return namedSQLQueries

        } 

     

        //初始化

        public void init() throws IOException {

        LOGGER.info("初始化加载动态sql语句");

            namedHQLQueries = new HashMap<String,String>(); 

            namedSQLQueries = new HashMap<String,String>(); 

            boolean flag = this.resourceLoader instanceofResourcePatternResolver; 

            for (String file : fileNames) { 

                if (flag) { 

                    Resource[] resources =((ResourcePatternResolver) this.resourceLoader).getResources(file); 

                    buildMap(resources); 

                } else

                    Resource resource = resourceLoader.getResource(file); 

                    buildMap(resource); 

                } 

            } 

            //clear name cache 

            nameCache.clear(); 

        } 

     

      

        public void setResourceLoader(ResourceLoaderresourceLoader) { 

            this.resourceLoader = resourceLoader; 

        } 

     

        //解析resources

        private void buildMap(Resource[] resources) throws IOException { 

            if (resources == null) { 

                return

            } 

            for (Resource resource : resources) { 

                buildMap(resource); 

            } 

        } 

     

        @SuppressWarnings({ "rawtypes" }) 

        //解析xml讲动态语句sql方法map

        private void buildMap(Resource resource) { 

            InputSource inputSource = null

            try

                inputSource = newInputSource(resource.getInputStream());

                SAXReader saxReader = new SAXReader();

                Document doc = saxReader.read(inputSource);

                final Element dynamicHibernateStatement =doc.getRootElement(); 

                    Iterator rootChildren =dynamicHibernateStatement.elementIterator(); 

                    while (rootChildren.hasNext()) { 

                        final Element element = (Element)rootChildren.next(); 

                        final String elementName = element.getName(); 

                        if ("sql-query".equals(elementName)) { 

                            putStatementToCacheMap(resource,element, namedSQLQueries); 

                        } else if ("hql-query".equals(elementName)){ 

                           putStatementToCacheMap(resource, element, namedHQLQueries); 

                        } 

                    } 

               

            } catch (Exception e) { 

                LOGGER.error(e.toString()); 

                throw new RuntimeException(e); 

            } finally

                if (inputSource != null && inputSource.getByteStream() != null) { 

                    try

                        inputSource.getByteStream().close(); 

                    } catch (IOException e) { 

                        LOGGER.error(e.toString()); 

                        throw new RuntimeException(e); 

                    } 

                } 

            } 

     

        } 

     

        private void putStatementToCacheMap(Resource resource, final Element element,Map<String, String> statementMap) 

                throws IOException { 

            String sqlQueryName =element.attribute("name").getText(); 

            Validate.notEmpty(sqlQueryName); 

            if (nameCache.contains(sqlQueryName)) { 

                throw new RuntimeException("重复的sql-query/hql-query语句定义在文件:" + resource.getURI() + "中,必须保证name的唯一."); 

            } 

            nameCache.add(sqlQueryName); 

            String queryText = element.getText();

            LOGGER.info("缓存key="+sqlQueryName+"value="+queryText);

            statementMap.put(sqlQueryName,queryText); 

        }

 

         

}

 

一看就明白,不解释了。

现在sql已经缓存了,是该freemaker出场了。

 

 

 

C.freemarker还原真实sql

通过queryName从缓存中查找出其对应的sql/hql语句(最原始的,里面带有freemarker语法)

然后通过freemarker模板和传递进去的parameters参数对模板进行解析,得到最终的语句(纯sql/hql)

 

 我们得到的fromTmDocTemplate t  wheret.auditStage='${stage}' order by t.orders,freemaker田田就可以了

工具包

/**

     * @param key 模板名称

     * @param value 模板值

     * @param rootMap 传参数

     * @return

     * @throws Exception

     */

    public static String  parseConfig(String key,Stringvalue,Map<String,Object> rootMap) {

        try {

            Configurationconfiguration = newConfiguration(); 

             configuration.setNumberFormat("#"); 

             StringTemplateLoader stringLoader = new StringTemplateLoader();

             Template t = new Template(key,new StringReader(value),configuration);

            

            configuration.setTemplateLoader(stringLoader);

             StringWriter sw = new StringWriter(100);

             t.process(rootMap, sw);

            System.out.println("解析后模板="+sw.toString());

             return sw.toString();

        }catch(Exception e) {

            // TODO: handle exception

            throw new RuntimeException(e);

        }

       

         

    }

  d.使用

 调用程序通过sql/hql语句的name属性和传入查询参数来得到最终解析出来的语句

我们期望的方法可能是这样的:

public <X> List<X> findByNamedQuery(final String queryName, final Map<String, ?> parameters)   

最后将解析后的sql/hql传递给底层api,返回查询结果

 

 

会freemaker语法的人,写sql完全能做到iBATIS那样了,强大的动态查询,代码里再也看不到一个if else 了,是不是好爽啊

 

阅读更多
个人分类: java
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭