基于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()方法的:并不完善:
以上就是自定义持久层的理解: