网站怎么添加友情链接,承德网站建设方案,备案 添加网站,国外网站模板下载Mybatis的基础使用以及与Spring的相关集成在官方文档都写的非常详细#xff0c;但无论我们采用xml还是注解方式在使用的过程中经常会出现各种奇怪的问题#xff0c;需要花费大量的时间解决。 抽空了解一下Mybatis的相关源码还是很有必要。 先来看一个简单的Demo#xff1a; … Mybatis的基础使用以及与Spring的相关集成在官方文档都写的非常详细但无论我们采用xml还是注解方式在使用的过程中经常会出现各种奇怪的问题需要花费大量的时间解决。 抽空了解一下Mybatis的相关源码还是很有必要。 先来看一个简单的Demo Test
public void test() throws IOException {String resource mybatis-config.xml;InputStream inputStream Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);SqlSession session sqlSessionFactory.openSession();MethodInfo info (MethodInfo) session.selectOne(com.ycdhz.mybatis.dao.MethodInfoMapper.selectById, 1);System.out.println(info.toString());
}这个是官网中入门的一段代码我根据自己的情况做了一些参数上的改动。这段代码很容易理解解析一个xml文件通过SqlSessionFactoryBuilder构建一个SqlSessionFactory实例。 拿到了SqlSessionFactory我们就可以获取SqlSession。SqlSession 包含了面向数据库执行 SQL 命令所需的所有方法所以我们可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。 代码很简单的展示了Mybatis到底是什么有什么作用。 Mybatis主线流程解析Configuration返回SqlSessionFactory拿到SqlSession对执行器进行初始化 SimpleExecutor操作数据库 我们先来看一下mybatis-config.xml在这个xml中包含了Mybatis的核心设置有获取数据库连接实例的数据源DataSource和决定事务作用域和控制方式的事务管理器TransactionManager等等。 具体的配置信息可以参考官方文档。需要注意的是Xml的属性配置有一定的顺序要求具体的可以查看http://mybatis.org/dtd/mybatis-3-config.dtd。 !ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?) ?xml version1.0 encodingUTF-8 ?
!DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtd
configurationenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.jdbc.Driver/property nameurl valuejdbc:mysql://localhost:3306/mytest/property nameusername valueroot/property namepassword valueroot//dataSource/environment/environmentsmappersmapper resourcemybatis/MethodInfoMapper.xml/!--mapper classcom.ycdhz.mybatis.dao.MethodInfoMapper /--!--package namecom.ycdhz.mybatis.dao /--/mappers
/configurationtest()前两行主要是通过流来读取配置文件我们直接从new SqlSessionFactoryBuilder().build(inputStream)这段代码开始 public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// SqlSessionFactoryBuilde拿到输入流后构建了一个XmlConfigBuilder的实例。通过parse()进行解析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) {//XmlConfigBuilder.parse()解析完后将数据传给DefaultSqlSessionFactoryreturn new DefaultSqlSessionFactory(config);}
}public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());ErrorContext.instance().resource(SQL Mapper Configuration);this.parsed false;}public Configuration parse() {if (parsed) {throw new BuilderException(Each XMLConfigBuilder can only be used once.);}parsed true; //这个方法主要就是解析xml文件了parseConfiguration(parser.evalNode(/configuration));return configuration;}private void parseConfiguration(XNode root) {try {//issue #117 read properties firstpropertiesElement(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 #631environmentsElement(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);}}
}mybatis-config.xml 文件中的mapper属性支持四种配置方式但是只有packageclass这两种发式支持通过注解来配置和映射原生信息原因在于configuration.addMappers()这个方法。 private void mapperElement(XNode parent) throws Exception {if (parent ! null) {for (XNode child : parent.getChildren()) {if (package.equals(child.getName())) {String mapperPackage child.getStringAttribute(name);configuration.addMappers(mapperPackage);} else {String resource child.getStringAttribute(resource);String url child.getStringAttribute(url);String mapperClass child.getStringAttribute(class);if (resource ! null url null mapperClass null) {ErrorContext.instance().resource(resource);InputStream inputStream Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());mapperParser.parse();} else if (resource null url ! null mapperClass null) {ErrorContext.instance().resource(url);InputStream inputStream Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());mapperParser.parse();} else if (resource null url null mapperClass ! null) {Class? 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.);}}}}}addMappers()调用了MapperAnnotationBuilder.parse()这样一段代码。我们发现当resource不为空的时候代码首先会调用loadXmlResource()去Resource文件夹下查找(com/ycdhz/mybatis/dao/MethodInfoMapper.xml) 如果发现当前文件就加载。但实际这个时候type信息来自注解MethodInfoMapper.xml在容器中被加载两次。所以Configruation下的静态类StrictMap.put()时会抛出一个 Mapped Statements collection already contains value for com.ycdhz.mybatis.dao.MethodInfoMapper.selectById 的异常 public class MapperAnnotationBuilder {public void parse() {String resource type.toString();if (!configuration.isResourceLoaded(resource)) {loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods type.getMethods();for (Method method : methods) {try {// issue #237if (!method.isBridge()) {parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}private void loadXmlResource() {// Spring may not know the real resource name so we check a flag// to prevent loading again a resource twice// this flag is set at XMLMapperBuilder#bindMapperForNamespaceif (!configuration.isResourceLoaded(namespace: type.getName())) {String xmlResource type.getName().replace(., /) .xml;InputStream inputStream null;try {inputStream Resources.getResourceAsStream(type.getClassLoader(), xmlResource);} catch (IOException e) {// ignore, resource is not required}if (inputStream ! null) {XMLMapperBuilder xmlParser new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());xmlParser.parse();}}}
}这个时候我们已经完成了xml文件的解析过程拿到了DefaultSqlSessionFactory。下面我们再来看一下sqlSessionFactory.openSession()的过程 public class DefaultSqlSessionFactory implements SqlSessionFactory{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);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();}}
}public class Configuration {protected boolean cacheEnabled true;protected ExecutorType defaultExecutorType ExecutorType.SIMPLE;protected final InterceptorChain interceptorChain new InterceptorChain();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);}if (cacheEnabled) {executor new CachingExecutor(executor);}executor (Executor) interceptorChain.pluginAll(executor);return executor;}
}openSession()方法会调用DefaultSqlSessionFactory.openSessionFromDataSource()方法在这个方法中会开启事务、创建了执行器Executor MyBatis的事务管理分为两种形式配置mybatis-config.xml文件的transactionManager属性 1使用JDBC的事务管理机制即利用java.sql.Connection对象完成对事务的提交commit()、回滚rollback()、关闭close()等 2使用MANAGED的事务管理机制这种机制MyBatis自身不会去实现事务管理而是让程序的容器如JBOSSWeblogic来实现对事务的管理 执行器ExecutorType分为三类默认使用的是ExecutorType.SIMPLE 1ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。 2ExecutorType.REUSE: 这个执行器类型会复用预处理语句。 3ExecutorType.BATCH: 这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是 必须的,来保证一个简单并易于理解的行为。 因为Mybatis的一级缓存是默认开启的查看newExecutor()不难发现最后通过CachingExecutor对SimpleExecutor进行了装饰详细代码可以查看https://www.cnblogs.com/jiangyaxiong1990/p/9236764.html Mybatis缓存设计成两级结构分为一级缓存、二级缓存参考 https://blog.csdn.net/luanlouis/article/details/41280959 1一级缓存是Session会话级别的缓存位于表示一次数据库会话的SqlSession对象之中又被称之为本地缓存。默认情况下自动开启用户没有定制它的权利不过这也不是绝对的可以通过开发插件对它进行修改 实际上MyBatis的一级缓存是使用PerpetualCache来维护的PerpetualCache实现原理其实很简单其内部就是通过一个简单的HashMapk,v 来实现的没有其他的任何限制。 2二级缓存是Application应用级别的缓存它的是生命周期很长跟Application的声明周期一样也就是说它的作用范围是整个Application应用。 MyBatis的二级缓存设计得比较灵活你可以使用MyBatis自己定义的二级缓存实现你也可以通过实现org.apache.ibatis.cache.Cache接口自定义缓存也可以使用第三方内存缓存库如Redis等 转载于:https://www.cnblogs.com/jiangyaxiong1990/p/10306981.html