MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
测试源码地址:https://gitee.com/rjzhu/opencode/tree/master/mybatis-cache-demo-master
1.MyBatis配置文件,MybatisConfig.xml,Mapper.xml 2.SqlSessionFactoryBuilder,加载配置文件,构建Configuration对象,根据Configuration对象创建SqlSessionFactory 3.SqlSessionFactory,调用openSession方法,创建SqlSession对象 4.SqlSession,根据事物Transaction与Executor类型创建Executor 5.Executor,通过Executor的doQuery创建StatementHandler对象 5.Executor,通过Executor的doQuery创建StatementHandler对象 7.ParameterHandler,对于处理后的statement参数绑定 8.执行sql,通过statement.execute(sql)执行sql 9,ResultHandler,通过ResultHandler结果集封装
/** * mybatis执行流程:传统模式 */ @Test public void myBatisRaditionModel() throws Exception { // 1.读取Mybatis配置文件 InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); // 2.通过sqlSessionFactoryBuilder 构建 SqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); // 3.通过sqlSessionFactory开启SqlSession SqlSession sqlSession = factory.openSession(true); // 4.使用代理对象执行方法,--与接口模式的区别 List<StudentEntity> list = sqlSession.selectList("mapper.StudentMapper.getStudentById", 1); System.out.println("查询成功:" + list); // 6.释放资源 sqlSession.close(); in.close(); }
将配置文件读取流的形式
InputStream in = Resources.getResourceAsStream(“mybatis-config.xml”);
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //构建XMLConfigBuilder对象,解析xml XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //创建SqlSessionFactory return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } //具体解析字段 private void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } //解析构建SqlSessionFactory public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); //创建事物 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //创建执行器 final Executor executor = configuration.newExecutor(tx, execType); //创建SqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
// 4.使用代理对象执行方法 List<StudentEntity> list = sqlSession.selectList("mapper.StudentMapper.getStudentById", 1); @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //创建MappedStatement MappedStatement ms = configuration.getMappedStatement(statement); //执行查询 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //创建BoundSql BoundSql boundSql = ms.getBoundSql(parameter); //创建缓存 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); //执行查询 return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; //判断是否有缓存,没有则查询数据库 if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { // 查询数据库 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { //清除缓存 localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; } //执行查询,封装结果集 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { String sql = boundSql.getSql(); //具体执行查询sql statement.execute(sql); return resultSetHandler.<E>handleResultSets(statement); }
/** * mybatis执行流程:接口模式 */ @Test public void myBatisInterfaceModel() throws Exception { // 1.读取Mybatis配置文件 InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); // 2.通过sqlSessionFactoryBuilder 构建 SqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); // 3.通过sqlSessionFactory开启SqlSession SqlSession sqlSession = factory.openSession(true); // 4.通过SqlSession创建Mapper接口的代理对象,--与接口模式的区别 StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); // 5.使用代理对象执行方法 StudentEntity studentEntity = studentMapper.getStudentById(1); System.out.println(studentEntity); // 6.释放资源 sqlSession.close(); in.close(); }
同上
将配置文件读取流的形式
InputStream in = Resources.getResourceAsStream(“mybatis-config.xml”);
同上
同上
// 4.通过SqlSession创建Mapper接口的代理对象 StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); //这里返回的是mapper接口的代理对象 public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
//使用代理对象执行方法 StudentEntity studentEntity = studentMapper.getStudentById(1); //通过代理对象调用执行方法封装结果集,通过MapperProxy执行invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else { // 调用反射方法 return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } @Override public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { // 调用执行器 return mapperMethod.execute(sqlSession, args); } /** * 通过标签判断执行类型,封装结果集 */ public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
MyBatis中文网:https://mybatis.net.cn/index.html
MyBatis英文网:https://mybatis.org/mybatis-3/index.html
MyBatis工作原理及流程详细讲解:https://baijiahao.baidu.com/s?id=1666562082269776543&wfr=spider&for=pc
mybatis框架的架构(图解):https://www.cnblogs.com/wyhluckdog/p/10147921.html
MyBatis执行流程的各阶段介绍:https://www.cnblogs.com/-beyond/p/13232624.html