• 手写DAO框架(五)-DAO层实现


    -------前篇:手写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  欢迎关注

    下期:手写DAO框架(六)-框架使用示例

  • 相关阅读:
    国标ikecin插座资料
    LED平面管测试资料
    elementui使用MessageBox 弹框自定义内容$msgbox:实现一个textarea文本输入框
    ajax请求的时候后台有三个服务器地址
    前端用xshell向后端服务器部署项目
    vue+echarts 实现map3D地图tooltip弹框读取后台返回的数据,并显示弹框
    AWS网络架构及知识概述
    K8s控制器 StatefulSet
    pod的数据持久化2 NFS
    Pod的数据持久化1 hostPath 和emptyDir
  • 原文地址:https://www.cnblogs.com/shuimutong/p/11386656.html
Copyright © 2020-2023  润新知