设计思路 使用者 
引入自定义的框架jar 
编写配置文件
dbconfig.xml:数据库配置信息,存放mapper.xml位置 
mapper.xml:sql配置信息 
 
 
 
框架 
加载配置文件:以字节流存储在内存中
创建Resources类:InputStrean getResourceAsStream(String path) 
 
 
创建两个Bean:容器对象,存放配置文件解析结果
Configuration:核心配置类,dbconfig.xml解析结果 
MapperStatement:映射配置类,mapper.xml解析结果 
 
 
解析配置文件:dom4j
创建SqlSessionFactoryBuilder:build(InputStream in) 
使用dom4j解析配置文件,将结果存放在容器对象 
创建SqlSessionFactory对象:生产SqlSession会话对象(工厂模式) 
 
 
创建SqlSessionFactory接口及实现类DefaultSqlSessionFactory
openSession():生产sqlSession 
 
 
创建SqlSession接口及实现类DefaultSqlSession
定义crud操作
selectList() 
selectOne() 
update() 
delete() 
 
 
 
 
创建Executor接口及实现类SimpleExecutor
query(Configuration, MapperStatement, Object… params):执行JDBC代码 
 
 
 
实现 测试项目创建 
在resources目录下新建dbconfig.xml配置文件,填写数据源信息 
 
1 2 3 4 5 6 7 8 9 <configuration >     <dataSource >          <property  name ="driver-class"  value ="com.mysql.cj.jdbc.Driver" />          <property  name ="url"  value ="jdbc:mysql:localhost:3306/mydb" />          <property  name ="username"  value ="root" />          <property  name ="password"  value ="123456" />      </dataSource >  </configuration > 
1 2 3 4 5 6 7 8 9 10 11 12 <mapper  namespace ="user" >          <select  id ="findAll"  resultType ="com.chaofan.pojo.User" >          select * from user     </select >      <select  id ="findOne"  paramType ="com.chaofan.pojo.User"  resultType ="com.chaofan.pojo.User" >          select * from user where id=#{id} and username=#{username}     </select >  </mapper > 
使用namespace来区分不同的mapper.xml文件
在父标签内部,按照sql的不同,选择不同的标签来区别,再通过id属性来区分每一个标签,然后按照namespace.id组成每一个sql的唯一标识
最后在dbconfig.xml中指定mapper.xml的位置 
 
