浅跟mybatis
写在开篇:建议自己建个简单的项目,使用idea跟跟,实践下,我的这篇文章纯属自己记录
------------------------------------------------------------------------------------------------------------------------
目前市面上主流的实现mvc的框架就是SSM,mybatis就是那个M,他是实现ORM的轻量级连接数据库的框架,与spring结合使用。他底层操作数据库的代码也是调用的java原生的jdbc。
在使用mybatis时,我们只需要配置mybaits的配置信息在spring配置文件中,应用启动时会自动加载配置,为我们自动装配使用mybatis所需要的bean。
使用mybatis我们需要实现配置的信息有sqlSessionFactory和工厂类中需要的参数,dataSource、ConfigLocation和mapperLocations
其中dataSource中配置了数据库连接信息和数据库连接池信息,可以使用原生的mybatis数据源配置,也支持使用外部数据源,我们公司使用的是阿里的druidDataSource。
应用启动加载mybatis配置的时候会为我们初始化mybatis运行时的参数,在创建dataSource bean的时候会测试连接数据库,,执行一个测试sql,以保证数据库可以正常访问。
因为sqlSession工厂bean实现了spring的initializingBean接口,所以在初始化的时候,会执行sqlSessionFactoryBean的afterPropertiesSet方法初始化参数,在这时就会扫描配置,当然也包括mappe.xml文件,为没有mapper.xml文件都生成一个mapperProxy代理类对象,然后作为mapper接口的初始化bean,为每一个mapper.xml中的方法都创建一个MapperMethod方法对象,同时将每个方法中的标签内容转换成对应sqlNode的对象。
InitializingBean,实现这个接口后,spring容器在创建了对象后会去执行这个对象实现了的afterPropertiesSet()方法,初试化被创建的bean。
--------------------------------------------题外话---------------------------------------------------
所以,如果我们有类似的需求,也可以参照实现 引用别人整理的: 1)设置属性值; 2)调用Bean中的BeanNameAware.setBeanName()方法,如果该Bean实现了BeanNameAware接口; 3)调用Bean中的BeanFactoryAware.setBeanFactory()方法,如果该Bean实现了BeanFactoryAware接口; 4)调用BeanPostProcessors.postProcessBeforeInitialization()方法;@PostConstruct注解后的方法就是在这里被执行的 5)调用Bean中的afterPropertiesSet方法,如果该Bean实现了InitializingBean接口; 6)调用Bean中的init-method,通常是在配置bean的时候指定了init-method,例如:<bean class="beanClass" init-method="init"></bean> 7)调用BeanPostProcessors.postProcessAfterInitialization()方法; 8)如果该Bean是单例的,则当容器销毁并且该Bean实现了DisposableBean接口的时候,调用destory方法;如果该Bean是prototype,则将准备好的Bean提交给调用者,后续不再管理该Bean的生命周期。 原文链接:https://blog.csdn.net/t194978/article/details/81515986
------------------------------------------------------------------------------------------------------------------------
public void afterPropertiesSet() throws Exception { Assert.notNull(this.dataSource, "Property 'dataSource' is required"); Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); this.sqlSessionFactory = this.buildSqlSessionFactory(); } protected SqlSessionFactory buildSqlSessionFactory() throws IOException { XMLConfigBuilder xmlConfigBuilder = null; Configuration configuration; if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration(); configuration.setVariables(this.configurationProperties); } String[] typeHandlersPackageArray; String[] arr$; int len$; int i$; String packageToScan; if (StringUtils.hasLength(this.typeAliasesPackage)) { typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; \t\n"); arr$ = typeHandlersPackageArray; len$ = typeHandlersPackageArray.length; for(i$ = 0; i$ < len$; ++i$) { packageToScan = arr$[i$]; configuration.getTypeAliasRegistry().registerAliases(packageToScan); if (this.logger.isDebugEnabled()) { this.logger.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } int len$; if (!ObjectUtils.isEmpty(this.typeAliases)) { Class[] arr$ = this.typeAliases; len$ = arr$.length; for(len$ = 0; len$ < len$; ++len$) { Class<?> typeAlias = arr$[len$]; configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (this.logger.isDebugEnabled()) { this.logger.debug("Registered type alias: '" + typeAlias + "'"); } } } if (!ObjectUtils.isEmpty(this.plugins)) { Interceptor[] arr$ = this.plugins; len$ = arr$.length; for(len$ = 0; len$ < len$; ++len$) { Interceptor plugin = arr$[len$]; configuration.addInterceptor(plugin); if (this.logger.isDebugEnabled()) { this.logger.debug("Registered plugin: '" + plugin + "'"); } } } if (StringUtils.hasLength(this.typeHandlersPackage)) { typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; \t\n"); arr$ = typeHandlersPackageArray; len$ = typeHandlersPackageArray.length; for(i$ = 0; i$ < len$; ++i$) { packageToScan = arr$[i$]; configuration.getTypeHandlerRegistry().register(packageToScan); if (this.logger.isDebugEnabled()) { this.logger.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } if (!ObjectUtils.isEmpty(this.typeHandlers)) { TypeHandler[] arr$ = this.typeHandlers; len$ = arr$.length; for(len$ = 0; len$ < len$; ++len$) { TypeHandler<?> typeHandler = arr$[len$]; configuration.getTypeHandlerRegistry().register(typeHandler); if (this.logger.isDebugEnabled()) { this.logger.debug("Registered type handler: '" + typeHandler + "'"); } } } if (xmlConfigBuilder != null) { try { xmlConfigBuilder.parse(); if (this.logger.isDebugEnabled()) { this.logger.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception var23) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var23); } finally { ErrorContext.instance().reset(); } } if (this.transactionFactory == null) { this.transactionFactory = new SpringManagedTransactionFactory(); } Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource); configuration.setEnvironment(environment); if (this.databaseIdProvider != null) { try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException var22) { throw new NestedIOException("Failed getting a databaseId", var22); } } if (!ObjectUtils.isEmpty(this.mapperLocations)) { Resource[] arr$ = this.mapperLocations; len$ = arr$.length; for(i$ = 0; i$ < len$; ++i$) { Resource mapperLocation = arr$[i$]; if (mapperLocation != null) { try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception var20) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var20); } finally { ErrorContext.instance().reset(); } if (this.logger.isDebugEnabled()) { this.logger.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } } else if (this.logger.isDebugEnabled()) { this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found"); } return this.sqlSessionFactoryBuilder.build(configuration); }
在这里还做了一件事,就是给我们的每个mapper接口都初始化了一个动态代理类MapperProxy<T> implements InvocationHandler, Serializable。同时将mapper接口中的每个方法信息都初始化到了org.apache.ibatis.session.Configuration中
当我们在程序中调用一个数据库执行方法去操作数据库时,就会在代理类中查询到实现加载好的方法对应的mapperMethod类,拿到方法信息,通过动态sqlSource(DynamicSqlSource)类循环sqlNode对象执行 getBoundSql方法进行sql拼接。通过debug我们会发现sql拼接的时候会将example中的参数转换成#{},而#{}会被转化成?占位符,使用jdbc的statement进行参数赋值,最终在sql是用引号引起来作为参数拼接到sql中的,而${}是直接将内容作为一个可执行的sql放进去的,所以前两种方式传递sql是比较安全的。处理完sql之后openSession 打开事务,操作数据库