I/O輸出輸入流
阿新 • • 發佈:2020-11-24
Spring整合MyBatis
使用
- 配置資料來源
<!--配置dataSource--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/> <property name="username" value="xxx"/> <property name="password" value="xxx"/> </bean>
- 配置SqlSessionFactoryBean
<!--配置sqlSessionFactory, 這裡使用了SqlSessionFacotryBean,它繼承了FactoryBean介面,可以讓mybatis自定義生成我們的sqlSessionFactory --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="myBatis_Spring.entity"/> </bean>
- 掃描mapper介面,併為每個mapper介面代理物件
<!--掃描所有mapper,並生成代理物件-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="myBatis_Spring.mapper"/>
</bean>
物件說明
FactoryBean: 工廠Bean用於自定義生成Bean物件。當在ioc 中配置FactoryBean 的例項時,最終通過bean id 對應的是FactoryBean.getObject()例項,而非FactoryBean 例項本身
SqlSessionFactoryBean: 生成SqlSessionFactory 例項,該為單例對像,作用於整個應用生命週期。
MapperScannerConfigurer: 掃描我們寫的mapper介面,並在spring容器初始化時為我們建立一個單例的Mapper代理物件。
執行緒安全問題
和原生mybatis不同的是,創建出來的mapper是一個單例物件,按理說會存線上程安全問題。但是spring幫我們解決了這一點,當spring容器初始化的時候,會生成mapper的一個代理物件。每當mapper呼叫它的任何一個方法,都會經過代理物件的invoke方法。在這個invoke方法中,每次都會建立一個新的SqlSession,當這個方法呼叫完成後SqlSession方法也會隨之銷燬或者釋放。
/*
SqlSessionTemplate > SqlSessionInterceptor > invoke()
*/
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//獲取SqlSession物件
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
....
....
一級快取還會生效嗎
如果每次執行一個查詢都會建立一個新的SqlSession,那麼一級快取不是不起作用了嗎?
確實如此,但是當我們Service層方法加了@Transactional後,在這個有事務的方法內,第一次查詢資料庫,會把新建的SqlSession物件存入ThreadLoacl中,下一次進行查詢時會先判斷這個ThreadLocal中是否有對應的SqlSession物件。
/*
SqlSessionUtils > getSqlSession()
*/
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);
//從ThreadLoacl中獲取SqlSession
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
LOGGER.debug(() -> "Creating a new SqlSession");
//如果ThreadLoacl中沒有的話就建立一個新的SqlSession
session = sessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}