连接数据库connection
写入prepareStatement
返回结果resultSet
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<configuration>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/labor_money?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mapper/WalletUnionconfigMapper.xml"/>
mappers>
configuration>
通过idea工具,找到对应的maven引用,直接点击下载就好了
package com.moonl.jvm.orm;
import com.moonl.jvm.beans.WalletUnionconfig;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisSrc {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//
SqlSession sqlSession = sqlSessionFactory.openSession();
WalletUnionconfig walletUnionconfig = sqlSession.selectOne("com.moonl.jvm.dao.WalletUnionconfigMapper.queryObject","2");
System.out.println(walletUnionconfig.getValues());
}
}
首先我们需要拿到,我们的mybatis的配置源文件名称,再对它进行inputStream转换
接着,我们通过SqlSessionFactoryBuilder对配置文件中的信息进行构建
构建过程:
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
environments和mappers两个节点
environments是对我们dataSource和transactionManager中的信息进行解析
mappers是对我们的定义的数据库持久化xml进行解析
mappers支持的三种方式 resource(默认方式)、url、class
这时,我们的SqlSessionFactoryBuilder步骤就已经完成了
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
从源代码中,我们可以看到,首先获取到刚解析并构建好的配置环境信息,再获取TransactionFactory 事务信息,再创建我们的Executor 执行者
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
ExecutorType也分为三种类型:SIMPLE(默认), REUSE(复用), BATCH(批量)
同时Mybatis是默认开启一级缓存的,具体是如何进行使用,我会在执行查询的过程中进行讲解
这里使用sqlSession中的selectOne进行演示
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
通过源码,我们可以看到我们经常查询一条数据时,遇到的TooManyResultsException就是这里抛出来的,遇到这个问题,不是因为我们的代码有问题,而是我们的查询语句中查询出来了多条数据,这个时候,应该去检查我们的数据表和我们的查询语句及查询条件。
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
selectList的源码如上所示,首先通过我们要查询的方法:
com.moonl.jvm.dao.WalletUnionconfigMapper.queryObject 获取我们对应的mapper.xml信息定义queryObject 查询
下面我们进入到executor的query方法中去
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
首先是和我们的查询参数相结合,翻译我们的查询语句BoundSql
格式:select id,name,sex from domo where id = ?
和我们使用jdbc进行查询的时候定义的语法格式一致。
第二步, 创建我们的一级缓存key ,createCacheKey 方法
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
源码中,使用如下6个参数,共同生成了我们的一级缓存key
id(执行请求的方法id)
offset(页码)
limit(行)
sql(转义后的sql)
value(查询条件)
environment(环境配置id)
接下来,我们进入到
query(ms, parameter, rowBounds, resultHandler, key, boundSql);
开始了我们查询
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
关键代码
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
通过刚才定义好的cacheKey,先查询我们的localCache中,我们的缓存是否存在,如果不存在的话,则进行数据库查询,也就是进入到我们的queryFromDatabase中方法中去。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
进入代码中后,首先看到的是 localCache.putObject(key, EXECUTION_PLACEHOLDER);
这段代码,将我们的key,首先放入我们的一级本地缓存中,value是一个占位符
再开始jdbc数据库查询。
接着是finally 将刚才的缓存key进行清理。
最后将我们的查询结果put到我们的localCache当中去。
上述的步骤就完成了mybatis对数据库的查询,同时将我们的查询结果默认的写入到了localCache当中去。
执行流程:
1,配置文件转inputStream
2,SqlSessionFactoryBuilder构建SqlSessionFactory
3,openSqlSession
4,Executor
4.1,StatementHandler
4.2, ResultSetHandler
4.3,LocalCache
5,返回结果