首页 > 编程知识 正文

自动注入注解,mybatis详解

时间:2023-05-03 16:01:27 阅读:37369 作者:217

mybatis他是一个基于jdbc封装的持久层框架。

如果使用mybatis,则必须创建sqlSessionFactory和sqlSession,使用sqlSession创建代理对象,然后代理对象执行目标方法以获得最终执行结果。

因为我们块中的所有mapper都是基于接口的,所以mybatis使用jdk动态代理创建代理对象。

首先,让我们看看官方文档中关于这些名称的说明

SqlSessionFactoryBuilder

可以实例化、使用和销毁此类。 一旦创建了SqlSessionFactory,就不再需要它了。 因此,SqlSessionFactoryBuilder实例的最佳范围是方法范围,即局部方法变量。 虽然可以重用SqlSessionFactoryBuilder来创建多个SqlSessionFactory实例,但为了确保所有XML解析资源都释放到更重要的位置,最好不要保留。

SqlSessionFactory

一旦创建SqlSessionFactory,它就必须在APP应用程序期间一直存在,没有任何理由销毁或重新创建其他实例。 使用SqlSessionFactory的最佳做法是在执行APP应用程序时不要多次创建。 多次重建SqlSessionFactory被视为代码的“坏习惯”。 因此,SqlSessionFactory的最佳范围是应用范围。 有很多方法可以做到,但最简单的是使用单实例模式或静态单实例模式。

SqlSession

每个线程都必须有自己的SqlSession实例。 最佳范围是请求或方法范围,因为SqlSession实例不是线程安全的,不能共享。 绝对不要将对SqlSession实例的引用放在类的静态域中。 另外,也不要放入类的实例变量中。 此外,对servlet框架中任何类型的管理范围(如HttpSession )都不能放置对SqlSession实例的引用。 如果当前使用Web框架,请考虑将SqlSession放置在与HTTP请求类似的范围中。 也就是说,每次收到HTTP请求时打开SqlSession,返回响应后关闭。 这个关机操作很重要。 为了确保每次都可以执行关机操作,必须将此关机操作放在finally块中。

我们可能接触到的是mybatis加载xml文件并创建sqlSessionFactory。 xml文件的最终分析结果是创建一个Configuration对象,并将各种配置信息封装在此对象中。

这里直接创建Configuration对象,直接在他里面放置各种信息就可以了。

SqlSessionFactory的创建

publicstaticsqlsessionfactorygetfactory ()//数据源数据源数据源=获取数据源) ); //事务处理工厂transactionfactorytransactionfactory=newjdbctransactionfactory (; //初始化环境对象环境=新环境(开发)、事务处理工厂、数据源); //初始化设置--- -如果需要解析XML文件,则在上次解析XML文件后生成的此对象configuration=new configuration (environment ); //收集我们的映射器文件配置. add mapper (mapper.class ); //sqlsessionfactoryreturnnewsqlsessionfactorybuilder ().build (配置); } mapper文件的收集

在初始化sqlSessionFactory时,mybatis会收集所有与mysql相关的dao文件,并执行缓存处理、dao和xml文件绑定等操作。

让我们简要分析一下这个块是如何处理的

在configuration 757 linepublictvoidaddmapper (class ttype )//mapperRegistry中保存了映射器类型//mapperRegistry是在构建配置时创建的

//见名只其意 mapper的注册器 this.mapperRegistry.addMapper(type); }

mapper的注册

