让数据访问更加自由

针对 Smart 2.3-SNAPSHOT 版本

最近几天连续写了几篇 Smart 定制特性的文章,因为定制特性是 Smart 2.3(即将发布)中最重要的特性。前面给大家分享了:

今天,要与大家分享的是:数据访问器也是可以定制的。

所谓数据访问器(DataAccessor)其实就是通过 JDBC 操作数据库的一个接口。根据 Smart 的风格,一定会提供一个默认的实现,这个实现是基于 Apache Commons DbUtils 的。或许您觉得这个实现不够强大,那么可以编写一个 Plugin,通过简单的配置,即可使用您自己的 DataAccessor,同时,Smart 默认的 DataAccessor 将会禁用。

下面我将对 DataAccessor 的实现过程进行描述。过程非常简短,精彩只是瞬间。

第一步:提供 DataAccessor 接口

这个 DataAccessor 接口应该尽可能提供一些非常通用的 DAO 操作,例如:

<!-- lang: java -->
/**
 * 数据访问器
 *
 * @author huangyong
 * @since 2.3
 */
public interface DataAccessor {

    /**
     * 查询对应的实体,返回单条记录
     */
    <T> T queryEntity(Class<T> entityClass, String sql, Object... params);

    /**
     * 查询对应的实体列表,返回多条记录
     */
    <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params);

    /**
     * 查询对应的实体列表,返回单条记录(主键 => 实体)
     */
    <K, V> Map<K, V> queryEntityMap(Class<V> entityClass, String sql, Object... params);

    /**
     * 查询对应的数据,返回单条记录
     */
    Object[] queryArray(String sql, Object... params);

    /**
     * 查询对应的数据,返回多条记录
     */
    List<Object[]> queryArrayList(String sql, Object... params);

    /**
     * 查询对应的数据,返回单条记录(列名 => 数据)
     */
    Map<String, Object> queryMap(String sql, Object... params);

    /**
     * 查询对应的数据,返回多条记录(列名 => 数据)
     */
    List<Map<String, Object>> queryMapList(String sql, Object... params);

    /**
     * 查询对应的数据,返回单条数据(列名 => 数据)
     */
    <T> T queryColumn(String sql, Object... params);

    /**
     * 查询对应的数据,返回多条数据(列名 => 数据)
     */
    <T> List<T> queryColumnList(String sql, Object... params);

    /**
     * 查询指定列名对应的数据,返回多条数据(列名对应的数据 => 列名与数据的映射关系)
     */
    <T> Map<T, Map<String, Object>> queryColumnMap(String column, String sql, Object... params);

    /**
     * 查询记录条数,返回总记录数
     */
    long queryCount(String sql, Object... params);

    /**
     * 执行更新操作(包括:update、insert、delete),返回所更新的记录数
     */
    int update(String sql, Object... params);

    /**
     * 插入一条记录,返回插入后的主键
     */
    Serializable insertReturnPK(String sql, Object... params);
}

可见,DataAccessor 接口是面向 SQL 语句的,对外提供了一些 CRUD 操作,这些操作都是经常与我们打交道的。其实也没多少特色,要真说特色的话,第一应该是使用了泛型,第二应该是使用了动态参数。

接口既然有了,下面就轮到实现了,Smart 一直就是用的 DbUtils 实现 DAO 层的,所以默认实现肯定也是基于 DbUtils 的。

第二步:提供默认实现(基于 DbUtils)

其实这个实现类的代码,就是从 DatabaseHelper 里抄过来的,几乎完全一样。

<!-- lang: java -->
/**
 * 默认数据访问器
 * <br/>
 * 基于 Apache Commons DbUtils 实现
 *
 * @author huangyong
 * @since 2.3
 */
public class DefaultDataAccessor implements DataAccessor {

    private static final Logger logger = LoggerFactory.getLogger(DefaultDataAccessor.class);

    private final QueryRunner queryRunner;

    public DefaultDataAccessor() {
        DataSource dataSource = DatabaseHelper.getDataSource();
        queryRunner = new QueryRunner(dataSource);
    }

