`

Spring JdbcTemplate实现通用的泛型dao三:构建动态sql

 
阅读更多

Spring JdbcTemplate实现通用的泛型dao一:主功能实现

Spring JdbcTemplate实现通用的泛型dao二:实现自己的名称转换NameHandler 

Spring JdbcTemplate实现通用的泛型dao三:构建动态sql

Spring JdbcTemplate实现通用的泛型dao四:通用自定义转换到JavaBean的RowMapper实现

构建动态sql,其实说白了就是拼装sql语句,在这里我把传入的实体参数,属性有值的拼装进sql,为null的则忽略,要实现这个不用说,肯定要利用Java的反射功能,来看一个具有代表性的insert语句的构建:

/**
 * 构建insert语句
 * 
 * @param entity 实体映射对象
 * @param nameHandler 名称转换处理器
 * @return
 */publicstaticSqlContext buildInsertSql(Object entity,NameHandler nameHandler){Class<?> clazz = entity.getClass();String tableName = nameHandler.getTableName(clazz.getSimpleName());String primaryName = nameHandler.getPrimaryName(clazz.getSimpleName());StringBuilder sql =newStringBuilder("insert into ");List<Object>params=newArrayList<Object>();
    sql.append(tableName);//获取属性信息BeanInfo beanInfo =ClassUtils.getSelfBeanInfo(clazz);PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    sql.append("(");StringBuilder args =newStringBuilder();
    args.append("(");for(PropertyDescriptor pd : pds){Object value = getReadMethodValue(pd.getReadMethod(), entity);if(value ==null){continue;}
        sql.append(nameHandler.getColumnName(pd.getName()));
        args.append("?");params.add(value);
        sql.append(",");
        args.append(",");}
    sql.deleteCharAt(sql.length()-1);
    args.deleteCharAt(args.length()-1);
    args.append(")");
    sql.append(")");
    sql.append(" values ");
    sql.append(args);returnnewSqlContext(sql, primaryName,params);}

 

众所周知,Java的反射是性能较低的,也有性能较好的第三方实现如cglib,这里并没有使用。在我的实测中两者差距不大。

但是注意这里并没有使用属性的操作方式,也就是没有使用jdk反射获取属性的getDeclaredFields()方法,而是使用了BeanInfo和PropertyDescriptor,因为后者的运行效率要远远高于前者。

在我的实测中,构建一个拥有12个属性的JavaBean的动态sql,十万次所耗时间为900毫秒左右,完全可以接受。当然,这里对JavaBean的信息进行了缓存,如果不缓存时间将多耗上几个数量级。

下面顺便贴上完整的代码:

/**
 * sql辅助为类
 * 
 * User: liyd
 * Date: 2/13/14
 * Time: 10:03 AM
 */publicclassSqlUtils{/** 日志对象 */privatestaticfinalLogger LOG =LoggerFactory.getLogger(SqlUtils.class);/**
     * 构建insert语句
     *
     * @param entity 实体映射对象
     * @param nameHandler 名称转换处理器
     * @return
     */publicstaticSqlContext buildInsertSql(Object entity,NameHandler nameHandler){Class<?> clazz = entity.getClass();String tableName = nameHandler.getTableName(clazz.getSimpleName());String primaryName = nameHandler.getPrimaryName(clazz.getSimpleName());StringBuilder sql =newStringBuilder("insert into ");List<Object>params=newArrayList<Object>();
        sql.append(tableName);//获取属性信息BeanInfo beanInfo =ClassUtils.getSelfBeanInfo(clazz);PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        sql.append("(");StringBuilder args =newStringBuilder();
        args.append("(");for(PropertyDescriptor pd : pds){Object value = getReadMethodValue(pd.getReadMethod(), entity);if(value ==null){continue;}
            sql.append(nameHandler.getColumnName(pd.getName()));
            args.append("?");params.add(value);
            sql.append(",");
            args.append(",");}
        sql.deleteCharAt(sql.length()-1);
        args.deleteCharAt(args.length()-1);
        args.append(")");
        sql.append(")");
        sql.append(" values ");
        sql.append(args);returnnewSqlContext(sql, primaryName,params);}/**
     * 构建更新sql
     * 
     * @param entity
     * @param nameHandler
     * @return
     */publicstaticSqlContext buildUpdateSql(Object entity,NameHandler nameHandler){Class<?> clazz = entity.getClass();StringBuilder sql =newStringBuilder();List<Object>params=newArrayList<Object>();String tableName = nameHandler.getTableName(clazz.getSimpleName());String primaryName = nameHandler.getPrimaryName(clazz.getSimpleName());//获取属性信息BeanInfo beanInfo =ClassUtils.getSelfBeanInfo(clazz);PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();

        sql.append("update ");
        sql.append(tableName);
        sql.append(" set ");Object primaryValue =null;for(PropertyDescriptor pd : pds){Object value = getReadMethodValue(pd.getReadMethod(), entity);if(value ==null){continue;}String columnName = nameHandler.getColumnName(pd.getName());if(primaryName.equalsIgnoreCase(columnName)){
                primaryValue = value;}
            sql.append(columnName);
            sql.append(" = ");
            sql.append("?");params.add(value);
            sql.append(",");}
        sql.deleteCharAt(sql.length()-1);
        sql.append(" where ");
        sql.append(primaryName);
        sql.append(" = ?");params.add(primaryValue);returnnewSqlContext(sql, primaryName,params);}/**
     * 构建查询条件
     * 
     * @param entity
     * @param nameHandler
     */publicstaticSqlContext buildQueryCondition(Object entity,NameHandler nameHandler){//获取属性信息BeanInfo beanInfo =ClassUtils.getSelfBeanInfo(entity.getClass());//        PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(entityClass);PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();StringBuilder condition =newStringBuilder();List<Object>params=newArrayList<Object>();int count =0;for(PropertyDescriptor pd : pds){Object value = getReadMethodValue(pd.getReadMethod(), entity);if(value ==null){continue;}if(count >0){
                condition.append(" and ");}
            condition.append(nameHandler.getColumnName(pd.getName()));
            condition.append(" = ?");params.add(value);
            count++;}returnnewSqlContext(condition,null,params);}/**
     * 获取属性值
     *
     * @param readMethod
     * @param entity
     * @return
     */privatestaticObject getReadMethodValue(Method readMethod,Object entity){if(readMethod ==null){returnnull;}try{if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())){
                readMethod.setAccessible(true);}return readMethod.invoke(entity);}catch(Exception e){
            LOG.error("获取属性值失败", e);thrownewMincoderException(e);}}}

 

获取BeanInfo时写了一个ClassUtils来实现,里面对Bean信息进行了缓存。因为项目使用spring,本来想使用spring提供的BeanUtils.getPropertyDescriptor()方法的,里面同样拥有缓存,但是该方法会把实体类父类的属性信息也获取出来,而PropertyDescriptor中又没法判断,这将直接导致拼装sql时字段的错误,因为你不知道哪些字段是操作当前表所需要的。没办法,查看jdk本身的Introspector类,发现里面有如下方法定义:

publicstaticBeanInfo getBeanInfo(Class<?> beanClass,Class<?> stopClass)throwsIntrospectionException

 

即可以指定在哪个类停止获取属性,这正是我们需要的,可惜spring没有进行封装,只能自己实现了,参考了spring的实现,使用WeakHashMap来防止内存的溢出,及时清空Introspector本身的缓存:

/**
 * 类辅助
 *
 * User: liyd
 * Date: 2/12/14
 * Time: 10:08 PM
 */publicclassClassUtils{/** 日志对象 */privatestaticfinalLogger               LOG        =LoggerFactory.getLogger(ClassUtils.class);/**
     * Map keyed by class containing CachedIntrospectionResults.
     * Needs to be a WeakHashMap with WeakReferences as values to allow
     * for proper garbage collection in case of multiple class loaders.
     */privatestaticfinalMap<Class,BeanInfo> classCache =Collections.synchronizedMap(newWeakHashMap<Class,BeanInfo>());/**
     * 获取类本身的BeanInfo,不包含父类属性
     * 
     * @param clazz
     * @return
     */publicstaticBeanInfo getSelfBeanInfo(Class<?> clazz){try{BeanInfo beanInfo;if(classCache.get(clazz)==null){
                beanInfo =Introspector.getBeanInfo(clazz, clazz.getSuperclass());
                classCache.put(clazz, beanInfo);// Immediately remove class from Introspector cache, to allow for proper// garbage collection on class loader shutdown - we cache it here anyway,// in a GC-friendly manner. In contrast to CachedIntrospectionResults,// Introspector does not use WeakReferences as values of its WeakHashMap!Class classToFlush = clazz;do{Introspector.flushFromCaches(classToFlush);
                    classToFlush = classToFlush.getSuperclass();}while(classToFlush !=null);}else{
                beanInfo = classCache.get(clazz);}return beanInfo;}catch(IntrospectionException e){
            LOG.error("获取BeanInfo失败", e);thrownewMincoderException(e);}}/**
     * 初始化实例
     * 
     * @param clazz
     * @return
     */publicstaticObject newInstance(Class<?> clazz){try{return clazz.newInstance();}catch(Exception e){
            LOG.error("根据class创建实例失败", e);thrownewMincoderException(e);}}}

另外创建了对象SqlContext来保存构建后的sql和参数信息,定义如下:

 

/**
 * 执行sql的上下文内容
 * 
 * User: liyd
 * Date: 2/13/14
 * Time: 10:40 AM
 */publicclassSqlContext{/** 执行的sql */privateStringBuilder sql;/** 主键名称 */privateString        primaryKey;/** 参数,对应sql中的?号 */privateList<Object>params;publicSqlContext(StringBuilder sql,String primaryKey,List<Object>params){this.sql = sql;this.primaryKey = primaryKey;this.params=params;}//getter setter 略}
分享到:
评论

相关推荐

    JdbcTemplate通用泛型Dao实现

    本文将深入探讨`JdbcTemplate`通用泛型Dao实现的相关知识点,帮助开发者更好地理解和应用这一技术。 首先,让我们了解什么是`JdbcTemplate`。它是Spring框架的一部分,用于处理SQL操作。`JdbcTemplate`提供了一组...

    Java Web程序运用中泛型DAO的作用.zip

    在实际的Java Web应用中,我们可以结合Spring框架的Hibernate或JPA支持,进一步简化泛型DAO的实现,利用Spring的模板类如JdbcTemplate、HibernateTemplate等,自动处理SQL执行和结果映射。 总的来说,泛型DAO在Java...

    ssh2 + dao泛型

    在SSH2框架中,我们可以利用Spring的JdbcTemplate或MyBatis等工具来实现DAO层的泛型化。这些工具提供了强大的SQL执行能力,与泛型结合可以大大减少代码量,避免了重复的模板代码。例如,我们可以定义一个泛型方法`...

    编写通用Dao

    在这个接口中,`T`代表任何继承了特定基类或实现了特定接口的实体类,这得益于Java的泛型机制。`save`方法用于保存新实体到数据库,`update`用于更新已有实体,`deleteById`根据ID删除实体,`findById`通过ID查询...

    DBHelp

    3. **Spring JDBC**:在Java世界中,Spring框架提供了JDBC模板(JdbcTemplate)和SQL映射工具(MyBatis)等,可以方便地构建DAO层。如果`GenericDaoImpl`是在Spring环境中,那么它可能使用了这些工具来简化数据库...

    基于注解整合spring与hibernate

    在整合Spring与Hibernate时,我们通常会使用Spring的JdbcTemplate或JPA(Java Persistence API)来管理数据库操作。但是,由于Hibernate的强大功能和灵活性,很多开发者选择使用Hibernate作为ORM工具。在Spring中...

    SpringBoot_BaseDao.zip

    在IT行业中,SpringBoot是一个非常流行...通过学习这个压缩包中的内容,开发者可以加深对SpringBoot数据库操作的理解,掌握泛型和反射的实战技巧,并了解如何构建一个基础的DAO层。这将有助于提高开发效率和代码质量。

    ssh数据库基本操作封装

    在SSH框架中,泛型可以被应用到DAO(Data Access Object)层,创建泛型DAO接口和实现,以减少代码重复,提高代码复用性和可维护性。例如,你可以创建一个泛型BaseDAO接口,如下所示: ```java public interface ...

    spring3.0帮助文档

    Spring 3.0 完全基于Java 5进行构建,这意味着它充分利用了Java 5及更高版本的语言特性,如泛型、枚举、可变参数等,这为开发者提供了更为简洁的编程模型。 ##### 2.2 文档改进 随着Spring 3.0的到来,官方提供了...

    Spring 3.0 新特性

    Spring 3.0的JdbcTemplate和SimpleJdbcTemplate提供了更方便的数据库操作API,简化了数据库访问层的开发,减少了SQL注入的风险。 8. **对JSR-303(Bean Validation)的支持** Spring 3.0开始支持JSR-303 Bean ...

    手写ORM框架笔记.txt

    - **实现**:利用Spring框架提供的`JdbcTemplate`类,它可以方便地执行SQL语句,并处理结果集。通过这种方式,可以将数据库操作与上层业务逻辑分离,使得代码更加清晰易懂。 ##### 8. EntityOperation的ORM过程 - *...

Global site tag (gtag.js) - Google Analytics