SqlMapClient是线程安全的

Ibatis做为一个半自动化的Orm框架有他的缺点和优点。在这里我就不宽泛的说这些了。就说说为什么SqlMapClient是线程安全的,他是怎么实现的。

提出问题:

 private static SqlMapClient sqlMapper;    

/**
* It's not a good idea to put code that can fail in a class initializer,
* but for sake of argument, here's how you configure an SQL Map.
*/
static {
try {
Reader reader = Resources.getResourceAsReader("com/mydomain/data/SqlMapConfig.xml");
sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
} catch (IOException e) {
// Fail fast.
throw new RuntimeException("Something bad happened while building the SqlMapClient instance." + e, e);
}
}



这是一段ibatis simple工程的代码,大家都能看明白这是一个单例,只有一个SqlMapClient对象存在,在多线程的情况下,SqlMapClient是怎么解决事务隔离呢,怎么共享资源的呢?
[newpage]
一、 SqlMapClient是怎么被创建的
打开SqlMapClientBuilder发现buildSqlMapClien一句话

public static SqlMapClient buildSqlMapClient(Reader reader) {    
// return new XmlSqlMapClientBuilder().buildSqlMap(reader);
return new SqlMapConfigParser().parse(reader);
}


我们顺着这条线一路看下去
SqlMapConfigParser类的做了两件事把reader交个一个NodeletParser去解析reader(也就是我们的配置文件),在一个就是XmlParserState的一个属性产生一个SqlMapClient对象

public class SqlMapConfigParser {    

protected final NodeletParser parser = new NodeletParser();
private XmlParserState state = new XmlParserState();
public SqlMapClient parse(Reader reader) {
try {
usingStreams = false;

parser.parse(reader);
return state.getConfig().getClient();
} catch (Exception e) {
throw new RuntimeException("Error occurred. Cause: " + e, e);
}
}


打开NodeletParser的parse方法,我们发现他就是解析xml配置文件的

public void parse(Reader reader) throws NodeletException {    
try {
Document doc = createDocument(reader);
parse(doc.getLastChild());
} catch (Exception e) {
throw new NodeletException("Error parsing XML. Cause: " + e, e);
}
}



最后这些文件被分门别类的放在了XmlParserState的这些属性里

private SqlMapConfiguration config = new SqlMapConfiguration();    

private Properties globalProps = new Properties();
private Properties txProps = new Properties();
private Properties dsProps = new Properties();
private Properties cacheProps = new Properties();
private boolean useStatementNamespaces = false;
private Map sqlIncludes = new HashMap();

private ParameterMapConfig paramConfig;
private ResultMapConfig resultConfig;
private CacheModelConfig cacheConfig;

private String namespace;
private DataSource dataSource;



现在我们回过头看return state.getConfig().getClient();
是这句话获得了SqlMapClient对象,这个对象是怎么创建的呢,在SqlMapConfiguration的构造方法里面就已经创建好了。


public SqlMapConfiguration() {    
errorContext = new ErrorContext();
delegate = new SqlMapExecutorDelegate();
typeHandlerFactory = delegate.getTypeHandlerFactory();
client = new SqlMapClientImpl(delegate);
registerDefaultTypeAliases();
}


原来我们的到的并不是SqlMapClient(接口不能实现)对象,而是他的一个实现SqlMapClientImpl

二、 深入SqlMapClientImpl内部
SqlMapClientImpl类中只有三个字段


