session 一级缓存优化
package org. apache. ibatis. executor;
import static org. apache. ibatis. executor. ExecutionPlaceholder. EXECUTION_PLACEHOLDER;
import java. sql. Connection;
import java. sql. SQLException;
import java. sql. Statement;
import java. util. List;
import java. util. concurrent. ConcurrentLinkedQueue;
import org. apache. ibatis. cache. CacheKey;
import org. apache. ibatis. cache. impl. PerpetualCache;
import org. apache. ibatis. cursor. Cursor;
import org. apache. ibatis. executor. statement. StatementUtil;
import org. apache. ibatis. logging. Log;
import org. apache. ibatis. logging. LogFactory;
import org. apache. ibatis. logging. jdbc. ConnectionLogger;
import org. apache. ibatis. mapping. BoundSql;
import org. apache. ibatis. mapping. MappedStatement;
import org. apache. ibatis. mapping. ParameterMapping;
import org. apache. ibatis. mapping. ParameterMode;
import org. apache. ibatis. mapping. StatementType;
import org. apache. ibatis. reflection. MetaObject;
import org. apache. ibatis. reflection. factory. ObjectFactory;
import org. apache. ibatis. session. Configuration;
import org. apache. ibatis. session. LocalCacheScope;
import org. apache. ibatis. session. ResultHandler;
import org. apache. ibatis. session. RowBounds;
import org. apache. ibatis. transaction. Transaction;
import org. apache. ibatis. type. TypeHandlerRegistry;
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory. getLog ( BaseExecutor. class ) ;
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue< DeferredLoad> deferredLoads;
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
protected int queryStack;
private boolean closed;
protected BaseExecutor ( Configuration configuration, Transaction transaction) {
this . transaction = transaction;
this . deferredLoads = new ConcurrentLinkedQueue < > ( ) ;
this . localCache = new PerpetualCache ( "LocalCache" ) ;
this . localOutputParameterCache = new PerpetualCache ( "LocalOutputParameterCache" ) ;
this . closed = false ;
this . configuration = configuration;
this . wrapper = this ;
}
@Override
public Transaction getTransaction ( ) {
if ( closed) {
throw new ExecutorException ( "Executor was closed." ) ;
}
return transaction;
}
@Override
public void close ( boolean forceRollback) {
try {
try {
rollback ( forceRollback) ;
} finally {
if ( transaction != null) {
transaction. close ( ) ;
}
}
} catch ( SQLException e) {
log. warn ( "Unexpected exception on closing transaction. Cause: " + e) ;
} finally {
transaction = null;
deferredLoads = null;
localCache = null;
localOutputParameterCache = null;
closed = true ;
}
}
@Override
public boolean isClosed ( ) {
return closed;
}
@Override
public int update ( MappedStatement ms, Object parameter) throws SQLException {
ErrorContext. instance ( ) . resource ( ms. getResource ( ) ) . activity ( "executing an update" ) . object ( ms. getId ( ) ) ;
if ( closed) {
throw new ExecutorException ( "Executor was closed." ) ;
}
clearLocalCache ( ) ;
return doUpdate ( ms, parameter) ;
}
@Override
public List< BatchResult> flushStatements ( ) throws SQLException {
return flushStatements ( false ) ;
}
public List< BatchResult> flushStatements ( boolean isRollBack) throws SQLException {
if ( closed) {
throw new ExecutorException ( "Executor was closed." ) ;
}
return doFlushStatements ( isRollBack) ;
}
@Override
public < E> List< E> query ( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms. getBoundSql ( parameter) ;
CacheKey key = createCacheKey ( ms, parameter, rowBounds, boundSql) ;
return query ( ms, parameter, rowBounds, resultHandler, key, boundSql) ;
}
@SuppressWarnings ( "unchecked" )
@Override
public < E> List< E> 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< E> list;
try {
queryStack++ ;
list = resultHandler == null ? ( List< E> ) 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 ( ) ;
}
deferredLoads. clear ( ) ;
if ( configuration. getLocalCacheScope ( ) == LocalCacheScope. STATEMENT) {
clearLocalCache ( ) ;
}
}
return list;
}
@Override
public < E> Cursor< E> queryCursor ( MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
BoundSql boundSql = ms. getBoundSql ( parameter) ;
return doQueryCursor ( ms, parameter, rowBounds, boundSql) ;
}
@Override
public void deferLoad ( MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class< ? > targetType) {
if ( closed) {
throw new ExecutorException ( "Executor was closed." ) ;
}
DeferredLoad deferredLoad = new DeferredLoad ( resultObject, property, key, localCache, configuration, targetType) ;
if ( deferredLoad. canLoad ( ) ) {
deferredLoad. load ( ) ;
} else {
deferredLoads. add ( new DeferredLoad ( resultObject, property, key, localCache, configuration, targetType) ) ;
}
}
@Override
public CacheKey createCacheKey ( MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if ( closed) {
throw new ExecutorException ( "Executor was closed." ) ;
}
CacheKey cacheKey = new CacheKey ( ) ;
cacheKey. update ( ms. getId ( ) ) ;
cacheKey. update ( rowBounds. getOffset ( ) ) ;
cacheKey. update ( rowBounds. getLimit ( ) ) ;
cacheKey. update ( boundSql. getSql ( ) ) ;
List< ParameterMapping> parameterMappings = boundSql. getParameterMappings ( ) ;
TypeHandlerRegistry typeHandlerRegistry = ms. getConfiguration ( ) . getTypeHandlerRegistry ( ) ;
for ( ParameterMapping parameterMapping : parameterMappings) {
if ( parameterMapping. getMode ( ) != ParameterMode. OUT) {
Object value;
String propertyName = parameterMapping. getProperty ( ) ;
if ( boundSql. hasAdditionalParameter ( propertyName) ) {
value = boundSql. getAdditionalParameter ( propertyName) ;
} else if ( parameterObject == null) {
value = null;
} else if ( typeHandlerRegistry. hasTypeHandler ( parameterObject. getClass ( ) ) ) {
value = parameterObject;
} else {
MetaObject metaObject = configuration. newMetaObject ( parameterObject) ;
value = metaObject. getValue ( propertyName) ;
}
cacheKey. update ( value) ;
}
}
if ( configuration. getEnvironment ( ) != null) {
cacheKey. update ( configuration. getEnvironment ( ) . getId ( ) ) ;
}
return cacheKey;
}
@Override
public boolean isCached ( MappedStatement ms, CacheKey key) {
return localCache. getObject ( key) != null;
}
@Override
public void commit ( boolean required) throws SQLException {
if ( closed) {
throw new ExecutorException ( "Cannot commit, transaction is already closed" ) ;
}
clearLocalCache ( ) ;
flushStatements ( ) ;
if ( required) {
transaction. commit ( ) ;
}
}
@Override
public void rollback ( boolean required) throws SQLException {
if ( ! closed) {
try {
clearLocalCache ( ) ;
flushStatements ( true ) ;
} finally {
if ( required) {
transaction. rollback ( ) ;
}
}
}
}
@Override
public void clearLocalCache ( ) {
if ( ! closed) {
localCache. clear ( ) ;
localOutputParameterCache. clear ( ) ;
}
}
protected abstract int doUpdate ( MappedStatement ms, Object parameter) throws SQLException;
protected abstract List< BatchResult> doFlushStatements ( boolean isRollback) throws SQLException;
protected abstract < E> List< E> doQuery ( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract < E> Cursor< E> doQueryCursor ( MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
protected void closeStatement ( Statement statement) {
if ( statement != null) {
try {
statement. close ( ) ;
} catch ( SQLException e) {
}
}
}
protected void applyTransactionTimeout ( Statement statement) throws SQLException {
StatementUtil. applyTransactionTimeout ( statement, statement. getQueryTimeout ( ) , transaction. getTimeout ( ) ) ;
}
private void handleLocallyCachedOutputParameters ( MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
if ( ms. getStatementType ( ) == StatementType. CALLABLE) {
final Object cachedParameter = localOutputParameterCache. getObject ( key) ;
if ( cachedParameter != null && parameter != null) {
final MetaObject metaCachedParameter = configuration. newMetaObject ( cachedParameter) ;
final MetaObject metaParameter = configuration. newMetaObject ( parameter) ;
for ( ParameterMapping parameterMapping : boundSql. getParameterMappings ( ) ) {
if ( parameterMapping. getMode ( ) != ParameterMode. IN) {
final String parameterName = parameterMapping. getProperty ( ) ;
final Object cachedValue = metaCachedParameter. getValue ( parameterName) ;
metaParameter. setValue ( parameterName, cachedValue) ;
}
}
}
}
}
private < E> List< E> queryFromDatabase ( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List< E> list;
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;
}
protected Connection getConnection ( Log statementLog) throws SQLException {
Connection connection = transaction. getConnection ( ) ;
if ( statementLog. isDebugEnabled ( ) ) {
return ConnectionLogger. newInstance ( connection, statementLog, queryStack) ;
} else {
return connection;
}
}
@Override
public void setExecutorWrapper ( Executor wrapper) {
this . wrapper = wrapper;
}
private static class DeferredLoad {
private final MetaObject resultObject;
private final String property;
private final Class< ? > targetType;
private final CacheKey key;
private final PerpetualCache localCache;
private final ObjectFactory objectFactory;
private final ResultExtractor resultExtractor;
public DeferredLoad ( MetaObject resultObject,
String property,
CacheKey key,
PerpetualCache localCache,
Configuration configuration,
Class< ? > targetType) {
this . resultObject = resultObject;
this . property = property;
this . key = key;
this . localCache = localCache;
this . objectFactory = configuration. getObjectFactory ( ) ;
this . resultExtractor = new ResultExtractor ( configuration, objectFactory) ;
this . targetType = targetType;
}
public boolean canLoad ( ) {
return
localCache. getObject ( key) != null
&& localCache. getObject ( key) != EXECUTION_PLACEHOLDER;
}
public void load ( ) {
@SuppressWarnings ( "unchecked" )
List< Object> list = ( List< Object> ) localCache. getObject ( key) ;
Object value = resultExtractor. extractObjectFromList ( list, targetType) ;
resultObject. setValue ( property, value) ;
}
}
}
二级缓存优化
public class CachingExecutor implements Executor {
@Override
public < E> List< E> query ( MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms. getBoundSql ( parameterObject) ;
CacheKey key = createCacheKey ( ms, parameterObject, rowBounds, boundSql) ;
return query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ;
}
@Override
public < E> List< E> query ( MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms. getCache ( ) ;
if ( cache != null) {
flushCacheIfRequired ( ms) ;
if ( ms. isUseCache ( ) && resultHandler == null) {
ensureNoOutParams ( ms, boundSql) ;
@SuppressWarnings ( "unchecked" )
List< E> list = ( List< E> ) tcm. getObject ( cache, key) ;
if ( list == null) {
list = delegate. query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ;
tcm. putObject ( cache, key, list) ;
}
return list;
}
}
return delegate. query ( ms, parameterObject, rowBounds, resultHandler, key, boundSql) ;
}
@Override
public void commit ( boolean required) throws SQLException {
delegate. commit ( required) ;
tcm. commit ( ) ;
}
@Override
public void rollback ( boolean required) throws SQLException {
try {
delegate. rollback ( required) ;
} finally {
if ( required) {
tcm. rollback ( ) ;
}
}
}
}
Statement重用优化
package org. apache. ibatis. executor;
import java. sql. Connection;
import java. sql. SQLException;
import java. sql. Statement;
import java. util. Collections;
import java. util. HashMap;
import java. util. List;
import java. util. Map;
import org. apache. ibatis. cursor. Cursor;
import org. apache. ibatis. executor. statement. StatementHandler;
import org. apache. ibatis. logging. Log;
import org. apache. ibatis. mapping. BoundSql;
import org. apache. ibatis. mapping. MappedStatement;
import org. apache. ibatis. session. Configuration;
import org. apache. ibatis. session. ResultHandler;
import org. apache. ibatis. session. RowBounds;
import org. apache. ibatis. transaction. Transaction;
public class ReuseExecutor extends BaseExecutor {
private final Map< String, Statement> statementMap = new HashMap < > ( ) ;
public ReuseExecutor ( Configuration configuration, Transaction transaction) {
super ( configuration, transaction) ;
}
@Override
public int doUpdate ( MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms. getConfiguration ( ) ;
StatementHandler handler = configuration. newStatementHandler ( this , ms, parameter, RowBounds. DEFAULT, null, null) ;
Statement stmt = prepareStatement ( handler, ms. getStatementLog ( ) ) ;
return handler. update ( stmt) ;
}
@Override
public < E> List< E> doQuery ( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms. getConfiguration ( ) ;
StatementHandler handler = configuration. newStatementHandler ( wrapper, ms, parameter, rowBounds, resultHandler, boundSql) ;
Statement stmt = prepareStatement ( handler, ms. getStatementLog ( ) ) ;
return handler. query ( stmt, resultHandler) ;
}
@Override
protected < E> Cursor< E> doQueryCursor ( MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
Configuration configuration = ms. getConfiguration ( ) ;
StatementHandler handler = configuration. newStatementHandler ( wrapper, ms, parameter, rowBounds, null, boundSql) ;
Statement stmt = prepareStatement ( handler, ms. getStatementLog ( ) ) ;
return handler. queryCursor ( stmt) ;
}
@Override
public List< BatchResult> doFlushStatements ( boolean isRollback) {
for ( Statement stmt : statementMap. values ( ) ) {
closeStatement ( stmt) ;
}
statementMap. clear ( ) ;
return Collections. emptyList ( ) ;
}
private Statement prepareStatement ( StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler. getBoundSql ( ) ;
String sql = boundSql. getSql ( ) ;
if ( hasStatementFor ( sql) ) {
stmt = getStatement ( sql) ;
applyTransactionTimeout ( stmt) ;
} else {
Connection connection = getConnection ( statementLog) ;
stmt = handler. prepare ( connection, transaction. getTimeout ( ) ) ;
putStatement ( sql, stmt) ;
}
handler. parameterize ( stmt) ;
return stmt;
}
private boolean hasStatementFor ( String sql) {
try {
Statement statement = statementMap. get ( sql) ;
return statement != null && ! statement. getConnection ( ) . isClosed ( ) ;
} catch ( SQLException e) {
return false ;
}
}
private Statement getStatement ( String s) {
return statementMap. get ( s) ;
}
private void putStatement ( String sql, Statement stmt) {
statementMap. put ( sql, stmt) ;
}
}
批量处理SQL优化
package org. apache. ibatis. executor;
import java. sql. BatchUpdateException;
import java. sql. Connection;
import java. sql. SQLException;
import java. sql. Statement;
import java. util. ArrayList;
import java. util. Collections;
import java. util. List;
import org. apache. ibatis. cursor. Cursor;
import org. apache. ibatis. executor. keygen. Jdbc3KeyGenerator;
import org. apache. ibatis. executor. keygen. KeyGenerator;
import org. apache. ibatis. executor. keygen. NoKeyGenerator;
import org. apache. ibatis. executor. statement. StatementHandler;
import org. apache. ibatis. mapping. BoundSql;
import org. apache. ibatis. mapping. MappedStatement;
import org. apache. ibatis. session. Configuration;
import org. apache. ibatis. session. ResultHandler;
import org. apache. ibatis. session. RowBounds;
import org. apache. ibatis. transaction. Transaction;
public class BatchExecutor extends BaseExecutor {
public static final int BATCH_UPDATE_RETURN_VALUE = Integer. MIN_VALUE + 1002 ;
private final List< Statement> statementList = new ArrayList < > ( ) ;
private final List< BatchResult> batchResultList = new ArrayList < > ( ) ;
private String currentSql;
private MappedStatement currentStatement;
public BatchExecutor ( Configuration configuration, Transaction transaction) {
super ( configuration, transaction) ;
}
@Override
public int doUpdate ( MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms. getConfiguration ( ) ;
final StatementHandler handler = configuration. newStatementHandler ( this , ms, parameterObject, RowBounds. DEFAULT, null, null) ;
final BoundSql boundSql = handler. getBoundSql ( ) ;
final String sql = boundSql. getSql ( ) ;
final Statement stmt;
if ( sql. equals ( currentSql) && ms. equals ( currentStatement) ) {
int last = statementList. size ( ) - 1 ;
stmt = statementList. get ( last) ;
applyTransactionTimeout ( stmt) ;
handler. parameterize ( stmt) ;
BatchResult batchResult = batchResultList. get ( last) ;
batchResult. addParameterObject ( parameterObject) ;
} else {
Connection connection = getConnection ( ms. getStatementLog ( ) ) ;
stmt = handler. prepare ( connection, transaction. getTimeout ( ) ) ;
handler. parameterize ( stmt) ;
currentSql = sql;
currentStatement = ms;
statementList. add ( stmt) ;
batchResultList. add ( new BatchResult ( ms, sql, parameterObject) ) ;
}
handler. batch ( stmt) ;
return BATCH_UPDATE_RETURN_VALUE;
}
@Override
public < E> List< E> doQuery ( MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
flushStatements ( ) ;
Configuration configuration = ms. getConfiguration ( ) ;
StatementHandler handler = configuration. newStatementHandler ( wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql) ;
Connection connection = getConnection ( ms. getStatementLog ( ) ) ;
stmt = handler. prepare ( connection, transaction. getTimeout ( ) ) ;
handler. parameterize ( stmt) ;
return handler. query ( stmt, resultHandler) ;
} finally {
closeStatement ( stmt) ;
}
}
@Override
protected < E> Cursor< E> doQueryCursor ( MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
flushStatements ( ) ;
Configuration configuration = ms. getConfiguration ( ) ;
StatementHandler handler = configuration. newStatementHandler ( wrapper, ms, parameter, rowBounds, null, boundSql) ;
Connection connection = getConnection ( ms. getStatementLog ( ) ) ;
Statement stmt = handler. prepare ( connection, transaction. getTimeout ( ) ) ;
handler. parameterize ( stmt) ;
Cursor< E> cursor = handler. queryCursor ( stmt) ;
stmt. closeOnCompletion ( ) ;
return cursor;
}
@Override
public List< BatchResult> doFlushStatements ( boolean isRollback) throws SQLException {
try {
List< BatchResult> results = new ArrayList < > ( ) ;
if ( isRollback) {
return Collections. emptyList ( ) ;
}
for ( int i = 0 , n = statementList. size ( ) ; i < n; i++ ) {
Statement stmt = statementList. get ( i) ;
applyTransactionTimeout ( stmt) ;
BatchResult batchResult = batchResultList. get ( i) ;
try {
batchResult. setUpdateCounts ( stmt. executeBatch ( ) ) ;
MappedStatement ms = batchResult. getMappedStatement ( ) ;
List< Object> parameterObjects = batchResult. getParameterObjects ( ) ;
KeyGenerator keyGenerator = ms. getKeyGenerator ( ) ;
if ( Jdbc3KeyGenerator. class . equals ( keyGenerator. getClass ( ) ) ) {
Jdbc3KeyGenerator jdbc3KeyGenerator = ( Jdbc3KeyGenerator) keyGenerator;
jdbc3KeyGenerator. processBatch ( ms, stmt, parameterObjects) ;
} else if ( ! NoKeyGenerator. class . equals ( keyGenerator. getClass ( ) ) ) {
for ( Object parameter : parameterObjects) {
keyGenerator. processAfter ( this , ms, stmt, parameter) ;
}
}
closeStatement ( stmt) ;
} catch ( BatchUpdateException e) {
StringBuilder message = new StringBuilder ( ) ;
message. append ( batchResult. getMappedStatement ( ) . getId ( ) )
. append ( " (batch index #" )
. append ( i + 1 )
. append ( ")" )
. append ( " failed." ) ;
if ( i > 0 ) {
message. append ( " " )
. append ( i)
. append ( " prior sub executor(s) completed successfully, but will be rolled back." ) ;
}
throw new BatchExecutorException ( message. toString ( ) , e, results, batchResult) ;
}
results. add ( batchResult) ;
}
return results;
} finally {
for ( Statement stmt : statementList) {
closeStatement ( stmt) ;
}
currentSql = null;
statementList. clear ( ) ;
batchResultList. clear ( ) ;
}
}
}
懒加载