主要以线程的数量为主的分析
问题1:线程安全
在整个app多线程中,可以共用一个SqlMapClient来执行操作。原理是利用了ThreadLocal,ThreadLocal实际上是不同的Thread以ThreadLocal自身对象为key的一份ThreadLocalMap拷贝,每个thread创建了各自的Map,这个Map的key就是ThreadLocal的hashcode,所以,同一个ThreadLocal存储的不同value会存储在Map中table数组的同一个index上,并组成了链表。不同的ThreadLocal则存储在Thread中不同的index处,这个Map也不可能大小总为1;
在sqlMapClientImpl里面获取sqlMapSessionImpl对象
protected SqlMapSessionImpl getLocalSqlMapSession() {
SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();
if (sqlMapSession == null || sqlMapSession.isClosed()) {
sqlMapSession = new SqlMapSessionImpl(this);
localSqlMapSession.set(sqlMapSession);
}
return sqlMapSession;
}
这是对一个线程的,new SqlMapSessionImpl()对象的构造器在SqlMapSessionImpl类里面:
/**
* Constructor
*
* @param client - the client that will use the session
*/
public SqlMapSessionImpl(ExtendedSqlMapClient client) {
this.delegate = client.getDelegate();
this.session = this.delegate.popSession();
this.session.setSqlMapClient(client);
this.session.setSqlMapExecutor(client);
this.session.setSqlMapTxMgr(client);
this.closed = false;
}
public class SqlMapSessionImpl implements SqlMapSession {
protected SqlMapExecutorDelegate delegate;
protected SessionScope session;
protected boolean closed;
/**
* Constructor
*
* @param client - the client that will use the session
*/
public SqlMapSessionImpl(ExtendedSqlMapClient client) {
this.delegate = client.getDelegate();
this.session = this.delegate.popSession();
this.session.setSqlMapClient(client);
this.session.setSqlMapExecutor(client);
this.session.setSqlMapTxMgr(client);
this.closed = false;
}
}
这里看到,真正做事的是SqlMapExecutorDelegate这个类,并且还注意到,这里其实把SqlMapExecutorDelegate这个实现类传递给了sqlMapSession,又封装了一层。
问题2:Session上下文(事务控制)
每个SessionScope管理自己的事务,在这个事务中执行过的所有sql语句。这里用的是代理模式,sqlMapSessionImpl和SqlMapExecutorDelegate是集成相同的接口sqlMapExecutor和SqlMapTransactionManager.
从上面的代码可以看出,sqlMapSessionImpl构造了一个sessionScope对象,sessionScope是装饰模式,是一个threadLocal主要的工作类。它被SqlMapExecutorDelegate中的一个pool维护着,
,到此,可以看到很多操作都包装在了SqlMapExecutorDelegate类里。
SessionScope对象池用Collections.synchronizedList(new ArrayList(size));线程安全的List来存储,因为所有SessionScope都公用同一个SqlMapExecutorDelegate。
SqlMapExecutorDelegate类:
protected SessionScope popSession() {
return (SessionScope) sessionPool.pop();
}
public class SqlMapExecutorDelegate {
private static final Probe PROBE = ProbeFactory.getProbe();
/**
* The default maximum number of requests
*/
public static final int DEFAULT_MAX_REQUESTS = 512;
/**
* The default maximum number of sessions
*/
public static final int DEFAULT_MAX_SESSIONS = 128;
/**
* The default maximum number of transactions
*/
public static final int DEFAULT_MAX_TRANSACTIONS = 32;
private boolean lazyLoadingEnabled;
private boolean cacheModelsEnabled;
private boolean enhancementEnabled;
private int maxRequests = DEFAULT_MAX_REQUESTS;
private int maxSessions = DEFAULT_MAX_SESSIONS;
private int maxTransactions = DEFAULT_MAX_TRANSACTIONS;
private TransactionManager txManager;
private HashMap mappedStatements;
private HashMap cacheModels;
private HashMap resultMaps;
private HashMap parameterMaps;
private ThrottledPool requestPool;
private ThrottledPool sessionPool;
protected SqlExecutor sqlExecutor;
private TypeHandlerFactory typeHandlerFactory;
private DataExchangeFactory dataExchangeFactory;
private ResultObjectFactory resultObjectFactory;
private boolean statementCacheEnabled;
/**
* Default constructor
*/
public SqlMapExecutorDelegate() {
mappedStatements = new HashMap();
cacheModels = new HashMap();
resultMaps = new HashMap();
parameterMaps = new HashMap();
<span style="color:#ff0000;">requestPool = new ThrottledPool(RequestScope.class, DEFAULT_MAX_REQUESTS);//默认请求的池的大小是512</span>
<span style="color:#ff0000;"> sessionPool = new ThrottledPool(SessionScope.class, DEFAULT_MAX_SESSIONS);//默认session池的大小是128</span>
sqlExecutor = new SqlExecutor();
typeHandlerFactory = new TypeHandlerFactory();
dataExchangeFactory = new DataExchangeFactory(typeHandlerFactory);
}
public class ThrottledPool {
private Throttle throttle;
private Class type;
private List pool;
/**
* Create a ThrottledPool for a Class
* @param type - the type of objects being managed
* @param size - the size of the pool
*/
public ThrottledPool(Class type, int size) {
try {
this.throttle = new Throttle(size);
this.type = type;
this.pool = Collections.synchronizedList(new ArrayList(size));<span style="color:#ff0000;">//size是以tyep类型的对象的默认大小为准,例如requestPool的默认大小是512</span>
for (int i=0; i < size; i++) {
this.pool.add(type.newInstance());
}
} catch (Exception e) {
throw new RuntimeException("Error instantiating class. Cause: " + e, e);
}
}
/**
* Pop an object from the pool
* @return - the Object
*/
public Object pop() {
throttle.increment();
return pool.remove(0);<span style="color:#ff0000;">//池中的对象都是按照队列的形式保存,也就是以队列的形式处理,即先进先出</span>
}
public class Throttle {
private final Object LOCK = new Object();
private int count;
private int limit;
private long maxWait;
/**
* Create a throttle object with just a limit
* @param limit - the number of references to allow
*/
public Throttle(int limit) {
this.limit = limit;
this.maxWait = 0;
}
/**
* Create a throttle object with a limit and a wait time
* @param limit - the number of references to allow
* @param maxWait - the maximum wait time allowed for a reference
*/
public Throttle(int limit, long maxWait) {
this.limit = limit;
this.maxWait = maxWait;
}
/**
* Add a reference; if a reference is not available, an exception is thrown
*/
public void increment() {
<span style="color:#ff0000;">synchronized (LOCK) {</span>
long totalWaitTime = 0;
while (count >= limit) {
if (maxWait > 0) {
long waitTime = System.currentTimeMillis();
try {
LOCK.wait(maxWait - totalWaitTime);
} catch (InterruptedException e) {
//ignore
}
totalWaitTime += System.currentTimeMillis() - waitTime;
if (totalWaitTime > maxWait) {
throw new RuntimeException("Throttle waited too long (" + totalWaitTime + " milliseconds) for lock.");
}
} else {
try {
LOCK.wait();
} catch (InterruptedException e) {
//ignore
}
}
}
count++;
}
}
至此,是获取localSqlMapSession即SqlMapSessionImpl对象的流程。
回到最初的代码:
SqlMapClientImpl类:
public List queryForList(String id, Object paramObject) throws SQLException {
return getLocalSqlMapSession().queryForList(id, paramObject);
}
SqlMapSessionImpl类:
public List queryForList(String id, Object paramObject) throws SQLException {
return delegate.queryForList(session, id, paramObject);
}
SqlMapExecutorDelegate类:
/**
* Execute a query for a list
*
* @param session - the session scope
* @param id - the statement ID
* @param paramObject - the parameter object
* @return - the data list
* @throws SQLException - if the query fails
*/
public List queryForList(SessionScope session, String id, Object paramObject) throws SQLException {
return queryForList(session, id, paramObject, SqlExecutor.NO_SKIPPED_RESULTS, SqlExecutor.NO_MAXIMUM_RESULTS);
}
/**
<pre name="code" class="html" style="font-size: 14px; line-height: 25.2000007629395px;">SqlMapExecutorDelegate类:
* Execute a query for a list * * @param session - the session scope * @param id - the statement ID * @param paramObject - the parameter object * @param skip - the number of rows to skip * @param max - the maximum number of rows to return * @return - the data list * @throws SQLException - if the query fails */ public List queryForList(SessionScope session, String id, Object paramObject, int skip, int max) throws SQLException { List list = null; MappedStatement ms = getMappedStatement(id); Transaction trans = getTransaction(session); boolean autoStart = trans == null;//这边必须强调一下,之前很纠结,误读为autostart这个参数是false,其实这边是判断trans是否为Null,是null,z则为true,否则为false.
这里对于获取transaction很重要。因为是true的时候,就要去新生成这个session的transaction.
try {
trans = autoStartTransaction(session, autoStart, trans);
RequestScope request = popRequest(session, ms);
try {
list = ms.executeQueryForList(request, trans, paramObject, skip, max);
} finally {
pushRequest(request);
}
autoCommitTransaction(session, autoStart);
} finally {
autoEndTransaction(session, autoStart);
}
return list;
}
/**
* Get a transaction for the session
*
* @param session - the session
* @return - the transaction
*/
public Transaction getTransaction(SessionScope session) {
return session.getTransaction();
}
id是XML中定义SQL语句的名称,param是参数。用getMappedStatement方法,根据id来获取需要执行的MappedStatement,XML文件中所有定义的Statement都存储在一个叫mappedStatements的HashMap中,并以XML定义的名称(即Id)为key,MappedStatement对象为value,这样根据名称就取到了对应的MappedStatement。
然后根据session来获取Transaction,如下:
public Transaction getTransaction(SessionScope session) { return session.getTransaction(); }
很显然,在session初始化的时候就已经定义好了一个Transaction,但这个Transaction不一定有值。
public Transaction getTransaction(SessionScope session) { return session.getTransaction(); }
SqlMapExecutorDelegate类:
protected Transaction autoStartTransaction(SessionScope session, boolean autoStart, Transaction trans) throws SQLException {
Transaction transaction = trans;
if (autoStart) {
session.getSqlMapTxMgr().startTransaction();//<span style="font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2000007629395px;">getSqlMapTxMgr()实际上指向了SqlMapSessionImpl</span>
transaction = getTransaction(session);
}
return transaction;
}
SqlMapClientImpl类:
public void startTransaction() throws SQLException {
getLocalSqlMapSession().startTransaction();
}
SqlMapSesssionOmpl类:
public void startTransaction() throws SQLException {
delegate.startTransaction(session);
}
<pre name="code" class="html" style="font-size: 14px; line-height: 25.2000007629395px;">SqlMapExecutorDelegate类:
/**
* Start a transaction on the session with the specified isolation level.
*
* @param session - the session
* @throws SQLException - if the transaction could not be started
*/
public void startTransaction(SessionScope session, int transactionIsolation) throws SQLException {
try {
txManager.begin(session, transactionIsolation);
} catch (TransactionException e) {
throw new NestedSQLException("Could not start transaction. Cause: " + e, e);
}
}
TransactionManager类:
public void begin(SessionScope session, int transactionIsolation) throws SQLException, TransactionException {
Transaction trans = session.getTransaction();
TransactionState state = session.getTransactionState();
if (state == TransactionState.STATE_STARTED) {
throw new TransactionException("TransactionManager could not start a new transaction. " +
"A transaction is already started.");
} else if (state == TransactionState.STATE_USER_PROVIDED) {
throw new TransactionException("TransactionManager could not start a new transaction. " +
"A user provided connection is currently being used by this session. " +
"The calling .setUserConnection (null) will clear the user provided transaction.");
}
txThrottle.increment();
try {
trans = transactionConfig.newTransaction(transactionIsolation);
session.setCommitRequired(false);
} catch (SQLException e) {
txThrottle.decrement();
throw e;
} catch (TransactionException e) {
txThrottle.decrement();
throw e;
}
session.setTransaction(trans);
session.setTransactionState(TransactionState.STATE_STARTED);
}
SqlMapSessionImpl管理这一个SessionScope和一个SqlMapExecutorDelegate,而getSqlMapTxMgr()实际上指向了SqlMapSessionImpl,而SqlMapSessionImpl只是一个代理类,这时SqlMapSessionImpl的startTransaction()方法实际指向了SqlMapExecutorDelegate的startTransaction(Session)方法,因为SqlMapSessionImpl把持这SessionScope和SqlMapExecutorDelegate对象,所以很轻松把当前SqlMapSessionImpl中的SessionScope为参数调用SqlMapExecutorDelegate的startTransaction(Session)了,最后SqlMapExecutorDelegate调用自己的事务管理类TransactionManager把Session参数传进去,并做事务操作,startTransaction这时新建立一个Transaction对象,并把new出来的Transaction对象赋值给Session,且事务池+1,设定状态。
总结一下:SqlMapSessionImpl作为了SessionScope和SqlMapExecutorDelegate中间沟通的桥梁。
executeUpdate被封装在GeneralStatement对象里面,并其子类有DeleteStatement,SelectStatement,InsertStatement等,所有的SQL解析,执行,返回结果集都集中在这里。
最后提交事务:autoCommitTransaction(session, autoStart);如果autoStart为true,则执行session中的commitTransaction()方法,类似startTransaction()一样实际调用SqlMapExecutorDelegate的commitTransaction()方法,利用TransactionManager类的public void commit(SessionScope session)方法提交事务,如下:
- public void commit(SessionScope session) throws SQLException, TransactionException {
- Transaction trans = session.getTransaction();
- TransactionState state = session.getTransactionState();
- if (state == TransactionState.STATE_USER_PROVIDED) {
- } else if (state != TransactionState.STATE_STARTED && state != TransactionState.STATE_COMMITTED ) {
- throw new TransactionException("TransactionManager could not commit. No transaction is started.");
- }
- if (session.isCommitRequired() || forceCommit) {
- trans.commit();
- session.setCommitRequired(false);
- }
- session.setTransactionState(TransactionState.STATE_COMMITTED);
- }
总结一下:所有的事务控制都转接给公共SqlMapExecutorDelegate类中的TransactionManager类操作,TransactionManager自己维护着一个事务池,事务的新增和销毁都在这里集中管理。