在使用 Mybatis 时, 需要通过 SqlSessionFactory 打开一个SqlSession, 然后实例化 Mapper 接口的动态代理对象, 执行数据库相关操作。
创建 SqlSessionFactory 对象 1 2 3 <bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> </bean >
在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建 SqlSessionFactory,SqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口,Spring 将会在应用启动时为你创建 SqlSessionFactory,并使用 sqlSessionFactory 这个名字存储起来。
定位 : org.mybatis.spring.SqlSessionFactoryBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 public class SqlSessionFactoryBean implements FactoryBean <SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { @Override public void afterPropertiesSet () throws Exception { notNull(dataSource, "Property 'dataSource' is required" ); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required" ); state((configuration == null && configLocation == null ) || !(configuration != null && configLocation != null ), "Property 'configuration' and 'configLocation' can not specified with together" ); this .sqlSessionFactory = buildSqlSessionFactory(); } protected SqlSessionFactory buildSqlSessionFactory () throws Exception { final Configuration targetConfiguration; XMLConfigBuilder xmlConfigBuilder = null ; if (this .configuration != null ) { targetConfiguration = this .configuration; if (targetConfiguration.getVariables() == null ) { targetConfiguration.setVariables(this .configurationProperties); } else if (this .configurationProperties != null ) { targetConfiguration.getVariables().putAll(this .configurationProperties); } } else if (this .configLocation != null ) { xmlConfigBuilder = new XMLConfigBuilder (this .configLocation.getInputStream(), null , this .configurationProperties); targetConfiguration = xmlConfigBuilder.getConfiguration(); } else { LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration" ); targetConfiguration = new Configuration (); Optional.ofNullable(this .configurationProperties).ifPresent(targetConfiguration::setVariables); } Optional.ofNullable(this .objectFactory).ifPresent(targetConfiguration::setObjectFactory); Optional.ofNullable(this .objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory); Optional.ofNullable(this .vfs).ifPresent(targetConfiguration::setVfsImpl); if (hasLength(this .typeAliasesPackage)) { scanClasses(this .typeAliasesPackage, this .typeAliasesSuperType).stream() .filter(clazz -> !clazz.isAnonymousClass()) .filter(clazz -> !clazz.isInterface()) .filter(clazz -> !clazz.isMemberClass()) .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias); } if (!isEmpty(this .typeAliases)) { Stream.of(this .typeAliases).forEach(typeAlias -> { targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias); LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'" ); }); } if (!isEmpty(this .plugins)) { Stream.of(this .plugins).forEach(plugin -> { targetConfiguration.addInterceptor(plugin); LOGGER.debug(() -> "Registered plugin: '" + plugin + "'" ); }); } if (hasLength(this .typeHandlersPackage)) { scanClasses(this .typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass()) .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers())) .forEach(targetConfiguration.getTypeHandlerRegistry()::register); } if (!isEmpty(this .typeHandlers)) { Stream.of(this .typeHandlers).forEach(typeHandler -> { targetConfiguration.getTypeHandlerRegistry().register(typeHandler); LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'" ); }); } targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler); if (!isEmpty(this .scriptingLanguageDrivers)) { Stream.of(this .scriptingLanguageDrivers).forEach(languageDriver -> { targetConfiguration.getLanguageRegistry().register(languageDriver); LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'" ); }); } Optional.ofNullable(this .defaultScriptingLanguageDriver) .ifPresent(targetConfiguration::setDefaultScriptingLanguage); if (this .databaseIdProvider != null ) { try { targetConfiguration.setDatabaseId(this .databaseIdProvider.getDatabaseId(this .dataSource)); } catch (SQLException e) { throw new NestedIOException ("Failed getting a databaseId" , e); } } Optional.ofNullable(this .cache).ifPresent(targetConfiguration::addCache); if (xmlConfigBuilder != null ) { try { xmlConfigBuilder.parse(); LOGGER.debug(() -> "Parsed configuration file: '" + this .configLocation + "'" ); } catch (Exception ex) { throw new NestedIOException ("Failed to parse config resource: " + this .configLocation, ex); } finally { ErrorContext.instance().reset(); } } targetConfiguration.setEnvironment(new Environment (this .environment, this .transactionFactory == null ? new SpringManagedTransactionFactory () : this .transactionFactory, this .dataSource)); if (this .mapperLocations != null ) { if (this .mapperLocations.length == 0 ) { LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found." ); } else { for (Resource mapperLocation : this .mapperLocations) { if (mapperLocation == null ) { continue ; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder (mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException ("Failed to parse mapping resource: '" + mapperLocation + "'" , e); } finally { ErrorContext.instance().reset(); } LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'" ); } } } else { LOGGER.debug(() -> "Property 'mapperLocations' was not specified." ); } return this .sqlSessionFactoryBuilder.build(targetConfiguration); } public void onApplicationEvent (ApplicationEvent event) { if (failFast && event instanceof ContextRefreshedEvent) { this .sqlSessionFactory.getConfiguration().getMappedStatementNames(); } } private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException { Set<Class<?>> classes = new HashSet <>(); String[] packagePatternArray = tokenizeToStringArray(packagePatterns, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packagePattern : packagePatternArray) { Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class" ); for (Resource resource : resources) { try { ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata(); Class<?> clazz = Resources.classForName(classMetadata.getClassName()); if (assignableType == null || assignableType.isAssignableFrom(clazz)) { classes.add(clazz); } } catch (Throwable e) { LOGGER.warn(() -> "Cannot load the '" + resource + "'. Cause by " + e.toString()); } } } return classes; } }
实例化 Mapper 接口 扫描 Mapper 1 2 3 4 5 <bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name ="basePackage" value ="org.mybatis.spring.sample.mapper" /> <property name ="annotationClass" value ="org.apache.ibatis.annotations.Mapper" /> </bean >
定位 : org.mybatis.spring.mapper.MapperScannerConfigurer
作用 :用于扫描 Mapper 接口,借助ClassPathMapperScanner扫描器注册 Mapper 接口的BeanDefinition 对象 (注意: 默认情况并没有设置 annotationClass )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor , InitializingBean, ApplicationContextAware, BeanNameAware { @Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) { if (this .processPropertyPlaceHolders) { processPropertyPlaceHolders(); } ClassPathMapperScanner scanner = new ClassPathMapperScanner (registry); scanner.setAddToConfig(this .addToConfig); scanner.setAnnotationClass(this .annotationClass); scanner.setMarkerInterface(this .markerInterface); scanner.setSqlSessionFactory(this .sqlSessionFactory); scanner.setSqlSessionTemplate(this .sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this .sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this .sqlSessionTemplateBeanName); scanner.setResourceLoader(this .applicationContext); scanner.setBeanNameGenerator(this .nameGenerator); scanner.setMapperFactoryBeanClass(this .mapperFactoryBeanClass); if (StringUtils.hasText(lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization)); } if (StringUtils.hasText(defaultScope)) { scanner.setDefaultScope(defaultScope); } scanner.registerFilters(); scanner.scan(StringUtils.tokenizeToStringArray(this .basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); } }
ClassPathMapperScanner
定位: org.mybatis.spring.mapper.ClassPathMapperScanner
作用 : 负责执行扫描,将用户配置信息,设置到扫描到的 Mapper 接口的 BeanDefinition 对象,如将 BeanClass 修改为 MapperFactoryBean,将 autowireMode 设置为根据类型注入,可以让 Spring 实例化的时候,会使用 MapperFactoryBean 类型,并自动注入 SqlSessionFactory 实例,实现创建 Mapper 动态代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { public void registerFilters () { boolean acceptAllInterfaces = true ; if (this .annotationClass != null ) { addIncludeFilter(new AnnotationTypeFilter (this .annotationClass)); acceptAllInterfaces = false ; } if (this .markerInterface != null ) { addIncludeFilter(new AssignableTypeFilter (this .markerInterface) { @Override protected boolean matchClassName (String className) { return false ; } }); acceptAllInterfaces = false ; } if (acceptAllInterfaces) { addIncludeFilter((metadataReader, metadataReaderFactory) -> true ); } addExcludeFilter((metadataReader, metadataReaderFactory) -> { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info" ); }); } public Set<BeanDefinitionHolder> doScan (String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super .doScan(basePackages); if (beanDefinitions.isEmpty()) { LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration." ); } else { processBeanDefinitions(beanDefinitions); } return beanDefinitions; } private void processBeanDefinitions (Set<BeanDefinitionHolder> beanDefinitions) { AbstractBeanDefinition definition; BeanDefinitionRegistry registry = getRegistry(); for (BeanDefinitionHolder holder : beanDefinitions) { definition = (AbstractBeanDefinition) holder.getBeanDefinition(); boolean scopedProxy = false ; if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) { definition = (AbstractBeanDefinition) Optional .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition()) .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException ( "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]" )); scopedProxy = true ; } String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface" ); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); definition.setBeanClass(this .mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig" , this .addToConfig); boolean explicitFactoryUsed = false ; if (StringUtils.hasText(this .sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory" , new RuntimeBeanReference (this .sqlSessionFactoryBeanName)); explicitFactoryUsed = true ; } else if (this .sqlSessionFactory != null ) { definition.getPropertyValues().add("sqlSessionFactory" , this .sqlSessionFactory); explicitFactoryUsed = true ; } if (StringUtils.hasText(this .sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored." ); } definition.getPropertyValues().add("sqlSessionTemplate" , new RuntimeBeanReference (this .sqlSessionTemplateBeanName)); explicitFactoryUsed = true ; } else if (this .sqlSessionTemplate != null ) { if (explicitFactoryUsed) { LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored." ); } definition.getPropertyValues().add("sqlSessionTemplate" , this .sqlSessionTemplate); explicitFactoryUsed = true ; } if (!explicitFactoryUsed) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'." ); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); if (scopedProxy) { continue ; } if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null ) { definition.setScope(defaultScope); } if (!definition.isSingleton()) { BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true ); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) { registry.removeBeanDefinition(proxyHolder.getBeanName()); } registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition()); } } } @Override protected boolean isCandidateComponent (AnnotatedBeanDefinition beanDefinition) { return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); } }
通过注解 @MapperScan 扫描 使用例子:
1 2 3 4 @Configuration @MapperScan(value = "org.mybatis.spring.sample.mapper", annotationClass = Mapper.class) public class AppConfig {}
MapperScan 注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) @Repeatable(MapperScans.class) public @interface MapperScan { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator > nameGenerator() default BeanNameGenerator.class; Class<? extends Annotation > annotationClass() default Annotation.class; Class<?> markerInterface() default Class.class; String sqlSessionTemplateRef () default "" ; String sqlSessionFactoryRef () default "" ; Class<? extends MapperFactoryBean > factoryBean() default MapperFactoryBean.class; String lazyInitialization () default "" ; String defaultScope () default AbstractBeanDefinition.SCOPE_DEFAULT; }
MapperScannerRegistrar
定位 : org.mybatis.spring.annotation.MapperScannerRegistrar
作用 : @MapperScann 的注册器,根据注解信息注册 MapperScannerConfigurer 对象,用于扫描 Mapper 接口
1 2 3 4 5 6 7 8 9 10 11 12 public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware { @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null ) { registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0 )); } } }
通过 Mybatis-Spring 的 xml 配置(基本不用) 1 2 3 4 5 6 7 8 <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:mybatis ="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd" > <mybatis:scan base-package ="org.mybatis.spring.sample.mapper" /> </beans >
<mybatis:scan /> 的解析器 org.mybatis.spring.config.MapperScannerBeanDefinitionParser, 实现原理等同于 MapperScannerRegistrar, 解析 mybatis:scan 配置的标签信息并注册 MapperScannerConfigurer 对象
实例化 Mapper MapperFactoryBean
定位 : org.mybatis.spring.mapper.MapperFactoryBean
作用 : 实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class MapperFactoryBean <T> extends SqlSessionDaoSupport implements FactoryBean <T> { protected void checkDaoConfig () { super .checkDaoConfig(); notNull(this .mapperInterface, "Property 'mapperInterface' is required" ); Configuration configuration = getSqlSession().getConfiguration(); if (this .addToConfig && !configuration.hasMapper(this .mapperInterface)) { try { configuration.addMapper(this .mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this .mapperInterface + "' to configuration." , e); throw new IllegalArgumentException (e); } finally { ErrorContext.instance().reset(); } } } public T getObject () throws Exception { return getSqlSession().getMapper(this .mapperInterface); } }
SqlSessionDaoSupport
定位 : org.mybatis.spring.support.SqlSessionDaoSupport
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSessionTemplate sqlSessionTemplate; public void setSqlSessionFactory (SqlSessionFactory sqlSessionFactory) { if (this .sqlSessionTemplate == null || sqlSessionFactory != this .sqlSessionTemplate.getSqlSessionFactory()) { this .sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory); } } protected SqlSessionTemplate createSqlSessionTemplate (SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate (sqlSessionFactory); } public void setSqlSessionTemplate (SqlSessionTemplate sqlSessionTemplate) { this .sqlSessionTemplate = sqlSessionTemplate; } }
SqlSessionTemplate SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。 SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class SqlSessionTemplate implements SqlSession , DisposableBean { public SqlSessionTemplate (SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required" ); notNull(executorType, "Property 'executorType' is required" ); this .sqlSessionFactory = sqlSessionFactory; this .executorType = executorType; this .exceptionTranslator = exceptionTranslator; this .sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class [] { SqlSession.class }, new SqlSessionInterceptor ()); } @Override public <T> Cursor<T> selectCursor (String statement, Object parameter, RowBounds rowBounds) { return this .sqlSessionProxy.selectCursor(statement, parameter, rowBounds); } @Override public <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds) { return this .sqlSessionProxy.selectList(statement, parameter, rowBounds); } @Override public int update (String statement, Object parameter) { return this .sqlSessionProxy.update(statement, parameter); } @Override public <T> T getMapper (Class<T> type) { return getConfiguration().getMapper(type, this ); } private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this .sqlSessionFactory, SqlSessionTemplate.this .executorType, SqlSessionTemplate.this .exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this .sqlSessionFactory)) { sqlSession.commit(true ); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this .exceptionTranslator != null && unwrapped instanceof PersistenceException) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this .sqlSessionFactory); sqlSession = null ; Throwable translated = SqlSessionTemplate.this .exceptionTranslator .translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null ) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null ) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this .sqlSessionFactory); } } } } }
获取当前的 sqlSession
定位 : org.mybatis.spring.SqlSessionUtils#getSqlSession
作用 : 从当前线程变量中获取 SqlSession,如果不存在,则创建一个, 并将新创建的 SQL Session 添加到线程变量中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public static SqlSession getSqlSession (SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED); SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null ) { return session; } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; } private static void registerSessionHolder (SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; if (TransactionSynchronizationManager.isSynchronizationActive()) { Environment environment = sessionFactory.getConfiguration().getEnvironment(); if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]" ); holder = new SqlSessionHolder (session, executorType, exceptionTranslator); TransactionSynchronizationManager.bindResource(sessionFactory, holder); TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization (holder, sessionFactory)); holder.setSynchronizedWithTransaction(true ); holder.requested(); } else { if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null ) { LOGGER.debug(() -> "SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional" ); } else { throw new TransientDataAccessResourceException ( "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization" ); } } } else { LOGGER.debug(() -> "SqlSession [" + session + "] was not registered for synchronization because synchronization is not active" ); } }
SqlSession 和 Mapper 关系 在 Mybatis 中, 每次打开 openSession 后, 都会创建一个新的 SqlSession 实例, 在从 SqlSession 中获取 Mapper 实例时, 也会创建一个新的 Mapper 代理对象;
在 Mybatis-Spring 中, Mapper 的代理对象由 Spring 容器的 MapperFactoryBean 对象创建, 并且为单例的,每次调用 Mapper 的增删改查时,会先 openSession ( 创建一个新 SqlSession 实例) 后, 通过 ThreadLocal 来实现当前线程持有当前的 SqlSession 实例对象
Mybatis-Spring 事务托管 使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。
一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。
1 2 3 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <constructor-arg ref ="dataSource" /> </bean >
编程式事务管理案例
1 2 3 4 5 6 7 8 9 10 11 12 13 public class UserService { private final PlatformTransactionManager transactionManager; public UserService (PlatformTransactionManager transactionManager) { this .transactionManager = transactionManager; } public void createUser () { TransactionTemplate transactionTemplate = new TransactionTemplate (transactionManager); transactionTemplate.execute(txStatus -> { userMapper.insertUser(user); return null ; }); } }
链接 https://mybatis.org/spring/zh/index.html
mybatis-spring 注释源码