自定义持久层框架:

基于jdbc,模仿mybaties

1:核心类:Configuration configuration ,MappedStatement mappedStatement

作用: configuration 用来封装解析过的核心配置文件;包含了DataSource,Map<MappedStatement>map

        MappedStatement用来分装解析的mapper.xml文件解析出的内容:


自定义框架:

1:读取配置文件以流的形式存在内存中:可以封装工具类:


2:解析流:将配置文件信息解析到Configuration ,MappedStatement对象中:

Configuration对象中存在map集合:Map<MappedStatement>map

key为namespace(命名空间mapper文件)+id(sql的id),value为解析的mapper.xml文件的封装对象:代码如下:

一般是buil类中创建的:这里使用了建造者生产模式:

// 第一:使用dom4j解析配置文件,将解析出来的内容封装到Configuration中

XMLConfigBuilder xmlConfigBuilder =new XMLConfigBuilder();

Configuration configuration =xmlConfigBuilder.parseConfig(in);

// 第二:创建sqlSessionFactory对象:工厂类:生产sqlSession:会话对象

DefaultSqlSessionFactory defaultSqlSessionFactory =new DefaultSqlSessionFactory(configuration);     

如何解析:parseConfig()方法

一般是获取xml文件的根节点,然后对接点数据进行解析;并分装到configuration对象中::源码如下:

public Configuration parseConfig(InputStream inputStream)throws DocumentException,PropertyVetoException {

//dom4j的类SAXReader().read()方法来获取document对象

    Document document =new SAXReader().read(inputStream);

//<configuration>获取根元素<configuration>对象

    Element rootElement =document.getRootElement();

//获取属性集合

    Listlist =rootElement.selectNodes("//property");

Properties properties =new Properties();

for (Element element :list) {

String name =element.attributeValue("name");

String value =element.attributeValue("value");

properties.setProperty(name,value);

}

//向数据库中放入数据

    ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();

comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));

comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));

comboPooledDataSource.setUser(properties.getProperty("username"));

comboPooledDataSource.setPassword(properties.getProperty("password"));

//存入configuration中

    configuration.setDataSource(comboPooledDataSource);

//mapper.xml解析: 拿到路径--字节输入流---dom4j进行解析

    ListmapperList =rootElement.selectNodes("//mapper");

for (Element element :mapperList) {

String mapperPath =element.attributeValue("resource");

InputStream resourceAsSteam =Resources.getResourceAsSteam(mapperPath);

XMLMapperBuilder xmlMapperBuilder =new XMLMapperBuilder(configuration);

xmlMapperBuilder.parse(resourceAsSteam);

}

return configuration;

}

在mybaties底层分装解析配置文件的内容远比自定义的要多:这里只是解析的比较核心的类容:

注意点:这里解析了:DataSource:和mapper:

注意如何解析mapper.xml:源码:


通过读取配置文件中的resource;来获取mapper.xml的文件路径:对mapper.xml文件进行解析分装:

再来看一下:如何解析mapper.xml文件:思考一下:解析哪些类容:

1:如何获取指定的标签:也就是,如何执行指定的sql语句:要解析    namespace 和sql的id:组成key;

2:有了独一无二的key,map集合中又该封装什么类容:上文提到:核心类中的MappedStatement:这个类:中要封装哪些类容:

id标识,返回值类型,参数值类型,sql语句:当然这也是简化的:mybaties底层封住的远比这多:看一下自定义底层解析代码:

//获取selected属性

    Listlist =rootElement.selectNodes("//select");

for (Element element :list) {

String id =element.attributeValue("id");

String resultType =element.attributeValue("resultType");

String paramterType =element.attributeValue("paramterType");

//获取sql语句

        String sqlText =element.getTextTrim();

MappedStatement mappedStatement =new MappedStatement();

mappedStatement.setId(id);

mappedStatement.setResultType(resultType);

mappedStatement.setParamterType(paramterType);

mappedStatement.setSql(sqlText);

String key =namespace+"."+id;

configuration.getMappedStatementMap().put(key,mappedStatement);

}

//获取insert属性

    List insertList =rootElement.selectNodes("//insert");

for (Element element :insertList) {

//获取标签中的id

        String id =element.attributeValue("id");

//获取参数类型

        String paramterType =element.attributeValue("parameterType");

//获取sql语句

        String sql =element.getTextTrim();

//将获取的数据封装到mappedStament对象中

        MappedStatement mappedStatement =new MappedStatement();

mappedStatement.setId(id);

mappedStatement.setParamterType(paramterType);

mappedStatement.setSql(sql);

String key=namespace+"."+id;

configuration.getMappedStatementMap().put(key,mappedStatement);

}

//mapper获取update属性:

    List updateList =rootElement.selectNodes("//update");

for (int i =0; i

//获取标签中的id

        String id =updateList.get(i).attributeValue("id");

//获取参数类型

        String parameterType =updateList.get(i).attributeValue("parameterType");

//获取sql语句

        String sql =updateList.get(i).getTextTrim();

//封装入mappedStatement对象中:

        MappedStatement mappedStatement =new MappedStatement();

mappedStatement.setId(id);

mappedStatement.setParamterType(parameterType);

mappedStatement.setSql(sql);

String key=namespace+"."+id;

configuration.getMappedStatementMap().put(key,mappedStatement);

}

