针对 Smart 2.3-SNAPSHOT 版本
最近几天连续写了几篇 Smart 定制特性的文章,因为定制特性是 Smart 2.3(即将发布)中最重要的特性。前面给大家分享了:
- 类加载器是可以定制的:http://my.oschina.net/huangyong/blog/264455
- 数据库连接池时可以定制的:http://my.oschina.net/huangyong/blog/265015
今天,要与大家分享的是:数据访问器也是可以定制的。
所谓数据访问器(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 博文: