-------前篇:手写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实现

 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  欢迎关注

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

手写DAO框架(五)-DAO层实现的更多相关文章

  1. 手写MVC框架(一)-再出发

    背景 前段时间把之前写的DAO框架(手写DAO框架(一)-从“1”开始)整理了一下,重构了一版.整理过程中看以前写的代码,只是为了了解实现,只是为了实现,代码写的有点粗糙.既然已经整理了DAO框架,索 ...

  2. 手写MVC框架(二)-代码实现和使用示例

    --------上一篇:手写MVC框架(一)-再出发----- 背景 书接上文,之前整理了实现MVC框架需要写哪些东西.这周粗看了一下,感觉也没多少工作量,所以就计划一天时间来完成.周末的时间,哪会那 ...

  3. 手写MQ框架(一)-准备启程

    一.背景 很久以前写了DAO框架和MVC框架,前段时间又重写了DAO框架-GDAO(手写DAO框架(一)-从“1”开始,源码:https://github.com/shuimutong/gdao.gi ...

  4. 手写MQ框架(二)-服务端实现

    一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...

  5. (二)springMvc原理和手写springMvc框架

    我们从两个方面了解springmvc执行原理,首先我们去熟悉springmvc执行的过程,然后知道原理后通过手写springmvc去深入了解代码中执行过程. (一)SpringMVC流程图 (二)Sp ...

  6. 手写SpringMVC 框架

    手写SpringMVC框架 细嗅蔷薇 心有猛虎 背景:Spring 想必大家都听说过,可能现在更多流行的是Spring Boot 和Spring Cloud 框架:但是SpringMVC 作为一款实现 ...

  7. 手写MQ框架(四)-使用netty改造梳理

    一.背景 书接上文手写MQ框架(三)-客户端实现,前面通过web的形式实现了mq的服务端和客户端,现在计划使用netty来改造一下.前段时间学习了一下netty的使用(https://www.w3cs ...

  8. 手写SpringMVC框架(三)-------具体方法的实现

    续接前文 手写SpringMVC框架(二)结构开发设计 本节我们来开始具体方法的代码实现. doLoadConfig()方法的开发 思路:我们需要将contextConfigLocation路径读取过 ...

  9. 手写 jQuery 框架

    1.测试页面; <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  10. 手写RPC框架指北另送贴心注释代码一套

    Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的.所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架. 完整代码以及 ...

随机推荐

  1. openresty开发系列22--lua的元表

    openresty开发系列22--lua的元表 举个例子,在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作. 那如何计算两个table的相加 ...

  2. 从0开始学爬虫7之BeautifulSoup模块的简单介绍

    参考文档: https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/ # 安装 beautifulsoup4 (pytools) D:\pyt ...

  3. 【Java】分布式自增ID算法---雪花算法 (snowflake,Java版)

    一般情况,实现全局唯一ID,有三种方案,分别是通过中间件方式.UUID.雪花算法. 方案一,通过中间件方式,可以是把数据库或者redis缓存作为媒介,从中间件获取ID.这种呢,优点是可以体现全局的递增 ...

  4. redis八大应用场景

    1.缓存 缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力.Redis提供了键过期功能,也提供了灵活的键淘汰策略,所以,现在Redis用在缓 ...

  5. python3传文件到linux服务器然后解压

    #!/usr/bin/env python # -*- coding:utf-8 -*- import os import paramiko import time from scp import S ...

  6. 推荐linux运维必备的几本书

    首先,<鸟哥的linux私房菜> 鸟哥 其次,<linux就该这么学> 刘瑞版 然后,<CentOS linux系统运维> 张祥琳版 最后,<CentOS运维 ...

  7. mysql备份时过滤掉某些库 以及 去掉"Warning: Using a password on the command line interface can be insecure."提示信息

    在对mysql进行完整备份时使用--all-database参数 # mysqldump -u root -h localhost -p --all-database > /root/all.s ...

  8. Hive 企业调优

    9.企业级调优 9.1 Fetch 抓取 Fetch 抓取:Hive 中对某些情况的查询可以不必使用 MapReduce 计算: hive.fetch.task.conversion:more 9.2 ...

  9. LeetCode 941. 有效的山脉数组(Valid Mountain Array)

    941. 有效的山脉数组 941. Valid Mountain Array 题目描述 给定一个整数数组 A,如果它是有效的山脉数组就返回 true,否则返回 false. 让我们回顾一下,如果 A ...

  10. linux 加载新的磁盘(卷组)

    pvcreate /dev/vdbvgcreate datavg /dev/vdblvcreate -n datalv -L 99.8G datavgmkfs.ext3 /dev/datavg/dat ...