简化版Mybatis实现思路

  • 1.创建SqlSessionFactory实例.
  • 2.实例化过程中,加载配置文件创建configuration对象.
  • 3.通过factory创建SqlSession对象,把configuaration传入SqlSession.
  • 4.通过SqlSession获取mapper接口动态代理
  • 5.通过代理对调sqlsession中查询方法;
  • 6.sqlsession将查询方法转发给executor;
  • 7.executor基于JDBC访问数据库获取数据;
  • 8.executor通过反射将数据转换成POJO并返回给sqlsession;
  • 9.数据返回给调用者

上节讲到快速入门mybatis的demo三大阶段

// 1.读取mybatis配置文件创SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);inputStream.close();//-------------第二阶段-------------// 2.获取sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();// 3.获取对应mapperTUserMapper mapper = sqlSession.getMapper(TUserMapper.class);//-------------第三阶段-------------// 4.执行查询语句并返回结果TUser user = mapper.selectByPrimaryKey(1);System.out.println(user.toString());

第一阶段:

第一阶段先把配置文件加载到内存,包括数据库信息和mapper.xml。

针对mapper.xml我们定义一个MappedStatement类来存入相应信息.

public class MappedStatement {    //此处忽略getset方法    private String namespace;        private String sourceId;//mapper接口路径+xml里面的每一个id        private String sql;//sql语句        private String resultType;//返回类型    }

再定义一个全局配置信息即Configuration存放所有配置信息:

public class Configuration {    //记录mapper xml文件存放的位置    public static final String MAPPER_CONFIG_LOCATION = "config";    //记录数据库连接信息文件存放位置    public static final String DB_CONFIG_FILE = "db.properties";        private String dbUrl;    private String dbUserName;    private String dbPassword;    private String dbDriver;    //mapper xml解析完以后select节点的信息存放在mappedStatements,key为MappedStatement里面      //的sourceId    protected final Map<String, MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();        //为mapper接口生成动态代理的方法public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    return MapperProxyFactory.getMapperProxy(sqlSession, type);}}

SqlSessionFactory实例化,并加载configuaration对象信息,这样就把所有的配置信息加载到内存里

public class SqlSessionFactory {    //配置对象全局唯一 加载数据库信息和mapper文件信息    private Configuration conf = new Configuration();    public SqlSessionFactory() {        //加载数据库信息         loadDbInfo();         //加载mapper文件信息         loadMappersInfo();    }        private void loadMappersInfo() {        URL resources =null;        resources = SqlSessionFactory.class.getClassLoader().getResource(conf.MAPPER_CONFIG_LOCATION);        File mappers = new File(resources.getFile());        if(mappers.isDirectory()){            File[] listFiles = mappers.listFiles();            for (File file : listFiles) {                loadMapperInfo(file);            }        }    }    private void loadMapperInfo(File file) {        // 创建saxReader对象          SAXReader reader = new SAXReader();          // 通过read方法读取一个文件 转换成Document对象          Document document=null;        try {            document = reader.read(file);        } catch (DocumentException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }          //获取根节点元素对象          Element node = document.getRootElement();        //获取命名空间        String namespace = node.attribute("namespace").getData().toString();        //获取select子节点列表        List<Element> selects = node.elements("select");        for (Element element : selects) {//遍历select节点,将信息记录到MappedStatement对象,并登记到configuration对象中            MappedStatement mappedStatement = new MappedStatement();            String id = element.attribute("id").getData().toString();            String resultType = element.attribute("resultType").getData().toString();            String sql = element.getData().toString();            String sourceId = namespace+"."+id;            mappedStatement.setSourceId(sourceId);            mappedStatement.setResultType(resultType);            mappedStatement.setSql(sql);            mappedStatement.setNamespace(namespace);            conf.getMappedStatements().put(sourceId, mappedStatement);//登记到configuration对象中        }    }    private void loadDbInfo() {        InputStream dbIn = SqlSessionFactory.class.getClassLoader().getResourceAsStream(conf.DB_CONFIG_FILE);         Properties p = new Properties();         try {            p.load(dbIn);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }         conf.setDbDriver(p.get("jdbc.driver").toString());         conf.setDbPassword(p.get("jdbc.password").toString());         conf.setDbUrl(p.get("jdbc.url").toString());         conf.setDbUserName(p.get("jdbc.username").toString());    }    public SqlSession openSession(){        SqlSession sqlSession  = new DefaultSqlSession(conf);        return sqlSession;    }}

第二阶段

第二阶段为获取Sqlsession并且从sqlsession获取mapper动态代理.

Sqlsession

  • mybatis暴露给外部的接口,实现增删改查的能力
  • 1.对外提供数据访问的api
  • 2.对内将请求转发给executor
  • 3.executor基于JDBC访问数据库

