https://mybatis.net.cn/index.html
mybatis是一个用Java编写的持久层框架,它使用ORM实现了结果集的封装。
ORM是Object Relational Mapping 对象关系映射。简单来说,就是把数据库表和实体类及实体类的属性对应起来,让开发者操作实体类就实现操作数据库表,它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等复杂过程。
ORM:Object-Relation-Mapping,也就是对象关系映射,是一种程序设计思想,mybatis就是ORM的一种实现方式,简单来说就是将数据库中查询出的数据映射到对应的实体中。
CREATE TABLE `mayikt_users` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb3;1.引入mybatis相关依赖 已经完成了
2.mybatis-config.xml(该配置文件名称是可以改) 存放就是我们数据库相关连接信息
3.定义mapper ----编写我们mybatis 相关 sql语句 每个表 对应一个mapper
4.定义java对象--需要注意下 类中的 成员属性与数据库表中字段 映射 默认 类中的 成员属性数据库表中字段名称对应的。
5.使用 mybatis api开始执行该 sql语句即可 得到结果
org.mybatis mybatis 3.4.5 mysql mysql-connector-java 8.0.18 存放数据库连接信息mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?> package com.mayikt.test; import com.mayikt.entity.UserEntity; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * @author songchuanfu * @qq 1802284273 */ public class Test01 { public static void main(String[] args) throws IOException { // 1.读取加载mybatis-config.xml String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 2.获取到获取到 SqlSession sqlSession = sqlSessionFactory.openSession(); // 3.根据 mapper id=getByUsers 执行该s ql 语句 通过 sql语句得到我们的对象 orm List userEntitys = sqlSession.selectList("getByUsers", UserEntity.class); System.out.println(userEntitys); sqlSession.close(); } } 1.mapper接口方式开发整合就必须是对应的mapper接口的全限定类名
2.接口中的方法与映射文件中的SQL语句的ID
3.需要在mybatis-config.xml 新增 加载该userMaaper
4.定义mapper 接口 需要考虑方法的名称与userMapper.xml的 sql id名称保持一致。
package com.mayikt.mapper; import com.mayikt.entity.UserEntity; import java.util.List; /** * @author songchuanfu * @qq 1802284273 */ public interface UserMapper { List getByUsers(); } <?xml version="1.0" encoding="UTF-8" ?> 5.相关代码
package com.mayikt.test; import com.mayikt.entity.UserEntity; import com.mayikt.mapper.UserMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * @author songchuanfu * @qq 1802284273 */ public class Test01 { public static void main(String[] args) throws IOException { // 1.读取加载mybatis-config.xml String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 2.获取到获取到 SqlSession sqlSession = sqlSessionFactory.openSession(); // 3.根据 mapper id=getByUsers 执行该s ql 语句 通过 sql语句得到我们的对象 orm // List userEntitys = sqlSession.selectList("getByUsers", UserEntity.class); UserMapper mapper = sqlSession.getMapper(UserMapper.class); System.out.println(mapper.getByUsers()); // System.out.println(userEntitys); sqlSession.close(); } } 1、Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;(解析配置文件)
2、工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
3、单例模式,例如ErrorContext和LogFactory;
4、代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;调动我们的Mapper接口
5、组合模式,例如SqlNode和各个子类ChooseSqlNode等;
6、模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;
7、适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
8、装饰者模式,例如Cache包中的cache.decorators子包中等各个装饰者的实现;(多级缓存)
9、迭代器模式,例如迭代器模式PropertyTokenizer;
底层基于jdk动态代理实现
在mybatis mapper 是一个接口为何可以调用的呢?底层其实就是基于JDK动态代理实现。
相关代码:
package com.mayikt.mybatis.ext; import org.springframework.stereotype.Indexed; import java.lang.annotation.*; /** * @author songchuanfu * @qq 1802284273 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Indexed public @interface MayiktInsert { String value(); } package com.mayikt.mybatis; import com.mayikt.mybatis.ext.MayiktInsert; import com.mayikt.utils.MayiktJdbcUtils; import org.apache.commons.lang.StringUtils; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.PreparedStatement; /** * @author songchuanfu * @qq 1802284273 */ public class MybatisJdkInvocationHandler implements InvocationHandler { private Class mapperClass; public MybatisJdkInvocationHandler(Class mapperClass) { this.mapperClass = mapperClass; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 使用java反射技术获取该方法上的注解 MayiktInsert declaredAnnotation = method.getDeclaredAnnotation(MayiktInsert.class); String insertSql = declaredAnnotation.value(); if (StringUtils.isEmpty(insertSql)) { return null; } // 执行该sql语句 Connection connection = MayiktJdbcUtils.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(insertSql); int result = preparedStatement.executeUpdate(); return result; } public T getProxy() { return (T) Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, this); } } package com.mayikt.mybatis; import com.mayikt.mybatis.ext.MayiktInsert; /** * @author songchuanfu * @qq 1802284273 */ public interface UserMapper { @MayiktInsert("INSERT INTO `mayikt`.`mayikt_users` (`id`, `name`, `age`) VALUES (null, 'wangmazi', NULL);") int addUser(); } package com.mayikt.mybatis; /** * @author songchuanfu * @qq 1802284273 */ public class Test01 { public static void main(String[] args) { System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class); int result = userMapper.addUser(); System.out.println(result); } } package com.mayikt.mybatis; /** * @author songchuanfu * @qq 1802284273 */ public class MapperProxy { public static UserMapper getUserMapper(Class mapperClass) { return new MybatisInvocationHandler(mapperClass).getProxy(); } public static void main(String[] args) { // 将jdk动态生成好的 class 存放本地 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class); int i = userMapper.addUser(); System.out.println(i); } } mybatis 底层 mapper 接口会执行到MapperProxy
会执行到:MapperProxy
jdk动态代理 底层 生成 mapper 代理类
public final class $Proxy4 extends Proxy implements UserMapper {
}
当我们在去调用$Proxy4.方法的时候 会执行 mybatis jdk 动态代理回调类
public class MapperProxy implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class mapperInterface; private final Map methodCache; public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @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 if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } // 执行到我们 mapperMethod.execute(sqlSession, args); final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } return mapperMethod.execute(sqlSession, args); 判断sql语句类型 select、insert、delete、update 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); } 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; } name:Mapper类完整路径地址+方法名称组合 通过源码分析可以得出 底层 result = sqlSession.selectList(command.getName(), param); 1.初始化阶段:读取 解析XML 配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作,底层采用建造模式
2.代理封装阶段:封装 mybatis 的编程模型,使用 mapper 接口开发的初始化工作,底层爱用jdk动态代理模式
3.数据访问阶段:通过 SqlSession 完成 SQL 的解析,参数的映射、SQL 的执行、结果的解析过程;
建造者模式是设计模式的一种,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
以下情况建议使用建造者模式:
1、某个对象有复杂的内部结构,通常包含多个成员属性,而且属性是可选的。
2、相同的方法,不同的执行顺序,产生不同的对象或不同的运行结果。
3、当创建一个对象需要很多步骤时,适合使用建造者模式。
1.mybatis 解析xml
2.StringBuilder
3.OkHttpClient
Request request = new Request.Builder().url("http://www.mayikt.com").build();.build();)
例如我们现在封装HttpClient框架,里面有很多配置属性
例如 接口url、请求方法、超时时间、body等
方式1: new MayiktHttpClient("http://www.mayikt.com", "POST"); new MayiktHttpClient("http://www.mayikt.com", "POST", 10000l); 方式2: MayiktHttpClient post = new MayiktBuilder().setUrl("http://www.mayikt.com") .setMethod("POST").setBody("mayikt").setTimeout(1000l).build(); System.out.println(post); package com.mayikt.test; /** * @author songchuanfu * @qq 1802284273 */ public class MayiktHttpClient { /** * 请求url */ private String url; /** * 请求方法 */ private String method; /** * 超时时间 */ private Long timeout; /** * 内容 */ private String body; public MayiktHttpClient(String url, String method) { this.url = url; this.method = method; } public MayiktHttpClient() { } //retry public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public Long getTimeout() { return timeout; } public void setTimeout(Long timeout) { this.timeout = timeout; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } }我们使用
new MayiktHttpClient("http://www.mayikt.com", "POST")如果需要传递三个参数 看起来逼格不是很高
new MayiktHttpClient("http://www.mayikt.com", "POST",10000l); package com.mayikt.test; /** * @author songchuanfu * @qq 1802284273 */ public class MayiktBuilder { /** * 请求url */ private String url; /** * 请求方法 */ private String method; /** * 超时时间 */ private Long timeout; /** * 内容 */ private String body; public MayiktHttpClient build() { return new MayiktHttpClient(this); } public MayiktBuilder setUrl(String url) { this.url = url; return this; } public MayiktBuilder setMethod(String method) { this.method = method; return this; } public MayiktBuilder setTimeout(Long timeout) { this.timeout = timeout; return this; } public MayiktBuilder setBody(String body) { this.body = body; return this; } public String getMethod() { return method; } public String getUrl() { return url; } public Long getTimeout() { return timeout; } public String getBody() { return body; } } package com.mayikt.test; /** * @author 余胜军 * @ClassName MayiktHttpClient * @qq 644064779 * @addres www.mayikt.com * 微信:yushengjun644 */ public class MayiktHttpClient { /** * 请求url */ private String url; /** * 请求方法 */ private String method; /** * 超时时间 */ private Long timeout; /** * 内容 */ private String body; public MayiktHttpClient(String url, String method) { this.url = url; this.method = method; } public MayiktHttpClient(String url, String method, Long timeout) { this.url = url; this.method = method; this.timeout = timeout; } public MayiktHttpClient(MayiktBuilder mayiktBuilder) { this.url = mayiktBuilder.getUrl(); this.body = mayiktBuilder.getBody(); this.method = mayiktBuilder.getMethod(); this.timeout = mayiktBuilder.getTimeout(); } public MayiktHttpClient() { } //retry public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public Long getTimeout() { return timeout; } public void setTimeout(Long timeout) { this.timeout = timeout; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } @Override public String toString() { return "MayiktHttpClient{" + "url='" + url + '\'' + ", method='" + method + '\'' + ", timeout=" + timeout + ", body='" + body + '\'' + '}'; } }用法:
new MayiktHttpClient("http://www.mayikt.com", "POST"); new MayiktHttpClient("http://www.mayikt.com", "POST", 10000l); MayiktHttpClient post = new MayiktBuilder().setUrl("http://www.mayikt.com") .setMethod("POST").setBody("mayikt").setTimeout(1000l).build(); System.out.println(post);mybatis源码解读01.rar
configuration 对象的关键属性解析如下:
MapperRegistry:mapper 接口动态代理工厂类的注册中心。在 MyBatis 中,通过mapperProxy 实现 InvocationHandler 接口,MapperProxyFactory 用于生成动态代理的实例对象;
ResultMap:用于解析 mapper.xml 文件中的 resultMap 节点,使用 ResultMapping 来封装id,result 等子元素;
MappedStatement:用于存储 mapper.xml 文件中的 select、insert、update 和 delete 节点,同时还包含了这些节点的很多重要属性;
SqlSource:用于创建 BoundSql,mapper.xml 文件中的 sql 语句会被解析成 BoundSql 对象,经过解析 BoundSql 包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);1.BaseBuilder:所有解析器的父类,包含配置文件实例,为解析文件提供的一些通用的方法;
2.XMLConfigBuilder: 主要负责解析 mybatis-config.xml;
3.XMLMapperBuilder: 主要负责解析映射配置 Mapper.xml 文件;
4.XMLStatementBuilder: 主要负责解析映射配置文件中的 SQL 节点;
1.mybatis 底层采用 dom解析配置xml配置
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //读取xml相关配置 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); //标记 如果parsed该是为false没有解析 如果是为true的情况下 //则是已经解析过 parsed=true this.parsed = false; this.environment = environment; this.parser = parser; } public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); //读取xml相关配置(dom) this.document = createDocument(new InputSource(inputStream)); } private Document createDocument(InputSource inputSource) { // important: this must only be called AFTER common constructor try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(new ErrorHandler() { @Override public void error(SAXParseException exception) throws SAXException { throw exception; } @Override public void fatalError(SAXParseException exception) throws SAXException { throw exception; } @Override public void warning(SAXParseException exception) throws SAXException { } }); // 解析xml配置 return builder.parse(inputSource); } catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e); } }断点debug 查看document 解析xml配置成功
在new new XMLConfigBuilder(inputStream, environment, properties); 中 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; //已经解析是为false this.environment = environment; this.parser = parser; } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //读取xml成功之后开始解析xml return build(parser.parse()); //解析xml成功之后会得到一个 Configuration public Configuration parse() { //因为在构造函数设置了parsed 为fasle,每个XMLConfigBuilder只能使用一次。 if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } // 标识为:true parsed = true; //开始从根节点解析 parseConfiguration(parser.evalNode("/configuration")); return configuration; } !-- 数据库相关的配置--> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } } 解析我们的resource相关配置
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(inputStream)); } private Document createDocument(InputSource inputSource) { // important: this must only be called AFTER common constructor try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(new ErrorHandler() { @Override public void error(SAXParseException exception) throws SAXException { throw exception; } @Override public void fatalError(SAXParseException exception) throws SAXException { throw exception; } @Override public void warning(SAXParseException exception) throws SAXException { } }); return builder.parse(inputSource); } catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e); } }解析完成配置文件之后:将我们的Configuration交给DefaultSqlSessionFactory
@Override public SqlSession openSession() { // 默认传递一个执行 ----简单的执行器 return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 获取环境配置 final Environment environment = configuration.getEnvironment(); // 获取TransactionFactory final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 获取Transaction事务 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 根据事务 execType 获取 Executor 执行器 final Executor executor = configuration.newExecutor(tx, execType); // new DefaultSqlSession 最后封装我们的configuration、executor、autoCommit // 封装到DefaultSqlSession返回。 return new 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(); } } //获取 Executor 执行器 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } // 如果cacheEnabled是为true 返回CachingExecutor 判断是否开启了二级缓存 // 走我们的CachingExecutor 执行器 if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }SimpleExecutor:默认的Executor,每个SQL执行时候都会创建新的Statement
ReuseExecutor:相同的SQL会重复使用Statement
BatchExecutor:用于批处理的Executor
CachingExecutor:可缓存数据的Executor,用代理模式包装了其他类型的Executor
// 如果cacheEnabled是为true 返回CachingExecutor 则开启mybatis 二级缓存
<?xml version="1.0" encoding="UTF-8" ?> *//3. 创建SqlSessionFactoryBuilder.build(inputStream) 建造者 键盘快捷键ctrl+点击方法 *SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); *//4.获取到session //传递true 或者false 是否自动提交 true *SqlSession sqlSession = sqlSessionFactory.openSession(false);
1.解析mybatis-configxml 得到Configuration 在将该对象赋值给我们SqlSessionFactory
new SqlSessionFactoryBuilder().build(inputStream);
2.使用sqlSessionFactory 得到SqlSession 对象时 判断当前使用的执行器类型
SimpleExecutor:默认的Executor,每个SQL执行时候都会创建新的Statement
ReuseExecutor:相同的SQL会重复使用Statement
BatchExecutor:用于批处理的Executor
CachingExecutor:可缓存数据的Executor,用代理模式包装了其他类型的Executor
// 如果cacheEnabled是为true 返回CachingExecutor 则开启mybatis 二级缓存
默认采用SimpleExecutor 执行器 如果开启了二级缓存则使用CachingExecutor
SqlSession sqlSession = sqlSessionFactory.openSession(false);
最后返回了我们的默认的SqlSession
| 留言与评论(共有 0 条评论) “” |