1 <mappers  resource ="UserMapper.xml" /> 
使用端暂时结束,然后开始编写库
目前配置文件总览
1 2 3 4 5 6 7 8 9 10 11 <configuration >     <dataSource >          <property  name ="driver-class"  value ="com.mysql.cj.jdbc.Driver" />          <property  name ="url"  value ="jdbc:mysql:localhost:3306/mydb" />          <property  name ="username"  value ="root" />          <property  name ="password"  value ="123456" />      </dataSource >  	     <mappers  resource ="UserMapper.xml" />  </configuration > 
1 2 3 4 5 6 7 8 9 10 11 12 13 <mapper  namespace ="user" >          <select  id ="findAll"  resultType ="com.chaofan.pojo.User" >          select * from user     </select >      <select  id ="findOne"  paramType ="com.chaofan.pojo.User"  resultType ="com.chaofan.pojo.User" >          select * from user where id=#{id} and username=#{username}     </select >  </mapper > 
自定义框架的实现 创建项目 新建一个IPersistence模块
新建Resources类 编写Resources类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package  com.chaofan.io;import  java.io.InputStream;public  class  Resources  {         public  static  InputStream getResourceAsStream (String path) {         return  Resources.class.getClassLoader().getResourceAsStream(path);     } } 
非常简单,获得当前类的类加载器然后加载配置文件获得输入流
容器对象定义 Configuration核心配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package  com.chaofan.pojo;import  javax.sql.DataSource;import  java.util.HashMap;import  java.util.Map;public  class  Configuration  {    private  DataSource dataSource;          Map<String, MappedStatement> mappedStatementMap = new  HashMap <>();     public  DataSource getDataSource ()  {         return  dataSource;     }     public  void  setDataSource (DataSource dataSource)  {         this .dataSource = dataSource;     }     public  Map<String, MappedStatement> getMappedStatementMap ()  {         return  mappedStatementMap;     }     public  void  setMappedStatementMap (Map<String, MappedStatement> mappedStatementMap)  {         this .mappedStatementMap = mappedStatementMap;     } } 
MappedStatement解析出来的Mapper对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package  com.chaofan.pojo;public  class  MappedStatement  {         private  String id;          private  String resultType;          private  String paramType;          private  String sql;     public  String getId ()  {         return  id;     }     public  void  setId (String id)  {         this .id = id;     }     public  String getResultType ()  {         return  resultType;     }     public  void  setResultType (String resultType)  {         this .resultType = resultType;     }     public  String getParamType ()  {         return  paramType;     }     public  void  setParamType (String paramType)  {         this .paramType = paramType;     }     public  String getSql ()  {         return  sql;     }     public  void  setSql (String sql)  {         this .sql = sql;     } } 
编写配置文件解析类 解析配置文件。保存在Configuration类的实例中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package  com.chaofan.config;import  com.chaofan.io.Resources;import  com.chaofan.pojo.Configuration;import  com.mchange.v2.c3p0.ComboPooledDataSource;import  org.dom4j.Document;import  org.dom4j.Element;import  org.dom4j.io.SAXReader;import  java.io.InputStream;import  java.util.List;import  java.util.Properties;public  class  XMLConfigBuilder  {    private  final  Configuration configuration;     public  XMLConfigBuilder ()  {         this .configuration = new  Configuration ();     }          @SuppressWarnings("unchecked")      public  Configuration parseConfig (InputStream in)  throws  Exception {         Document  document  =  new  SAXReader ().read(in);                  Element  rootElement  =  document.getRootElement();         List<Element> list = 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  dataSource  =  new  ComboPooledDataSource ();         dataSource.setJdbcUrl(properties.getProperty("url" ));         dataSource.setDriverClass(properties.getProperty("driver-class" ));         dataSource.setUser(properties.getProperty("username" ));         dataSource.setPassword(properties.getProperty("password" ));         configuration.setDataSource(dataSource);                           List<Element> mapperList = rootElement.selectNodes("//mapper" );                  for  (Element element : mapperList) {             String  mapperPath  =  element.attributeValue("resource" );             InputStream  mapperStream  =  Resources.getResourceAsStream(mapperPath);             XMLMapperBuilder  xmlMapperBuilder  =  new  XMLMapperBuilder (configuration);             xmlMapperBuilder.parse(mapperStream);         }         return  configuration;     } } 
使用xpath获取所有的<property>标签,然后遍历获取到的List,保存在Properties类的对象中
也可以用Map存储
 