    @Override
    public <T> T queryEntity(Class<T> entityClass, String sql, Object... params) {
        T result;
        try {
            Map<String, String> fieldMap = EntityHelper.getEntityMap().get(entityClass);
            if (MapUtil.isNotEmpty(fieldMap)) {
                result = queryRunner.query(sql, new BeanHandler<T>(entityClass, new BasicRowProcessor(new BeanProcessor(fieldMap))), params);
            } else {
                result = queryRunner.query(sql, new BeanHandler<T>(entityClass), params);
            }
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return result;
    }

    @Override
    public <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
        List<T> result;
        try {
            Map<String, String> fieldMap = EntityHelper.getEntityMap().get(entityClass);
            if (MapUtil.isNotEmpty(fieldMap)) {
                result = queryRunner.query(sql, new BeanListHandler<T>(entityClass, new BasicRowProcessor(new BeanProcessor(fieldMap))), params);
            } else {
                result = queryRunner.query(sql, new BeanListHandler<T>(entityClass), params);
            }
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return result;
    }

    @Override
    public <K, V> Map<K, V> queryEntityMap(Class<V> entityClass, String sql, Object... params) {
        Map<K, V> entityMap;
        try {
            entityMap = queryRunner.query(sql, new BeanMapHandler<K, V>(entityClass), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return entityMap;
    }

    @Override
    public Object[] queryArray(String sql, Object... params) {
        Object[] array;
        try {
            array = queryRunner.query(sql, new ArrayHandler(), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return array;
    }

    @Override
    public List<Object[]> queryArrayList(String sql, Object... params) {
        List<Object[]> arrayList;
        try {
            arrayList = queryRunner.query(sql, new ArrayListHandler(), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return arrayList;
    }

    @Override
    public Map<String, Object> queryMap(String sql, Object... params) {
        Map<String, Object> map;
        try {
            map = queryRunner.query(sql, new MapHandler(), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return map;
    }

    @Override
    public List<Map<String, Object>> queryMapList(String sql, Object... params) {
        List<Map<String, Object>> fieldMapList;
        try {
            fieldMapList = queryRunner.query(sql, new MapListHandler(), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return fieldMapList;
    }

    @Override
    public <T> T queryColumn(String sql, Object... params) {
        T entity;
        try {
            entity = queryRunner.query(sql, new ScalarHandler<T>(), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return entity;
    }

    @Override
    public <T> List<T> queryColumnList(String sql, Object... params) {
        List<T> list;
        try {
            list = queryRunner.query(sql, new ColumnListHandler<T>(), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return list;
    }

    @Override
    public <T> Map<T, Map<String, Object>> queryColumnMap(String column, String sql, Object... params) {
        Map<T, Map<String, Object>> map;
        try {
            map = queryRunner.query(sql, new KeyedHandler<T>(column), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return map;
    }

    @Override
    public long queryCount(String sql, Object... params) {
        long result;
        try {
            result = queryRunner.query(sql, new ScalarHandler<Long>("count(*)"), params);
        } catch (SQLException e) {
            logger.error("查询出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return result;
    }

    @Override
    public int update(String sql, Object... params) {
        int result;
        try {
            Connection conn = DatabaseHelper.getConnection();
            result = queryRunner.update(conn, sql, params);
        } catch (SQLException e) {
            logger.error("更新出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return result;
    }

    @Override
    public Serializable insertReturnPK(String sql, Object... params) {
        Serializable key = null;
        try {
            Connection conn = DatabaseHelper.getConnection();
            PreparedStatement pstmt = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
            if (ArrayUtil.isNotEmpty(params)) {
                for (int i = 0; i < params.length; i++) {
                    pstmt.setObject(i + 1, params[i]);
                }
            }
            int rows = pstmt.executeUpdate();
            if (rows == 1) {
                ResultSet rs = pstmt.getGeneratedKeys();
                if (rs.next()) {
                    key = (Serializable) rs.getObject(1);
                }
            }
        } catch (SQLException e) {
            logger.error("插入出错!", e);
            throw new RuntimeException(e);
        }
        printSQL(sql);
        return key;
    }

    private static void printSQL(String sql) {
        logger.debug("[Smart] SQL - {}", sql);
    }
}

既然 DatabaseHelper 的代码被挪到 DefaultDataAccessor 里了,那么 DatabaseHelper 现在应该是什么样子呢?

第三步:简化 DatabaseHelper

可以想象,DatabaseHelper 一定被简化了,方法还是那些方法,只是实现已经委托给 DataAccessor 了。以下仅对 DatabaseHelper 的部分相关代码进行描述:

<!-- lang: java -->
/**
 * 封装数据库相关操作
 *
 * @author huangyong
 * @since 1.0
 */
public class DatabaseHelper {

    ...

    /**
     * 获取数据访问器
     */
    private static final DataAccessor dataAccessor = InstanceFactory.getDataAccessor();

    ...

    /**
     * 根据 SQL 语句查询 Entity
     */
    public static <T> T queryEntity(Class<T> entityClass, String sql, Object... params) {
        return dataAccessor.queryEntity(entityClass, sql, params);
    }

    /**
     * 根据 SQL 语句查询 Entity 列表
     */
    public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
        return dataAccessor.queryEntityList(entityClass, sql, params);
    }

    /**
     * 根据 SQL 语句查询 Entity 映射(Field Name => Field Value)
     */
    public static <K, V> Map<K, V> queryEntityMap(Class<V> entityClass, String sql, Object... params) {
        return dataAccessor.queryEntityMap(entityClass, sql, params);
    }

    /**
     * 根据 SQL 语句查询 Array 格式的字段(单条记录)
     */
    public static Object[] queryArray(String sql, Object... params) {
        return dataAccessor.queryArray(sql, params);
    }

    /**
     * 根据 SQL 语句查询 Array 格式的字段列表(多条记录)
     */
    public static List<Object[]> queryArrayList(String sql, Object... params) {
        return dataAccessor.queryArrayList(sql, params);
    }

    /**
     * 根据 SQL 语句查询 Map 格式的字段(单条记录)
     */
    public static Map<String, Object> queryMap(String sql, Object... params) {
        return dataAccessor.queryMap(sql, params);
    }

    /**
     * 根据 SQL 语句查询 Map 格式的字段列表(多条记录)
     */
    public static List<Map<String, Object>> queryMapList(String sql, Object... params) {
        return dataAccessor.queryMapList(sql, params);
    }

    /**
     * 根据 SQL 语句查询指定字段(单条记录)
     */
    public static <T> T queryColumn(String sql, Object... params) {
        return dataAccessor.queryColumn(sql, params);
    }

    /**
     * 根据 SQL 语句查询指定字段列表(多条记录)
     */
    public static <T> List<T> queryColumnList(String sql, Object... params) {
        return dataAccessor.queryColumnList(sql, params);
    }

    /**
     * 根据 SQL 语句查询指定字段映射(多条记录)
     */
    public static <T> Map<T, Map<String, Object>> queryColumnMap(String column, String sql, Object... params) {
        return dataAccessor.queryColumnMap(column, sql, params);
    }

    /**
     * 根据 SQL 语句查询记录条数
     */
    public static long queryCount(String sql, Object... params) {
        return dataAccessor.queryCount(sql, params);
    }

    /**
     * 执行更新语句(包括:update、insert、delete)
     */
    public static int update(String sql, Object... params) {
        return dataAccessor.update(sql, params);
    }

    /**
     * 执行插入语句,返回插入后的主键
     */
    public static Serializable insertReturnPK(String sql, Object... params) {
        return dataAccessor.insertReturnPK(sql, params);
    }
}

这样,DatabaseHelper 还是以前的模样,只是一部分职责已经交给了 DataAccessor,这些类之间的关系也非常清晰:

在此输入图片描述

我们仍然是面向 DatabaseHelper 编程,完全可以当 DataAccessor 不存在,除非您想对 DataAccessor 进行定制了。

最后一步:提供定制特性

与之前的做法一样,我们稍微给 InstanceFactory 添加几行代码即可完成定制特性。

<!-- lang: java -->
/**
 * 实例工厂
 *
 * @author huangyong
 * @since 2.3
 */
public class InstanceFactory {

    /**
     * 用于缓存对应的实例
     */
    private static final Map<String, Object> cache = new ConcurrentHashMap<String, Object>();

    ...

    /**
     * DataAccessor
     */
    private static final String DATA_ACCESSOR = "smart.framework.custom.data_accessor";

    ...

    /**
     * 获取 DataAccessor
     */
    public static DataAccessor getDataAccessor() {
        return getInstance(DATA_ACCESSOR, DefaultDataAccessor.class);
    }

    ...

    @SuppressWarnings("unchecked")
    public static <T> T getInstance(String cacheKey, Class<T> defaultImplClass) {
        // 若缓存中存在对应的实例,则返回该实例
        if (cache.containsKey(cacheKey)) {
            return (T) cache.get(cacheKey);
        }
        // 从配置文件中获取相应的接口实现类配置
        String implClassName = ConfigHelper.getString(cacheKey);
        // 若实现类配置不存在,则使用默认实现类
        if (StringUtil.isEmpty(implClassName)) {
            implClassName = defaultImplClass.getName();
        }
        // 通过反射创建该实现类对应的实例
        T instance = ObjectUtil.newInstance(implClassName);
        // 若该实例不为空,则将其放入缓存
        if (instance != null) {
            cache.put(cacheKey, instance);
        }
        // 返回该实例
        return instance;
    }
}

这样一个具备定制特性的 DataAccessor 就开发完毕了,您可以在 smart.properties 配置文件中,使用自己的 DataAccessor 实现,这样数据访问的实现就更加自由了。

<!-- lang: java -->
smart.framework.custom.data_accessor=您的 DataAccessor 实现类

下次将与大家分享 Smart MVC 的定制特性,包括:HandlerMapping、HandlerInvoker、HandlerExceptionResolver 与 ViewResolver,这些组件都是可以定制的。我们下回见!


欢迎下载 Smart 源码:

http://git.oschina.net/huangyong/smart

欢迎阅读 Smart 博文:

http://my.oschina.net/huangyong/blog/158380

转载于:https://my.oschina.net/huangyong/blog/265378

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值