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); } } public static SqlMapClient buildSqlMapClient(Reader reader) { // return new XmlSqlMapClientBuilder().buildSqlMap(reader); return new SqlMapConfigParser().parse(reader); } 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); } } 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); } } 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;
public SqlMapConfiguration() { errorContext = new ErrorContext(); delegate = new SqlMapExecutorDelegate(); typeHandlerFactory = delegate.getTypeHandlerFactory(); client = new SqlMapClientImpl(delegate); registerDefaultTypeAliases(); } private static final Log log = LogFactory.getLog(SqlMapClientImpl.class); public SqlMapExecutorDelegate delegate; protected ThreadLocal localSqlMapSession = new ThreadLocal();
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); } protected SqlMapSessionImpl getLocalSqlMapSession() { SqlMapSessionImpl sqlMapSession = (SqlMapSessionImpl) localSqlMapSession.get(); if (sqlMapSession == null || sqlMapSession.isClosed()) { sqlMapSession = new SqlMapSessionImpl(this); localSqlMapSession.set(sqlMapSession); } return sqlMapSession; } 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;
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
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; }