-------前篇:手写DAO框架(四)-SQL执行---------
前言
通过上一篇,可以通过传入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实现
1 package me.lovegao.gdao.orm; 2 3 import java.io.Serializable; 4 import java.lang.reflect.ParameterizedType; 5 import java.util.List; 6 7 import me.lovegao.gdao.bean.GTableClassParseInfo; 8 import me.lovegao.gdao.bean.TwoTuple; 9 import me.lovegao.gdao.sqlexecute.ISqlExecutor; 10 import me.lovegao.gdao.util.GDaoCommonUtil; 11 import me.lovegao.gdao.util.GDaoOrmUtil; 12 import me.lovegao.gdao.util.GenerateSqlUtil; 13 14 /** 15 * 操作对象的dao 16 * @author simple 17 * 18 * @param <T> 19 * @param <PK> 20 */ 21 public class BaseDao<T, PK extends Serializable> { 22 private Class<T> entityClass; 23 private GTableClassParseInfo entityClassParseInfo; 24 private ISqlExecutor sqlExecutor; 25 26 @SuppressWarnings("unchecked") 27 protected BaseDao(ISqlExecutor sqlExecutor) { 28 entityClass = (Class<T>) ((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 29 this.sqlExecutor = sqlExecutor; 30 entityClassParseInfo = GDaoCommonUtil.parseClass(entityClass); 31 } 32 33 /** 34 * 新增 35 * @param entity 36 * @return 37 * @throws Exception 38 */ 39 public PK add(T entity) throws Exception { 40 PK id = null; 41 if(entity != null) { 42 TwoTuple<String, Object[]> sqlResult = GenerateSqlUtil.addSql(entityClassParseInfo, entity); 43 id = sqlExecutor.insert(sqlResult.a, sqlResult.b); 44 } 45 return id; 46 } 47 48 /** 49 * 批量新增 50 * @param list 51 * @throws Exception 52 */ 53 public void addBatch(List<T> list) throws Exception { 54 if(!GDaoCommonUtil.checkCollectionEmpty(list)) { 55 TwoTuple<String, List<Object[]>> sqlList = GenerateSqlUtil.addBatchSql(entityClassParseInfo, list); 56 sqlExecutor.insertOrUpdateBatch(sqlList.a, sqlList.b); 57 } 58 } 59 60 /** 61 * 根据主键删除数据 62 * @param id 63 * @throws Exception 64 */ 65 public void deleteByPK(PK id) throws Exception { 66 TwoTuple<String, PK> sqlIdEntity = GenerateSqlUtil.deleteByPKSql(entityClassParseInfo, id); 67 sqlExecutor.update(sqlIdEntity.a, new Object[] {sqlIdEntity.b}); 68 } 69 70 /** 71 * 更新 72 * @param entity 73 * @throws Exception 74 */ 75 public void update(T entity) throws Exception { 76 TwoTuple<String, Object[]> tuple = GenerateSqlUtil.updateSql(entityClassParseInfo, entity); 77 sqlExecutor.update(tuple.a, tuple.b); 78 } 79 80 /** 81 * 根据主键查找 82 * @param id 83 * @return 84 * @throws Exception 85 */ 86 public T queryByPK(PK id) throws Exception { 87 TwoTuple<String, Object[]> tuple = GenerateSqlUtil.queryByPKSql(entityClassParseInfo, id); 88 TwoTuple<List<Object[]>, String[]> resultTuple = sqlExecutor.queryValueAndColumn(tuple.a, tuple.b); 89 List<T> list = GDaoOrmUtil.convertObject2T(resultTuple.a, resultTuple.b, entityClassParseInfo); 90 if(!GDaoCommonUtil.checkCollectionEmpty(list)) { 91 return list.get(0); 92 } 93 return null; 94 } 95 96 /** 97 * 查询对象列表 98 * @param sql 查询sql 99 * @param replaceValues sql中对应?的值 100 * @return 包装类列表 101 * @throws Exception 102 */ 103 public List<T> list(String sql, Object... replaceValues) throws Exception { 104 TwoTuple<List<Object[]>, String[]> resultTuple = sqlExecutor.queryValueAndColumn(sql, replaceValues); 105 List<T> list = GDaoOrmUtil.convertObject2T(resultTuple.a, resultTuple.b, entityClassParseInfo); 106 return list; 107 } 108 109 /** 110 * 普通查询,结果需要自己转义 111 * @param sql 查询sql 112 * @param replaceValues sql中对应?的值 113 * @return List<{列1, 列2}> 114 * @throws Exception 115 */ 116 public List<Object[]> normalList(String sql, Object... replaceValues) throws Exception { 117 List<Object[]> list = sqlExecutor.query(sql, replaceValues); 118 return list; 119 } 120 121 /** 122 * 统计个数 123 * @param sql 有且仅有count()的sql 124 * @param replaceValues sql中对应?的值 125 * @return 126 * @throws Exception 127 */ 128 public long count(String sql, Object... replaceValues) throws Exception { 129 List<Object[]> list = sqlExecutor.query(sql, replaceValues); 130 if(!GDaoCommonUtil.checkCollectionEmpty(list)) { 131 return (long) list.get(0)[0]; 132 } 133 return 0; 134 } 135 136 }
BaseDao的定义主要是用于被其他类继承的。后期会有示例代码。
GTableClassParseInfo实现
在子类继承BaseDao之后,进行初始化的时候,需要对定义的类进行解析,为了避免每次都解析类,所以对类的解析结果进行了一个保存,通过GTableClassParseInfo这个类来保存的。具体定义如下。
1 package me.lovegao.gdao.bean; 2 3 import java.lang.reflect.Field; 4 import java.util.Map; 5 6 /** 7 * 被GTable注解的类解析后的信息 8 * @author simple 9 * 10 */ 11 public class GTableClassParseInfo { 12 /**类**/ 13 private Class<?> clazz; 14 /**表名**/ 15 private String tableName; 16 /**主键名称**/ 17 private String pkName; 18 /**主键的变量**/ 19 private Field pkField; 20 /**主键自动生成**/ 21 private boolean pkAutoGenerate; 22 /**声明了表字段的变量名列表,不含主键**/ 23 private Field[] fields; 24 /**声明的数据库表字段名列表,和fields顺序对应,不含主键**/ 25 private String[] tableColumnNames; 26 /**数据库字段和field对应关系,包含主键和非主键<columnName, Field>**/ 27 private Map<String, Field> allColumnFieldMap; 28 29 public Class<?> getClazz() { 30 return clazz; 31 } 32 public void setClazz(Class<?> clazz) { 33 this.clazz = clazz; 34 } 35 public String getTableName() { 36 return tableName; 37 } 38 public void setTableName(String tableName) { 39 this.tableName = tableName; 40 } 41 public Field[] getFields() { 42 return fields; 43 } 44 public void setFields(Field[] fields) { 45 this.fields = fields; 46 } 47 public String[] getTableColumnNames() { 48 return tableColumnNames; 49 } 50 public void setTableColumnNames(String[] tableColumnNames) { 51 this.tableColumnNames = tableColumnNames; 52 } 53 public String getPkName() { 54 return pkName; 55 } 56 public void setPkName(String pkName) { 57 this.pkName = pkName; 58 } 59 public boolean isPkAutoGenerate() { 60 return pkAutoGenerate; 61 } 62 public void setPkAutoGenerate(boolean pkAutoGenerate) { 63 this.pkAutoGenerate = pkAutoGenerate; 64 } 65 public Field getPkField() { 66 return pkField; 67 } 68 public void setPkField(Field pkField) { 69 this.pkField = pkField; 70 } 71 public Map<String, Field> getAllColumnFieldMap() { 72 return allColumnFieldMap; 73 } 74 public void setAllColumnFieldMap(Map<String, Field> allColumnFieldMap) { 75 this.allColumnFieldMap = allColumnFieldMap; 76 } 77 78 }
GDaoCommonUtil实现
为了把子类通过泛型定义的类转化为GTableClassParseInfo,需要通过一个转换的方法。
1 package me.lovegao.gdao.util; 2 3 import java.lang.reflect.Field; 4 import java.util.ArrayList; 5 import java.util.Collection; 6 import java.util.HashMap; 7 import java.util.List; 8 import java.util.Map; 9 10 import me.lovegao.gdao.bean.GTableClassParseInfo; 11 import me.lovegao.gdao.bean.annotation.GColumn; 12 import me.lovegao.gdao.bean.annotation.GId; 13 import me.lovegao.gdao.bean.annotation.GTable; 14 15 public class GDaoCommonUtil { 16 …… 17 /** 18 * 解析被表注解的类 19 * @param clazz 20 * @return 21 */ 22 public static GTableClassParseInfo parseClass(Class<?> clazz) { 23 GTable table = clazz.getAnnotation(GTable.class); 24 if(table == null) { 25 throw new NullPointerException("类没有声明GTable注解"); 26 } 27 String tableName = table.value(); 28 Field[] fields = clazz.getDeclaredFields(); 29 List<Field> fieldList = new ArrayList(); 30 List<String> fieldNames = new ArrayList(); 31 Map<String, Field> allColumnFieldMap = new HashMap(); 32 String pkName = ""; 33 Field pkField = null; 34 boolean pkAutoGenerate = false; 35 for(Field field : fields) { 36 if(field.isAnnotationPresent(GColumn.class)) { 37 GColumn column = field.getAnnotation(GColumn.class); 38 //主键声明 39 if(field.isAnnotationPresent(GId.class)) { 40 if(pkField != null) { 41 throw new RuntimeException("=====错误:一个数据库对象做多只能定义一个主键======"); 42 } 43 GId pkColumn = field.getAnnotation(GId.class); 44 pkName = column.name(); 45 pkField = field; 46 pkAutoGenerate = pkColumn.isAutoIncrease(); 47 } else { 48 fieldList.add(field); 49 fieldNames.add(column.name()); 50 } 51 allColumnFieldMap.put(column.name(), field); 52 } 53 } 54 GTableClassParseInfo tableInfo = new GTableClassParseInfo(); 55 tableInfo.setClazz(clazz); 56 tableInfo.setTableName(tableName); 57 tableInfo.setPkName(pkName); 58 tableInfo.setPkField(pkField); 59 tableInfo.setPkAutoGenerate(pkAutoGenerate); 60 tableInfo.setFields(fieldList.toArray(new Field[0])); 61 tableInfo.setTableColumnNames(fieldNames.toArray(new String[0])); 62 tableInfo.setAllColumnFieldMap(allColumnFieldMap); 63 return tableInfo; 64 } 65 }
至此,整个DAO框架基本完成。
完整代码参见git:https://github.com/shuimutong/gdao.git 欢迎关注