Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy——MapperProxy对象。当我们调用Mapper接口方法的时候,实际调用到MapperProxy对象的invoke()方法。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }
mapperMethod.execute()代码如下:
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATe: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
可以看出,invoke()方法实际上调用的就是SqlSession里面的东西了。
看默认实现类DefaultSqlSession里面的具体实现方法:
@Override publicList selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
根据类的全限定名+方法名(statement的值,比如com.google.dao.selectList),唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。
NOTE:不管有几个XML和Dao建立关系,只要保证namespace+id唯一即可。
问题二:session是怎么管理的? SqlSessionFactoryBuilderpublic SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { 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) { return new DefaultSqlSessionFactory(config); }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(); } }SqlSession
SqlSession对象由SqlSessionFactory类创建,需要注意的是,每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享,是线程不安全的,在 *** 作对象的时候必须要保持其线程安全。
问题三:为什么说SqlSession是线程不安全的?在Mybatis中SqlSession是提供给外部调用的顶层接口,实现类有:DefaultSqlSession、SqlSessionManager以及mybatis-spring提供的实现SqlSessionTemplate。默认实现类为DefaultSqlSession,Not Thread-Safe。 SqlSessionManager和SqlSessionTemplate都是Thread safe的。
DefaultSqlSessionpublic class DefaultSqlSession implements SqlSession { @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } }
executor.query()具体实现如下:
@Override publicList query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List list; try { queryStack++; //多线程情况下,第二个线程执行此方法时,会抛出强转类型异常。因为第一个线程放入localCache的是EXECUTION_PLACEHOLDER对象 list = resultHandler == null ? (List ) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }
privateSqlSessionTemplateList queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List list; //多线程情况下,第一个进入此方法的线程会在localCache放入一个EXECUTION_PLACEHOLDER对象 localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
在mybatis-spring框架中,由Spring提供的SqlSession实现类。
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()); }
private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 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; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 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) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
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; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }SqlSessionManager
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor()); } public static SqlSessionManager newInstance(Reader reader) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null)); } public static SqlSessionManager newInstance(Reader reader, String environment) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null)); } public static SqlSessionManager newInstance(Reader reader, Properties properties) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, properties)); } public static SqlSessionManager newInstance(InputStream inputStream) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, null)); } public static SqlSessionManager newInstance(InputStream inputStream, String environment) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, environment, null)); } public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties)); } public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) { return new SqlSessionManager(sqlSessionFactory); }
private class SqlSessionInterceptor implements InvocationHandler { public SqlSessionInterceptor() { // Prevent Synthetic Access } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取当前ThreadLocal上下文的SqlSession final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); if (sqlSession != null) { try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else { //如果无法从ThreadLocal上下文中获取SqlSession则新建一个SqlSession final SqlSession autoSqlSession = openSession(); try { final Object result = method.invoke(autoSqlSession, args); autoSqlSession.commit(); return result; } catch (Throwable t) { autoSqlSession.rollback(); throw ExceptionUtil.unwrapThrowable(t); } finally { autoSqlSession.close(); } } } }
查看SqlSessionManager.this.localSqlSession.set()方法的调用情况:
public void startManagedSession() { this.localSqlSession.set(openSession()); } public void startManagedSession(boolean autoCommit) { this.localSqlSession.set(openSession(autoCommit)); } public void startManagedSession(Connection connection) { this.localSqlSession.set(openSession(connection)); } public void startManagedSession(TransactionIsolationLevel level) { this.localSqlSession.set(openSession(level)); } public void startManagedSession(ExecutorType execType) { this.localSqlSession.set(openSession(execType)); } public void startManagedSession(ExecutorType execType, boolean autoCommit) { this.localSqlSession.set(openSession(execType, autoCommit)); } public void startManagedSession(ExecutorType execType, TransactionIsolationLevel level) { this.localSqlSession.set(openSession(execType, level)); } public void startManagedSession(ExecutorType execType, Connection connection) { this.localSqlSession.set(openSession(execType, connection)); }SqlSessionTemplate与SqlSessionManager的联系与区别
1.SqlSessionTemplate是Mybatis为了接入Spring提供的Bean。通过TransactionSynchronizationManager中的ThreadLocal
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)