还是以第一篇博客中给出的例子,根据代码实例来入手分析。
1 static {
2 InputStream inputStream = MybatisTest.class.getClassLoader().getResourceAsStream("mybatis-configuration.xml");
3 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
4 }
5
6 /**
7 * 查询单个记录
8 */
9 @Test
10 public void testSelectOne() {
11 SqlSession session = sqlSessionFactory.openSession();
12 User user = session.selectOne(NAME_SPACE + ".selectUserById", 1);
13 System.out.println(user);
14 session.close();
15 }
如何加载配置文件前面也已经介绍了,通过配置文件产生SqlSessionFactory,追溯源码可以发现其实现是 DefaultSqlSessionFactory。
1 public SqlSessionFactory build(Configuration config) {
2 return new DefaultSqlSessionFactory(config);
3 }
得到 SqlSessionFactory 之后,就可以通过 SqlSessionFactory 去获取 SqlSession 对象。源码如下:
1 @Override
2 public SqlSession openSession() {
3 return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
4 }
5
6
7 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
8 Transaction tx = null;
9 try {
10 //Environment对象封装了配置文件中对于数据源和事务的配置
11 final Environment environment = configuration.getEnvironment();
12 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
13 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
14 //获取Executor对象,用来执行sql语句
15 final Executor executor = configuration.newExecutor(tx, execType);
16 return new DefaultSqlSession(configuration, executor, autoCommit);
17 } catch (Exception e) {
18 closeTransaction(tx); // may have fetched a connection so lets call close()
19 throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
20 } finally {
21 ErrorContext.instance().reset();
22 }
23 }
这里我们重点看一下第 15 行代码:
1 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
2 executorType = executorType == null ? defaultExecutorType : executorType;
3 executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
4 Executor executor;
5 if (ExecutorType.BATCH == executorType) {
6 executor = new BatchExecutor(this, transaction);
7 } else if (ExecutorType.REUSE == executorType) {
8 executor = new ReuseExecutor(this, transaction);
9 } else {
10 executor = new SimpleExecutor(this, transaction);
11 }
12 if (cacheEnabled) {
13 executor = new CachingExecutor(executor);
14 }
15 executor = (Executor) interceptorChain.pluginAll(executor);
16 return executor;
17 }
根据执行器类型这里有多种不同的执行器Executor。
注意第 12 行代码,如果我们开启了缓存,即 cacheEnabled = true(这里是一级缓存,默认是开启的),第13行代码使用了装饰器模式,在原有的 Executor 上装饰了缓存功能。
第 15 行用于设置插件。
这时候已经得到SqlSession对象了,实际类型是 DefaultSqlSession。接下来我们就可以通过该对象来执行sql语句了。
1、insert 操作
1 /**
2 * 插入一条记录
3 */
4 @Test
5 public void testInsert() {
6 SqlSession session = sqlSessionFactory.openSession();
7 User user = new User(2, "zhangsan", 22);
8 session.insert(NAME_SPACE + ".insertUser", user);
9 session.commit();
10 session.close();
11 }
通过第8行代码,我们进入到 insert 方法:
1 @Override
2 public int insert(String statement, Object parameter) {
3 return update(statement, parameter);
4 }
注意:这里通过 insert 方法,调用的是 update 方法。
1 public int update(String statement, Object parameter) {
2 try {
3 dirty = true;
4 MappedStatement ms = configuration.getMappedStatement(statement);
5 return executor.update(ms, wrapCollection(parameter));
6 } catch (Exception e) {
7 throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
8 } finally {
9 ErrorContext.instance().reset();
10 }
11 }
第4行根据给的statement参数,获取配置的所有如下信息,并将其封装到 MappedStatement 对象中,关于这个对象后面会详细介绍。
1 <!-- 向 user 表插入一条数据 -->
2 <insert id="insertUser" parameterType="com.ys.po.User" >
3 insert into
4 user(<include refid="Base_Column_List" />)
5 value(#{id,jdbcType=INTEGER},#{name,jdbcType=VARCHAR},#{age,jdbcType=INTEGER})
6 </insert>
①、接着我们看第 5 行代码,首先看 wrapCollection(parameter) 方法:
1 private Object wrapCollection(final Object object) {
2 if (object instanceof Collection) {
3 DefaultSqlSession.StrictMap<Object> map = new DefaultSqlSession.StrictMap<Object>();
4 map.put("collection", object);
5 if (object instanceof List) {
6 map.put("list", object);
7 }
8 return map;
9 } else if (object != null && object.getClass().isArray()) {
10 DefaultSqlSession.StrictMap<Object> map = new DefaultSqlSession.StrictMap<Object>();
11 map.put("array", object);
12 return map;
13 }
14 return object;
15 }
通过这段代码的if-else if 语句主要做了如下两个操作:
1、如果传入的参数是集合 Collection,在 map 集合中放入一个key为"collection"、value为参数的键值对,接着判断该集合是不是 List 类型,如果是,那么在 map 集合中在放入一个key为"list"、value为参数的键值对。
2、如果传入的参数是数组类型,那么在 map 中放入一个key为"array"、value为参数的键值对。
注意:这里的 StrictMap ,其实就是一个 HashMap。


1 public static class StrictMap<V> extends HashMap<String, V> {
2
3 private static final long serialVersionUID = -5741767162221585340L;
4
5 @Override
6 public V get(Object key) {
7 if (!super.containsKey(key)) {
8 throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());
9 }
10 return super.get(key);
11 }
12
13 }
14
15 }
View Code
②、wrapCollection(parameter) 方法介绍完了。接着我们看 executor.update()方法:
这里需要说明的是 Executor 对象上面我们已经介绍了,由于默认是开启一级缓存的,这时候我们进入 CachingExecutor 类的 update() 方法:
1 public int update(MappedStatement ms, Object parameterObject) throws SQLException {
2 flushCacheIfRequired(ms);
3 return delegate.update(ms, parameterObject);
4 }
首先我们看这里的第 2 行代码:
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
}
这里表示的意思是是否清除缓存。看我们是否在配置文件中配置了 <cache> 标签,以及我们是否在 <insert /> 标签中是否增加了 flushCache="true"属性。如果有其中任何一个,此次操作都会清除缓存。
接着我们再看第3行代码,这里的delegate 是 Executor,但是这是一个接口,其真实类型是SimpleExecutor,经过装饰器模式,调用 CachingExecutor 的 update 方法,经过处理后,最后最后调用 SimpleExecutor的update方法:
具体调用:
首先调用 BaseExecutor 的 update 方法
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);
}
然后调用 doUpdate 方法,由于 SimpleExecutor 继承 BaseExecutor 类,并重写了 doUpdate 方法,我们看 SimpleExecutor 类的 doUpdate 方法:
1 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
2 Statement stmt = null;
3 try {
4 Configuration configuration = ms.getConfiguration();
5 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
6 stmt = prepareStatement(handler, ms.getStatementLog());
7 return handler.update(stmt);
8 } finally {
9 closeStatement(stmt);
10 }
11 }
看到这里,Statement 对象,看到我们熟悉的 JDBC 操作数据库的对象了吧。我们直接看第 6 行代码:
1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
2 Statement stmt;
3 Connection connection = getConnection(statementLog);
4 stmt = handler.prepare(connection, transaction.getTimeout());
5 handler.parameterize(stmt);
6 return stmt;
7 }
第 3 行代码获取数据库连接,是根据前面配置的数据源来获取。接着我们看 handler.update(stemt) 方法:
1 public int update(Statement statement) throws SQLException {
2 String sql = boundSql.getSql();
3 Object parameterObject = boundSql.getParameterObject();
4 KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
5 int rows;
6 if (keyGenerator instanceof Jdbc3KeyGenerator) {
7 statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
8 rows = statement.getUpdateCount();
9 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
10 } else if (keyGenerator instanceof SelectKeyGenerator) {
11 statement.execute(sql);
12 rows = statement.getUpdateCount();
13 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
14 } else {
15 statement.execute(sql);
16 rows = statement.getUpdateCount();
17 }
18 return rows;
19 }
这里就都是我们熟悉的 JDBC 操作了。
2、update 和 delete 操作
1 /**
2 * 更新一条记录
3 */
4 @Test
5 public void testUpdate() {
6 SqlSession session = sqlSessionFactory.openSession();
7 User user = new User(2, "lisi", 22);
8 session.update(NAME_SPACE + ".updateUserById", user);
9 session.commit();
10 session.close();
11 }
12
13 /**
14 * 删除一条记录
15 */
16 @Test
17 public void testDelete() {
18 SqlSession session = sqlSessionFactory.openSession();
19 session.delete(NAME_SPACE + ".deleteUserById", 2);
20 session.commit();
21 session.close();
22 }
进入到上述第 8 行和 第 19 行代码,我们发现都是进入到和 上面 insert 操作一样的代码:
第 8 行:
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
第 19 行:
@Override
public int delete(String statement, Object parameter) {
return update(statement, parameter);
}
之后的 update 也是上面的代码。这也和我们理解的应该保持一致。
结论:
insert、update、delete都是属于对数据库的行进行更新操作
所以这三种语句的执行都是采用的同种逻辑处理。最终都可以调用 executeUpdate() 方法来处理。唯一不同的是 select 操作,必须要调用 executeQuery() 来执行。
3、select 操作
1 /**
2 * 查询单个记录
3 */
4 @Test
5 public void testSelectOne() {
6 SqlSession session = sqlSessionFactory.openSession();
7 User user = session.selectOne(NAME_SPACE + ".selectUserById", 1);
8 System.out.println(user);
9 session.close();
10 }
11
12 /**
13 * 查询多个记录
14 */
15 @Test
16 public void testSelectList() {
17 SqlSession session = sqlSessionFactory.openSession();
18 List<User> listUser = session.selectList(NAME_SPACE + ".selectUserAll");
19 if (listUser != null) {
20 System.out.println(listUser.size());
21 }
22 session.close();
23 }
首先看第 7 行代码:
1 public <T> T selectOne(String statement, Object parameter) {
2 // Popular vote was to return null on 0 results and throw exception on too many.
3 List<T> list = this.<T>selectList(statement, parameter);
4 if (list.size() == 1) {
5 return list.get(0);
6 } else if (list.size() > 1) {
7 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
8 } else {
9 return null;
10 }
11 }
看到上面的第 3 行代码,我们可能马上就明白了,其实selectOne() 和 selectList() 也都是调用的 selectList() 方法,只不过 selectOne() 是获取集合的第一个元素而已。
接着看 selectList() 源码:
1 @Override
2 public <E> List<E> selectList(String statement, Object parameter) {
3 return this.selectList(statement, parameter, RowBounds.DEFAULT);
4 }
5
6 @Override
7 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
8 try {
9 MappedStatement ms = configuration.getMappedStatement(statement);
10 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
11 } catch (Exception e) {
12 throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
13 } finally {
14 ErrorContext.instance().reset();
15 }
16 }
看第10的 query 方法:
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
2 BoundSql boundSql = ms.getBoundSql(parameterObject);
3 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
4 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
5 }
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
2 throws SQLException {
3 Cache cache = ms.getCache();
4 if (cache != null) {
5 flushCacheIfRequired(ms);
6 if (ms.isUseCache() && resultHandler == null) {
7 ensureNoOutParams(ms, parameterObject, boundSql);
8 @SuppressWarnings("unchecked")
9 List<E> list = (List<E>) tcm.getObject(cache, key);
10 if (list == null) {
11 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
12 tcm.putObject(cache, key, list); // issue #578 and #116
13 }
14 return list;
15 }
16 }
17 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
18 }
最后我们来到doQuery() 方法:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
1 @Override
2 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
3 PreparedStatement ps = (PreparedStatement) statement;
4 ps.execute();
5 return resultSetHandler.<E> handleResultSets(ps);
6 }
至此,select 操作也执行完毕了。
本文版权归作者所有,欢迎转载,但未经作者同意不能转载,否则保留追究法律责任的权利。