自定义持久层框架:

基于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()方法的:并不完善:

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

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容