    public class DefaultSqlSession implements SqlSession {              //配置对象全局唯一 加载数据库信息和mapper文件信息       private Configuration conf;              //真正提供数据库访问能力的对象       private Executor executor;                 public DefaultSqlSession(Configuration conf) {           super();           this.conf = conf;           executor = new SimpleExecutor(conf);       }          public <T> T selectOne(String statement, Object parameter) {           List<Object> selectList = this.selectList(statement, parameter);           if(selectList==null||selectList.size()==0){               return null;           }           if(selectList.size()==1){               return (T) selectList.get(0);           }else {               throw new RuntimeException("Too Many Result!");           }          }          public <E> List<E> selectList(String statement, Object parameter) {           MappedStatement mappedStatement = conf.getMappedStatement(statement);           try {               return executor.query(mappedStatement, parameter);           } catch (SQLException e) {               // TODO Auto-generated catch block               e.printStackTrace();           }           return null;       }            @Override         //获取当前mapper接口的动态代理         public <T> T getMapper(Class<T> type) {           return conf.<T>getMapper(type, this);         }      }

    Executor是Mybatis核心接口定义了数据库操作的基本方法,Sqlsession都是基于它来实现的

     public interface Executor { <E> List<E> query(MappedStatement ms, Object parameter) throws SQLException; <T> T selectOne(String statement,Object parameter);

    }

    Executor实现类:

public class SimpleExecutor implements Executor {                private Configuration conf;                public SimpleExecutor(Configuration conf) {            this.conf = conf;        }        public <E> List<E> query(MappedStatement ms, Object parameter)                throws SQLException {            //获取mappedStatement对象,里面包含sql语句和目标对象等信息;            MappedStatement mappedStatement = conf.getMappedStatement(ms.getSourceId());            //1.获取Connection对象            Connection conn = getConnect();            //2.实例化StatementHandler对象,准备实例化Statement            StatementHandler statementHandler = new DefaultStatementHandler(mappedStatement);            //3.通过statementHandler和Connection获取PreparedStatement            PreparedStatement prepare = statementHandler.prepare(conn);            //4.实例化ParameterHandler对象,对Statement中sql语句的占位符进行处理            ParameterHandler parameterHandler = new DefaultParameterHandler(parameter);            parameterHandler.setParameters(prepare);            //5.执行查询语句,获取结果集resultSet            ResultSet resultSet = statementHandler.query(prepare);            //6.实例化ResultSetHandler对象,对resultSet中的结果集进行处理,转化成目标对象            ResultSetHandler resultSetHandler = new DefaultResultSetHandler(mappedStatement);            return resultSetHandler.handleResultSets(resultSet);        }            @Override        public <T> T selectOne(String statement, Object parameter) {            MappedStatement mappedStatement =conf.getMappedStatements().get(statement);            return null;        }                private Connection getConnect() {            Connection conn =null;            try {                Class.forName(conf.getDbDriver());                conn = DriverManager.getConnection(conf.getDbUrl(), conf.getDbUserName(), conf.getDbPassword());                } catch (ClassNotFoundException e) {                // TODO Auto-generated catch block                e.printStackTrace();            } catch (SQLException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            return conn;        }            public Configuration getConf() {            return conf;        }            public void setConf(Configuration conf) {            this.conf = conf;        }            }

mapper接口在我们工程里面没有实现类,是通过动态代理来执行方法的.

/** * mapper接口生成动态代理的工程类 *  */public class MapperProxyFactory<T> {    public static <T> T getMapperProxy(SqlSession sqlSession,Class<T> mapperInterface){      MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface);      return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);  }

InvocationHandler实现类:

public class MapperProxy<T> implements InvocationHandler {    private SqlSession sqlSession;        private final Class<T> mapperInterface;        public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {        super();        this.sqlSession = sqlSession;        this.mapperInterface = mapperInterface;    }    private <T> boolean isCollection(Class<T> type) {        return Collection.class.isAssignableFrom(type);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        if (Object.class.equals(method.getDeclaringClass())) {// 如果是Object本身的方法不增强            return method.invoke(this, args);        }        Class<?> returnType = method.getReturnType();// 获取方法的返回参数class对象        Object ret = null;        if (isCollection(returnType)) {// 根据不同的返回参数类型调用不同的sqlsession不同的方法            ret = sqlSession.selectList(mapperInterface.getName()+"."+ method.getName(), args);        } else {            ret = sqlSession.selectOne(mapperInterface.getName()+"."+ method.getName(), args);        }        return ret;    }}

第三阶段

第三阶段执行查询并返回结果.刚刚讲过我们执行数据库操作实际上是executor基于jdbc执行的。

jdbc三大巨头,Connection,PreparedStatement,ResultSet,

结果集Result再通过反射机制映射到对象上面,便做好了数据的映射(关于映射具体内容可查阅资料及源码),到这我们已经完成了一个简易的Mybatis框架了.

通过手写一个简单的Mybatis框架,我们就可以看得懂源码了,学习框架设计的思路并且增强我们Java的内功.