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,这里保存的是每个执行语句的状态。这里就不看了。
分享到:
相关推荐
SqlMapClient的设计模式类似于Hibernate中的SessionFactory,推荐作为单例进行实例化,以确保线程安全。 1. **SqlMapClient的初始化**: 初始化SqlMapClient通常在应用程序的启动阶段完成。首先,我们需要一个配置...
由于`SqlMapClient`是线程安全的,因此在实际应用中,通常会将其作为单例模式来创建和使用,类似于Hibernate中的`SessionFactory`。 初始化`SqlMapClient`对象通常需要一个配置文件,这个配置文件包含了数据源、...
SqlMapClient是一个线程安全的客户端对象,用于执行SQL语句。在批处理操作中,需要使用SqlMapClient的startTransaction()方法来启动事务,startBatch()方法来启动批处理,然后执行批处理操作,最后使用executeBatch...
若由iBATIS自身管理事务,则多个`Statement`执行可共享同一`SqlMapSession`实例,保证线程安全。外部程序管理则需自行控制`SqlMapSession`的生命周期。 在实际应用中,iBATIS常与Spring框架集成。Spring通过其`...
3. **SqlMapSession**:在 .NET 版本中,SqlMapSession 类代表一个数据库会话,它是线程安全的,用于执行多个数据库操作。每次数据库操作后,都应关闭 SqlMapSession,以释放资源。 4. **数据映射**:iBATIS 支持将...
Struts2为每个请求创建一个新的Action实例,简化了线程安全问题。 3. **Servlet依赖**:Struts1的Action直接依赖于Servlet API,而Struts2的Action更加独立,减少了对底层Servlet容器的依赖,提高了可测试性和可移植...
它是线程安全的,可以在应用的整个生命周期中复用。 3. **SqlSession**:SqlSession代表一次数据库会话,提供了执行SQL语句和操作数据库的方法。每次操作数据库后,应确保关闭SqlSession以释放资源。 4. **Mapper...
在基础语义部分,会介绍如何使用`XmlSqlMapClientBuilder`来构建`SqlMapClient`对象,它是一个线程安全的API接口,用于执行SQL语句和管理数据库事务。 #### OR映射(对象关系映射) iBatis 中的OR映射是指对象与...
4. **SqlSessionManager**:用于管理SqlSession的类,提供了线程安全的会话管理机制。 #### 三、ibatis与JDBC的比较 - **JDBC**:Java数据库连接标准,提供了基本的数据库访问接口,但在实际使用中需要开发者自行...
- **Multi-Threaded Programming**:支持多线程编程,确保并发访问数据库的安全性。 - **Batches**:批量执行SQL语句,提高执行效率。 - **Executing Statements via the SqlMapClient API**:通过SqlMapClient ...
9.2.5 Spring使用ThreadLocal解决线程安全问题 9.3 Spring对事务管理的支持 9.3.1 事务管理关键抽象 9.3.2 Spring的事务管理器实现类 9.3.3 事务同步管理器 9.3.4 事务传播行为 9.4 编程式的事务管理 9.5 使用XML...
9.2.5 Spring使用ThreadLocal解决线程安全问题 9.3 Spring对事务管理的支持 9.3.1 事务管理关键抽象 9.3.2 Spring的事务管理器实现类 9.3.3 事务同步管理器 9.3.4 事务传播行为 9.4 编程式的事务管理 9.5 使用XML...