  private static final Log log = LogFactory.getLog(SqlMapClientImpl.class);    

public SqlMapExecutorDelegate delegate;

protected ThreadLocal localSqlMapSession = new ThreadLocal();


log是一个日志记录的对象,与线程安全肯定是无关的
SqlMapExecutorDelegate这个类里面有什么东西呢

private static final Probe PROBE = ProbeFactory.getProbe();    

private boolean lazyLoadingEnabled;
private boolean cacheModelsEnabled;
private boolean enhancementEnabled;
private boolean useColumnLabel = true;
private boolean forceMultipleResultSetSupport;

private TransactionManager txManager;

private HashMap mappedStatements;
private HashMap cacheModels;
private HashMap resultMaps;
private HashMap parameterMaps;

protected SqlExecutor sqlExecutor;
private TypeHandlerFactory typeHandlerFactory;
private DataExchangeFactory dataExchangeFactory;

private ResultObjectFactory resultObjectFactory;
private boolean statementCacheEnabled;

这些属性都是一些关于跟sqlMap配置的一些信息,这些信息和线程安全也没有很大的关系。

最后就剩下localSqlMapSession字段了,其实有经验的同学一眼就能看出来这点的,ThreadLocal就是为处理线程安全而来的,他的实质为每个线程保存一个副本。他的实现就是存在一个全局的Map存放localSqlMapSession,key是线程的id号value值是一个localSqlMapSession的副本。
SqlMapClientImpl里面的方法:

Java代码
public Object insert(String id, Object param) throws SQLException {
return getLocalSqlMapSession().insert(id, param);
}

public Object insert(String id) throws SQLException {
return getLocalSqlMapSession().insert(id);
}

public int update(String id, Object param) throws SQLException {
return getLocalSqlMapSession().update(id, param);
}

public int update(String id) throws SQLException {
return getLocalSqlMapSession().update(id);
}

public int delete(String id, Object param) throws SQLException {
return getLocalSqlMapSession().delete(id, param);
}

public int delete(String id) throws SQLException {
return getLocalSqlMapSession().delete(id);
}

public Object queryForObject(String id, Object paramObject) throws SQLException {
return getLocalSqlMapSession().queryForObject(id, paramObject);
}

多么熟悉的方法啊,这就是我们经常用的curd的方法。从代码上证明了我们的推测,线程安全就是和localSqlMapSession有关
虽然找到了相关的属性,但是他们是怎么实现的呢。
三、 线程安全的实现。
就dao部分的线程安全来说一个是主要是事务的完成性。如果事务能够保证完整性,那么就可以说是线程安全的。
localSqlMapSession存的是什么什么东西呢,我们打开代码看看。

protected SqlMapSessionImpl getLocalSqlMapSession() {    
SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get();
if (sqlMapSession == null || sqlMapSession.isClosed()) {
sqlMapSession = new SqlMapSessionImpl(this);
localSqlMapSession.set(sqlMapSession);
}
return sqlMapSession;
}


再研究一下SqlMapSessionImpl,这个类只有三个字段
protected SqlMapExecutorDelegate delegate;
protected SessionScope sessionScope;
protected boolean closed;

很明显SessionScope这是我们要找的东西


private static long nextId;    
private long id;
// Used by Any
private SqlMapClient sqlMapClient;
private SqlMapExecutor sqlMapExecutor;
private SqlMapTransactionManager sqlMapTxMgr;
private int requestStackDepth;
// Used by TransactionManager
private Transaction transaction;
private TransactionState transactionState;
// Used by SqlMapExecutorDelegate.setUserProvidedTransaction()
private TransactionState savedTransactionState;
// Used by StandardSqlMapClient and GeneralStatement
private boolean inBatch;
// Used by SqlExecutor
private Object batch;
private boolean commitRequired;
private Map preparedStatements
;


根据我们的分析事务的完整性足以保证dao层的线程安全。Transaction保存在ThreadLocal里面证明了SqlMapClient是线程安全的,我们在整个工程中只要一个SqlMapClient对象就够了。

再来看下SessionScope这个类的字段
private SqlMapClient sqlMapClient;保存的是一个SqlMapClient 
private SqlMapExecutor sqlMapExecutor; 执行sql用的
private SqlMapTransactionManager sqlMapTxMgr; 管理事务的
private int requestStackDepth;
// Used by TransactionManager
private Transaction transaction; 事务
private TransactionState transactionState; 事务的状态
// Used by SqlMapExecutorDelegate.setUserProvidedTransaction()
private TransactionState savedTransactionState; 事务的保存状态
// Used by StandardSqlMapClient and GeneralStatement
private boolean inBatch;是否批处理
// Used by SqlExecutor
private Object batch;
private boolean commitRequired;是否用提交
private Map preparedStatements;这个应该是保存批处理的PreparedStatement


我们突然发现没有连接类Connection,如果用jdbc的话Connection是多么重要的一个对象啊,在这里没有保存Connection呢。打开JdbcTransaction(一个Transaction的实现)


private static final Log connectionLog = LogFactory.getLog(Connection.class);    

private DataSource dataSource;
private Connection connection;
private IsolationLevel isolationLevel = new IsolationLevel();

public JdbcTransaction(DataSource ds, int isolationLevel) throws TransactionException {
// Check Parameters
dataSource = ds;
if (dataSource == null) {
throw new TransactionException("JdbcTransaction initialization failed. DataSource was null.");
}
this.isolationLevel.setIsolationLevel(isolationLevel);
}

private void init() throws SQLException, TransactionException {
// Open JDBC Transaction
connection = dataSource.getConnection();
if (connection == null) {
throw new TransactionException("JdbcTransaction could not start transaction. Cause: The DataSource returned a null connection.");
}
// Isolation Level
isolationLevel.applyIsolationLevel(connection);
// AutoCommit
if (connection.getAutoCommit()) {
connection.setAutoCommit(false);
}
// Debug
if (connectionLog.isDebugEnabled()) {
connection = ConnectionLogProxy.newInstance(connection);
}
}

public void commit() throws SQLException, TransactionException {
if (connection != null) {
connection.commit();
}
}

public void rollback() throws SQLException, TransactionException {
if (connection != null) {
connection.rollback();
}
}

public void close() throws SQLException, TransactionException {
if (connection != null) {
try {
isolationLevel.restoreIsolationLevel(connection);
} finally {
connection.close();
connection = null;
}
}
}

public Connection getConnection() throws SQLException, TransactionException {
if (connection == null) {
init();
}
return connection;
}


原来Connection在这里保存着呢,事务的提交,回滚也是在这里实现的。

到这里大致明白了,ibatis为每一个操作SqlMapClient的线程建立一个SessionScope对象,这里面保存了Transaction,Connection,要执行的PreparedStatement。
SqlMapClient对象里面保存的是全局有关的缓存策略,ParameterMap,ResultMap,jdbc到Java对象的类型转换,别名等信息。


在每个执行的Statement中还有一个StatementScope,这里保存的是每个执行语句的状态。这里就不看了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值