Mapper解析类 解析mapper.xml的每一个sql语句,封装到MappedStatement对象中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package  com.chaofan.config;import  com.chaofan.pojo.Configuration;import  com.chaofan.pojo.MappedStatement;import  org.dom4j.Document;import  org.dom4j.Element;import  org.dom4j.io.SAXReader;import  java.io.InputStream;import  java.util.List;public  class  XMLMapperBuilder  {    private  final  Configuration configuration;     public  XMLMapperBuilder (Configuration configuration)  {         this .configuration = configuration;     }          @SuppressWarnings("unchecked")      public  void  parse (InputStream inputStream)  throws  Exception {         Document  document  =  new  SAXReader ().read(inputStream);         Element  rootElement  =  document.getRootElement();         String  namespace  =  rootElement.attributeValue("namespace" );         List<Element> selectList = rootElement.selectNodes("//select" );         for  (Element element : selectList) {                          String  id  =  element.attributeValue("id" );             String  resultType  =  element.attributeValue("resultType" );             String  paramType  =  element.attributeValue("paramType" );             String  sqlText  =  element.getTextTrim();                          MappedStatement  statement  =  new  MappedStatement ();             statement.setId(id);             statement.setResultType(resultType);             statement.setParamType(paramType);             statement.setSql(sqlText);             String  key  =  namespace + "."  + id;                          configuration.getMappedStatementMap().put(key, statement);         }     } } 
编写SqlSessionFactoryBuilder类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package  com.chaofan.session;import  com.chaofan.config.XMLConfigBuilder;import  com.chaofan.pojo.Configuration;import  java.io.InputStream;public  class  SqlSessionFactoryBuilder  {         public  SqlSessionFactory build (InputStream in)  throws  Exception {                  XMLConfigBuilder  builder  =  new  XMLConfigBuilder ();         Configuration  configuration  =  builder.parseConfig(in);                  return  new  DefaultSqlSessionFactory (configuration);     } } 
把InputStream传给XMLConfigBuilder类解析成Configuration对象,然后返回DefaultSqlSessionFactory对象
SqlSessionFactory接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package  com.chaofan.session;public  interface  SqlSessionFactory  {         SqlSession openSession () ; } 
生产SqlSession,然后编写默认实现类
DefaultSqlSessionFactory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package  com.chaofan.session;import  com.chaofan.pojo.Configuration;public  class  DefaultSqlSessionFactory  implements  SqlSessionFactory  {    private  final  Configuration configuration;     public  DefaultSqlSessionFactory (Configuration configuration)  {         this .configuration = configuration;     }     @Override      public  SqlSession openSession ()  {         return  new  DefaultSqlSession ();     } } 
这个工厂类生产SqlSession对象
SqlSession接口定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package  com.chaofan.session;import  java.util.List;public  interface  SqlSession  {         <E> List<E> selectList (String statementId, Object... params)  throws  Exception;          <T> T selectOne (String statementId, Object... params)  throws  Exception;          int  update (String statementId, Object params)  throws  Exception;          int  save (String statementId, Object params)  throws  Exception;          int  delete (String statementId, Object params)  throws  Exception;          void  close () ; } 
编写SqlSession实现类DefaultSqlSession 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package  com.chaofan.session;import  com.chaofan.pojo.Configuration;import  com.chaofan.pojo.MappedStatement;import  java.util.List;public  class  DefaultSqlSession  implements  SqlSession  {    private  final  Configuration configuration;     private  Executor executor;     public  DefaultSqlSession (Configuration configuration)  {         this .configuration = configuration;     }     @Override      public  <E> List<E> selectList (String statementId, Object... params)  throws  Exception {                  executor = new  SimpleExecutor ();         MappedStatement  mappedStatement  =  configuration.getMappedStatementMap().get(statementId);         return  executor.query(configuration, mappedStatement, params);     }     @Override      public  <T> T selectOne (String statementId, Object... params)  throws  Exception {         List<T> t = selectList(statementId, params);         if  (t.size() == 1 ) {             return  t.get(0 );         } else  if  (t.size() > 1 ){             throw  new  RuntimeException ("返回结果过多" );         }         throw  new  RuntimeException ("返回结果为空" );     }     @Override      public  int  update (String statementId, Object params)  throws  Exception {         executor = new  SimpleExecutor ();         MappedStatement  mappedStatement  =  configuration.getMappedStatementMap().get(statementId);         return  executor.update(configuration, mappedStatement, params);     }     @Override      public  int  save (String statementId, Object params)  throws  Exception {         executor = new  SimpleExecutor ();         MappedStatement  mappedStatement  =  configuration.getMappedStatementMap().get(statementId);         return  executor.update(configuration, mappedStatement, params);     }     @Override      public  int  delete (String statementId, Object params)  throws  Exception {         executor = new  SimpleExecutor ();         MappedStatement  mappedStatement  =  configuration.getMappedStatementMap().get(statementId);         return  executor.update(configuration, mappedStatement, params);     }     @Override      public  void  close ()  {         try  {             executor.close();         }catch (Exception ignore){}     } } 
Executer接口执行sql 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package  com.chaofan.session;import  com.chaofan.pojo.Configuration;import  com.chaofan.pojo.MappedStatement;import  java.sql.SQLException;import  java.util.List;public  interface  Executor  {         <E> List<E> query (Configuration configuration, MappedStatement mappedStatement, Object... params)  throws  Exception;          int  update (Configuration configuration, MappedStatement mappedStatement, Object... params)  throws  Exception;          void  close () ; } 
编写实现类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 package  com.chaofan.session;import  com.chaofan.config.BoundSql;import  com.chaofan.pojo.Configuration;import  com.chaofan.pojo.MappedStatement;import  com.chaofan.utils.GenericTokenParser;import  com.chaofan.utils.ParameterMapping;import  com.chaofan.utils.ParameterMappingTokenHandler;import  java.beans.PropertyDescriptor;import  java.lang.reflect.Field;import  java.lang.reflect.Method;import  java.sql.*;import  java.util.ArrayList;import  java.util.List;public  class  SimpleExecutor  implements  Executor  {    private  PreparedStatement statement;     private  Connection connection;     @Override      @SuppressWarnings({"unchecked", "Duplicates"})      public  <E> List<E> query (Configuration configuration, MappedStatement mappedStatement, Object... params)  throws  Exception {                  connection = configuration.getDataSource().getConnection();                  String  sql  =  mappedStatement.getSql();                  BoundSql  boundSql  =  getBoundSql(sql);                  statement = connection.prepareStatement(boundSql.getSqlText());                  String  paramType  =  mappedStatement.getParamType();         Class<?> type = getClassType(paramType);         List<ParameterMapping> mappingList = boundSql.getParameterMappingList();         for  (int  i  =  0 ; i < mappingList.size(); i++) {             ParameterMapping  parameterMapping  =  mappingList.get(i);             String  content  =  parameterMapping.getContent();             Field  field  =  type.getDeclaredField(content);                          field.setAccessible(true );             E  o  =  (E)field.get(params[0 ]);                          statement.setObject(i+1 , o);         }                  ResultSet  resultSet  =  statement.executeQuery();         String  resultType  =  mappedStatement.getResultType();         Class<?> resultTypeClass = getClassType(resultType);         Object  o  =  resultTypeClass.getDeclaredConstructor().newInstance();                  List<Object> objects = new  ArrayList <>();         while  (resultSet.next()) {             ResultSetMetaData  metaData  =  resultSet.getMetaData();             for  (int  i  =  0 ; i < metaData.getColumnCount(); i++) {                                  String  columnName  =  metaData.getColumnName(i+1 );                 Object  value  =  resultSet.getObject(columnName);                                  PropertyDescriptor  propertyDescriptor  =  new  PropertyDescriptor (columnName, resultTypeClass);                 Method  writeMethod  =  propertyDescriptor.getWriteMethod();                 writeMethod.invoke(o, value);             }             objects.add(o);         }         return  (List<E>) objects;     }     @Override      @SuppressWarnings("Duplicates")      public  int  update (Configuration configuration, MappedStatement mappedStatement, Object... params)  throws  Exception {         connection = configuration.getDataSource().getConnection();         String  sql  =  mappedStatement.getSql();         BoundSql  boundSql  =  getBoundSql(sql);         statement = connection.prepareStatement(boundSql.getSqlText());                  String  paramType  =  mappedStatement.getParamType();         Class<?> type = getClassType(paramType);         List<ParameterMapping> mappingList = boundSql.getParameterMappingList();         for  (int  i  =  0 ; i < mappingList.size(); i++) {             ParameterMapping  parameterMapping  =  mappingList.get(i);             String  content  =  parameterMapping.getContent();             Field  field  =  type.getDeclaredField(content);                          field.setAccessible(true );             Object  o  =  field.get(params[0 ]);                          statement.setObject(i+1 , o);         }                  return  statement.executeUpdate();     }     @Override      public  void  close ()  {         try  {             statement.close();             connection.close();         } catch  (SQLException ignored) {}     }     private  Class<?> getClassType(String paramType) throws  ClassNotFoundException {         if  (paramType != null ) {             return  Class.forName(paramType);         }         throw  new  NullPointerException ("paramType is null" );     }          private  BoundSql getBoundSql (String sql)  {                  ParameterMappingTokenHandler  tokenHandler  =  new  ParameterMappingTokenHandler ();         GenericTokenParser  genericTokenParser  =  new  GenericTokenParser ("#{" , "}" , tokenHandler);                  String  parseSql  =  genericTokenParser.parse(sql);                  List<ParameterMapping> mappings = tokenHandler.getParameterMappings();         return  new  BoundSql (parseSql, mappings);     } } 
最后测试一下 1 2 3 4 5 6 7 8 9 InputStream  inputStream  =  Resources.getResourceAsStream("dbconfig.xml" );SqlSessionFactory  sqlSessionFactory  =  new  SqlSessionFactoryBuilder ().build(inputStream);SqlSession  sqlSession  =  sqlSessionFactory.openSession();User  user  =  new  User ();user.setId("1" ); user.setName("张三" ); User  result  =  sqlSession.selectOne("user.findOne" , user);System.out.println(result); 
补充 根据接口动态生成实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override @SuppressWarnings("unchecked") public  <T> T getMapper (Class<T> mapperClass)  {         Object  o  =  Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new  Class []{mapperClass}, (proxy, method, args) -> {         String  methodName  =  method.getName();         String  className  =  method.getDeclaringClass().getName();         String  statementId  =  className + "."  + methodName;                  Type  genericReturnType  =  method.getGenericReturnType();         if  (genericReturnType instanceof  ParameterizedType) {             return  selectList(statementId, args);         }         return  selectOne(statementId, args);     });     return  (T) o; }