MapperRegistry 45 linepublic <T> void addMapper(Class<T> type) {//判断传入的mapper是不是接口类型 不是接口不做处理 if (type.isInterface()) { //如果这个mapper已经注册过,直接抛异常 if (this.hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); }//标记处理还未完成 boolean loadCompleted = false; try { //将mapper包装成MapperProxyFactory,然后放在knownMappers这个map中 this.knownMappers.put(type, new MapperProxyFactory(type)); //初始化了一个解析器 this.config就是我们的Configuration对象 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type); //1、解析~~~ parser.parse(); //标记处理成功 loadCompleted = true; } finally { //如果解析异常,将mapper移出map if (!loadCompleted) { this.knownMappers.remove(type); } } } } 1、解析

在将mapper包装之后,初始化了一个解析器,然后去解析处理缓存、mapper文件与xml文件的绑定

public void parse() { String resource = this.type.toString(); //判断这个mepper资源还没加载的话,再来进入if去处理 if (!this.configuration.isResourceLoaded(resource)) { //2、xml文件的加载 this.loadXmlResource(); //将mapper加入set集合,标记这个mapper资源已经处理过 this.configuration.addLoadedResource(resource); //设置名称空间 this.type.getName=package.ClassName(也就是入参为:mybatis.Mapper) //加载xml文件的时候,如果有xml文件,那么就会设置这个 没有xml文件,则这一步设置名称空间 this.assistant.setCurrentNamespace(this.type.getName()); //3、缓存的处理 this.parseCache(); this.parseCacheRef(); //拿到所有方法 Method[] var2 = this.type.getMethods(); int var3 = var2.length; for(int var4 = 0; var4 < var3; ++var4) { Method method = var2[var4]; //method有对应的statement, 因为接口的所有方法都会被解析绑定成一个statement if (this.canHaveStatement(method)) { if (this.getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent() && method.getAnnotation(ResultMap.class) == null) { this.parseResultMap(method); } try { this.parseStatement(method); } catch (IncompleteElementException var7) { this.configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } } this.parsePendingMethods(); } 2、xml文件的加载 private void loadXmlResource() {//已经加载过,则不处理 if (!this.configuration.isResourceLoaded("namespace:" + this.type.getName())) { //获取xml文件路径,默认的就是classpath/packageName/MapplerName.xml //也就是.mybatis/Mapper String xmlResource = this.type.getName().replace('.', '/') + ".xml"; //获取资源流 InputStream inputStream = this.type.getResourceAsStream("/" + xmlResource); if (inputStream == null) { try { inputStream = Resources.getResourceAsStream(this.type.getClassLoader(), xmlResource); } catch (IOException var4) { } } if (inputStream != null) { //初始化一个Xml解析器 XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, this.assistant.getConfiguration(), xmlResource, this.configuration.getSqlFragments(), this.type.getName()); //解析xml xmlParser.parse(); } } }

解析Mapper.xml文件

XMLMapperBuilder 77 linepublic void parse() {//再次判断是否已经加载过 这一块是入参 mybatis/Mapper.xml if (!this.configuration.isResourceLoaded(this.resource)) { //配置解析之后的xml this.configurationElement(this.parser.evalNode("/mapper")); //xml文件加入set,标记这个xml文件已经处理过了 this.configuration.addLoadedResource(this.resource); //绑定nameSpace 具体看后面 this.bindMapperForNamespace(); }//下面这三个 是干嘛的? 后面分析 暂时还不清楚 this.parsePendingResultMaps(); this.parsePendingCacheRefs(); this.parsePendingStatements(); }//配置解析之后的xmlprivate void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); if (namespace != null && !namespace.isEmpty()) { this.builderAssistant.setCurrentNamespace(namespace); //处理标签 ~~~~~~ this.cacheRefElement(context.evalNode("cache-ref")); this.cacheElement(context.evalNode("cache")); this.parameterMapElement(context.evalNodes("/mapper/parameterMap")); this.resultMapElements(context.evalNodes("/mapper/resultMap")); this.sqlElement(context.evalNodes("/mapper/sql"));//处理四大标签 构建statement 针对每一条sql都会构建好一个statement//直接看流程图吧 this.buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } else { throw new BuilderException("Mapper's namespace cannot be empty"); } } catch (Exception var3) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3); } } 3、缓存的加载~~~ mapper接口中的方法的加载~~~

解析缓存无非就是构建一个Cache对象,将其与nameSpace绑定在Configuration的map中而已

mapper中方法的加载也就是解析这个方法,构建一个MappedStatement,然后与nameSpace+id绑定在Configuration中。

这里需要注意的是他是放在map中的,key为nameSpace+id 也就是nameSpace+方法名,那么如果xml中有这个方法的sql,但是mapper中也标注了@Select等注解,那么方法上的会替换掉xml中的。因为都是直接调用map.put,而方法是后面解析再去设置的。

这里看一下大概的流程图。 细节问题都在每一步中去处理的。


至于每一步细节处理都得去打断点 去推敲代码 这里就只看流程就行了,有问题,再去找具体的出问题的代码分析就好

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。