------- 前篇: 手写 DAO 框架 (四)-SQL 执行 https://www.cnblogs.com/shuimutong/p/11068288.html ---------
前言
通过上一篇, 可以通过传入 sql 和对应的参数, 可以执行 sql 并返回结果. 但是对于一个 DAO 框架来说, 要尽量的面向对象编程, 也就是要实现 ORM 功能. 所以本篇主要介绍 DAO 层的实现, 主要是实现 ORM.
主要涉及技术
反射
反射
Class<T> entityClass = (Class<T>) ((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
这一段代码位于 BaseDao 中, 相当于是整个 ORM 的开端, 目的是获取子类通过泛型制定的具体类型.
举个例子:
- // 父类
- package me.lovegao.gdao.demo;
- import java.lang.reflect.ParameterizedType;
- public class ParentDemo<T> {
- private Class<T> entityClass;
- protected ParentDemo() {
- entityClass = (Class<T>) ((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
- }
- public Class<T> getEntityClass() {
- return entityClass;
- }
- }
- // 子类
- package me.lovegao.gdao.demo;
- public class ChildDemo extends ParentDemo<Integer> {
- public static void main(String[] args) {
- ParentDemo p = new ChildDemo();
- System.out.println(p.getEntityClass());
- }
- }
运行代码, 就可以得到子类在继承父类时泛型定义的类型.
本例结果:
class java.lang.Integer
BaseDao 实现
- package me.lovegao.gdao.ORM;
- import java.io.Serializable;
- import java.lang.reflect.ParameterizedType;
- import java.util.List;
- import me.lovegao.gdao.bean.GTableClassParseInfo;
- import me.lovegao.gdao.bean.TwoTuple;
- import me.lovegao.gdao.sqlexecute.ISqlExecutor;
- import me.lovegao.gdao.util.GDaoCommonUtil;
- import me.lovegao.gdao.util.GDaoOrmUtil;
- import me.lovegao.gdao.util.GenerateSqlUtil;
- /**
- * 操作对象的 dao
- * @author simple
- *
- * @param <T>
- * @param <PK>
- */
- public class BaseDao<T, PK extends Serializable> {
- private Class<T> entityClass;
- private GTableClassParseInfo entityClassParseInfo;
- private ISqlExecutor sqlExecutor;
- @SuppressWarnings("unchecked")
- protected BaseDao(ISqlExecutor sqlExecutor) {
- entityClass = (Class<T>) ((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
- this.sqlExecutor = sqlExecutor;
- entityClassParseInfo = GDaoCommonUtil.parseClass(entityClass);
- }
- /**
- * 新增
- * @param entity
- * @return
- * @throws Exception
- */
- public PK add(T entity) throws Exception {
- PK id = null;
- if(entity != null) {
- TwoTuple<String, Object[]> sqlResult = GenerateSqlUtil.addSql(entityClassParseInfo, entity);
- id = sqlExecutor.insert(sqlResult.a, sqlResult.b);
- }
- return id;
- }
- /**
- * 批量新增
- * @param list
- * @throws Exception
- */
- public void addBatch(List<T> list) throws Exception {
- if(!GDaoCommonUtil.checkCollectionEmpty(list)) {
- TwoTuple<String, List<Object[]>> sqlList = GenerateSqlUtil.addBatchSql(entityClassParseInfo, list);
- sqlExecutor.insertOrUpdateBatch(sqlList.a, sqlList.b);
- }
- }
- /**
- * 根据主键删除数据
- * @param id
- * @throws Exception
- */
- public void deleteByPK(PK id) throws Exception {
- TwoTuple<String, PK> sqlIdEntity = GenerateSqlUtil.deleteByPKSql(entityClassParseInfo, id);
- sqlExecutor.update(sqlIdEntity.a, new Object[] {sqlIdEntity.b});
- }
- /**
- * 更新
- * @param entity
- * @throws Exception
- */
- public void update(T entity) throws Exception {
- TwoTuple<String, Object[]> tuple = GenerateSqlUtil.updateSql(entityClassParseInfo, entity);
- sqlExecutor.update(tuple.a, tuple.b);
- }
- /**
- * 根据主键查找
- * @param id
- * @return
- * @throws Exception
- */
- public T queryByPK(PK id) throws Exception {
- TwoTuple<String, Object[]> tuple = GenerateSqlUtil.queryByPKSql(entityClassParseInfo, id);
- TwoTuple<List<Object[]>, String[]> resultTuple = sqlExecutor.queryValueAndColumn(tuple.a, tuple.b);
- List<T> list = GDaoOrmUtil.convertObject2T(resultTuple.a, resultTuple.b, entityClassParseInfo);
- if(!GDaoCommonUtil.checkCollectionEmpty(list)) {
- return list.get(0);
- }
- return null;
- }
- /**
- * 查询对象列表
- * @param sql 查询 sql
- * @param replaceValues sql 中对应? 的值
- * @return 包装类列表
- * @throws Exception
- */
- public List<T> list(String sql, Object... replaceValues) throws Exception {
- TwoTuple<List<Object[]>, String[]> resultTuple = sqlExecutor.queryValueAndColumn(sql, replaceValues);
- List<T> list = GDaoOrmUtil.convertObject2T(resultTuple.a, resultTuple.b, entityClassParseInfo);
- return list;
- }
- /**
- * 普通查询, 结果需要自己转义
- * @param sql 查询 sql
- * @param replaceValues sql 中对应? 的值
- * @return List<{列 1, 列 2}>
- * @throws Exception
- */
- public List<Object[]> normalList(String sql, Object... replaceValues) throws Exception {
- List<Object[]> list = sqlExecutor.query(sql, replaceValues);
- return list;
- }
- /**
- * 统计个数
- * @param sql 有且仅有 count() 的 sql
- * @param replaceValues sql 中对应? 的值
- * @return
- * @throws Exception
- */
- public long count(String sql, Object... replaceValues) throws Exception {
- List<Object[]> list = sqlExecutor.query(sql, replaceValues);
- if(!GDaoCommonUtil.checkCollectionEmpty(list)) {
- return (long) list.get(0)[0];
- }
- return 0;
- }
- }
BaseDao 的定义主要是用于被其他类继承的. 后期会有示例代码.
GTableClassParseInfo 实现
在子类继承 BaseDao 之后, 进行初始化的时候, 需要对定义的类进行解析, 为了避免每次都解析类, 所以对类的解析结果进行了一个保存, 通过 GTableClassParseInfo 这个类来保存的. 具体定义如下.
- package me.lovegao.gdao.bean;
- import java.lang.reflect.Field;
- import java.util.Map;
- /**
- * 被 GTable 注解的类解析后的信息
- * @author simple
- *
- */
- public class GTableClassParseInfo {
- /** 类 **/
- private Class<?> clazz;
- /** 表名 **/
- private String tableName;
- /** 主键名称 **/
- private String pkName;
- /** 主键的变量 **/
- private Field pkField;
- /** 主键自动生成 **/
- private boolean pkAutoGenerate;
- /** 声明了表字段的变量名列表, 不含主键 **/
- private Field[] fields;
- /** 声明的数据库表字段名列表, 和 fields 顺序对应, 不含主键 **/
- private String[] tableColumnNames;
- /** 数据库字段和 field 对应关系, 包含主键和非主键 < columnName, Field>**/
- private Map<String, Field> allColumnFieldMap;
- public Class<?> getClazz() {
- return clazz;
- }
- public void setClazz(Class<?> clazz) {
- this.clazz = clazz;
- }
- public String getTableName() {
- return tableName;
- }
- public void setTableName(String tableName) {
- this.tableName = tableName;
- }
- public Field[] getFields() {
- return fields;
- }
- public void setFields(Field[] fields) {
- this.fields = fields;
- }
- public String[] getTableColumnNames() {
- return tableColumnNames;
- }
- public void setTableColumnNames(String[] tableColumnNames) {
- this.tableColumnNames = tableColumnNames;
- }
- public String getPkName() {
- return pkName;
- }
- public void setPkName(String pkName) {
- this.pkName = pkName;
- }
- public boolean isPkAutoGenerate() {
- return pkAutoGenerate;
- }
- public void setPkAutoGenerate(boolean pkAutoGenerate) {
- this.pkAutoGenerate = pkAutoGenerate;
- }
- public Field getPkField() {
- return pkField;
- }
- public void setPkField(Field pkField) {
- this.pkField = pkField;
- }
- public Map<String, Field> getAllColumnFieldMap() {
- return allColumnFieldMap;
- }
- public void setAllColumnFieldMap(Map<String, Field> allColumnFieldMap) {
- this.allColumnFieldMap = allColumnFieldMap;
- }
- }
GDaoCommonUtil 实现
为了把子类通过泛型定义的类转化为 GTableClassParseInfo, 需要通过一个转换的方法.
- package me.lovegao.gdao.util;
- import java.lang.reflect.Field;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import me.lovegao.gdao.bean.GTableClassParseInfo;
- import me.lovegao.gdao.bean.annotation.GColumn;
- import me.lovegao.gdao.bean.annotation.GId;
- import me.lovegao.gdao.bean.annotation.GTable;
- public class GDaoCommonUtil {
- ......
- /**
- * 解析被表注解的类
- * @param clazz
- * @return
- */
- public static GTableClassParseInfo parseClass(Class<?> clazz) {
- GTable table = clazz.getAnnotation(GTable.class);
- if(table == null) {
- throw new NullPointerException("类没有声明 GTable 注解");
- }
- String tableName = table.value();
- Field[] fields = clazz.getDeclaredFields();
- List<Field> fieldList = new ArrayList();
- List<String> fieldNames = new ArrayList();
- Map<String, Field> allColumnFieldMap = new HashMap();
- String pkName = "";
- Field pkField = null;
- boolean pkAutoGenerate = false;
- for(Field field : fields) {
- if(field.isAnnotationPresent(GColumn.class)) {
- GColumn column = field.getAnnotation(GColumn.class);
- // 主键声明
- if(field.isAnnotationPresent(GId.class)) {
- if(pkField != null) {
- throw new RuntimeException("===== 错误: 一个数据库对象做多只能定义一个主键 ======");
- }
- GId pkColumn = field.getAnnotation(GId.class);
- pkName = column.name();
- pkField = field;
- pkAutoGenerate = pkColumn.isAutoIncrease();
- } else {
- fieldList.add(field);
- fieldNames.add(column.name());
- }
- allColumnFieldMap.put(column.name(), field);
- }
- }
- GTableClassParseInfo tableInfo = new GTableClassParseInfo();
- tableInfo.setClazz(clazz);
- tableInfo.setTableName(tableName);
- tableInfo.setPkName(pkName);
- tableInfo.setPkField(pkField);
- tableInfo.setPkAutoGenerate(pkAutoGenerate);
- tableInfo.setFields(fieldList.toArray(new Field[0]));
- tableInfo.setTableColumnNames(fieldNames.toArray(new String[0]));
- tableInfo.setAllColumnFieldMap(allColumnFieldMap);
- return tableInfo;
- }
- }
至此, 整个 DAO 框架基本完成.
完整代码参见 Git:https://github.com/shuimutong/gdao.git 欢迎关注
下期预告: 使用示例, 性能测试.
来源: http://www.bubuko.com/infodetail-3164909.html