//mapper获取delete属性:

    List deleteList =rootElement.selectNodes("//delete");

for (int i =0; i

//获取标签中的id

        String id =deleteList.get(i).attributeValue("id");

//获取参数类型

        String parameterType =deleteList.get(i).attributeValue("parameterType");

//获取sql语句

        String sql =deleteList.get(i).getTextTrim();

//封装入mappedStatement对象中:

        MappedStatement mappedStatement =new MappedStatement();

mappedStatement.setId(id);

mappedStatement.setParamterType(parameterType);

mappedStatement.setSql(sql);

String key=namespace+"."+id;

configuration.getMappedStatementMap().put(key,mappedStatement);

}

mapper.xml文件中有对标签类型的不同解析:解析:sql的id,返回值类型,参数类型,sql语句:解析完存入map中:返回configuration:

创建sqlSessionFactory对象:工厂类:生产sqlSession:会话对象

这个是为了传递configuration;对象:将对数据库的操作方法;放在实体类中:

包含3个方法:查询单个:查询全部:以及uodate方法:

以update的方法为例:这里调用的是执行器的方法:

到执行器方法中看一下:如何对数据库进行操作的:

//注册驱动获取连接

Connection connection = configuration.getDataSource().getConnection();

//获取语句字段,并将#{字段}替换成?,将字段存到ParameterMapping中的content中

String sql = mappedStatement.getSql();

BoundSql boundSql = getBoundSql(sql);

//获取预编译语句对象:

PreparedStatement preparedStatement =connection.prepareStatement(boundSql.getSqlText());

//获取参数值:获取参数类型,获取参数字段:提供反射获取参数类型,再通过字段名获取字段值

String paramterType = mappedStatement.getParamterType();

//获取参数类型对象

ClassclassType = getClassType(paramterType);

//访问指定参数名称的值存在ParameterMapping的list集合中

ListparameterMappingList =boundSql.getParameterMappingList();

for (int i =0; i

ParameterMapping parameterMapping =parameterMappingList.get(i);

String content =parameterMapping.getContent();

//获取返回值类型中的指定字段的对象

    Field field =classType.getDeclaredField(content);

//设置暴力访问

    field.setAccessible(true);

//提供类对象来获取值对象中的值

    Object o =field.get(params[0]);

preparedStatement.setObject(i+1,o);

}

//执行sql

int i =preparedStatement.executeUpdate();

connection.close();

preparedStatement.close();

return i;

这里:执行的注册驱动:创建连接对象:创建语句对象:对sql语句解析:获取参数值:替换占位符:执行sql:

这里连接一下:getBound(sql)方法:

ParameterMappingTokenHandler标记处理类:里面有:List<>parameterMappings集合;和方法:handleToken这个方法是将#{值}返回成?;将值注入到ParameterMapping存到parameterMappings中:

GenericTokenParser:标记解析器:负责解析sql,并调用标记处理类对象:

将:解析后的sql,和存放值得parameterMappings存入BoundSql对象中:

获取参数值:反射通过mappedStatement对象中的参数类型,获取class对象:在通过class对象,获取指定字段的指定filed对象:设置暴力访问:获取值filed.get(实例参数的实例:)获取参数值:对于编译语句替换执行:



关于getMapper()方式类执行sql语句:

分析:底层是通过获取代理对象:提供代理对象调用invoke方法的方式来实现操作:

如何区分:getMpper(调用的)是哪个方法:这里需要:mapper.xml的namespace的空间名,和接口的权限定名一致:接口中的方法名和sql的id名一致:

在defaultSqlsession中定义GetMapper()方法:

public T getMapper(Class mapperClass) {

// 使用JDK动态代理来为Dao接口生成代理对象,并返回

        Object proxyInstance =Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(),new Class[]{mapperClass},new InvocationHandler() {

//在代理对象执行方法是,后调用invoke

            @Override

            public Object invoke(Object proxy,Method method,Object[] args)throws Throwable {

// 底层都还是去执行JDBC代码 //根据不同情况,来调用selctList或者selectOne

                // 准备参数 1:statmentid :sql语句的唯一标识:namespace.id= 接口全限定名.方法名

                // 方法名:findAll

                String methodName = method.getName();

//接口的权限定名

                String className = method.getDeclaringClass().getName();

//statementId

                String statementId =className+"."+methodName;

/*

                // 准备参数2:params:args

                // 获取被调用方法的返回值类型类型是<>,接口规定的返回值类型是<>

Type genericReturnType = method.getGenericReturnType();

                // 判断是否进行了 泛型类型参数化返回值类型需要有<>为泛型if(genericReturnType instanceof ParameterizedType){

List objects = selectList(statementId, args);

return objects;

}

return selectOne(statementId,args);

*/

              if (methodName.startsWith("find")){

// 准备参数2:params:args

                  // 获取被调用方法的返回值类型类型是<>,接口规定的返回值类型是<>

                  Type genericReturnType = method.getGenericReturnType();

// 判断是否进行了 泛型类型参数化返回值类型需要有<>为泛型

                  if(genericReturnType instanceof ParameterizedType){

Listobjects = selectList(statementId, args);

return objects;

}else {

return selectOne(statementId,args);

}

}else {

return  update(statementId, args);

}

我这里是通过方法 名来区分是查询还是update()方法的:并不完善:

以上就是自定义持久层的理解:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容