什么是MyBatis?
MyBatis是一种持久化的ORM(Object Relational Mapping)框架。什么是ORM?用于实现面向对象编程语言中,不同系统的数据类型之间数据的转换。
就比如说,Java中有一个User类,MySQL中有一个user表,MyBatis就实现了User类和user表的映射(也就是Java实体类和user表对应,同时Java类中的每个属性和user表中的每一列对应)。
MyBatis还支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis快速入门
运行mybatis源码项目
为了便于更好分析源码,我们这里直接下载了MyBatis-3的源码,直接在MyBatis源码工程编写demo调试。
下载完成后,解压后,然后IDEA打开。
接下来就是Maven导入jar包。maven导入完成后,然后我们这时候发现项目是不可运行的,因为没有设置编译后classes文件存放目录,点击File-Project Structure
设置Project compiler output
这时候mybatis源码文件就可以运行了。
编写简单入门案例
一般的普通项目都是会导入mybatis的jar包
但是我们这里是直接使用的mybatis源码,所有不需要导入mybatis的jar包。
然后我们在pom.xml文件导入mysql-connector的jar包的依赖
<!--mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
目录如下
注意:这里要修改以下java目录类型(改为Sources)和resoures目录类型(改为Resource),
打开Project Strucre - Modules,点击目录-mark as Sources/Tests/Resources/…
然后我们编写mybatis的配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<settings>
<!--打开延迟加载的开关-->
<setting name= "lazyLoadingEnabled" value= "true"/>
<!--将积极加载改为按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 可以阻挡不相干的操作触发,实现懒加载-->
<setting name="lazyLoadTriggerMethods" value=""/>
</settings>
<!--配置所有实体类的别名-->
<typeAliases>
<!--<typeAlias type="org.lieying.test.bean.User" alias="User"/>-->
<!--自动扫描包下所有有@Alias注解的类-->
<typeAlias alias="User" type="org.apache.demo.User"/>
</typeAliases>
<environments default="development">
<environment id="development">
<!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 -->
<transactionManager type="JDBC" />
<!-- mybatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI -->
<!-- POOLED 表示支持JDBC数据源连接池 -->
<!-- UNPOOLED 表示不支持数据源连接池 -->
<!-- JNDI 表示支持外部数据源连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
编写一个User类
package org.apache.demo;
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
编写UserMapper.java文件
package org.apache.demo;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
//@Select("select * from user where id = #{id}")
User selectById(int id);
}
编写UserMapper.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.demo.UserMapper">
<select id="selectById" resultType="User">
select * from user where id = #{id}
</select>
</mapper>
编写jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testspring?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
jdbc.username=root
jdbc.password=200934652qwe
编写测试类
package org.apache.demo;
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 TestMybatis {
public static void main(String[] args) throws IOException {
//把文件放入输入流中
InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
//构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//获取一个SqlSession
SqlSession sqlSession=sqlSessionFactory.openSession();
//执行查询方法
User user= sqlSession.selectOne("org.apache.demo.UserMapper.selectById",1);
System.out.println(user);
// UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
// System.out.println(userMapper.selectById(1));
}
}
然后运行测试类。
发现运行成功,即我们这里成功的使用了mybatis从MySQL数据库查询了id=1的user。
MyBatis执行流程
我们首先要获取数据源,这里的数据源我们配置在mybatis-config.xml文件中
现在前面配置了jdbc.properties中的driver、url、username、password
那么mybatis是如何解析的呢?
我们在测试类设置断点
进入到build方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
然后通过parse.parse()方法解析,解析后返回一个Configuration对象,然后通过build()方法返回一个SqlSessionFactory对象。
我们看一下parse()方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));//解析mybatis-config.xml中的configuration标签,因此该方法就是解析mybatis-config.xml文件
return configuration;
}
进入parseConfiguration方法,我们发现这里又很多解析不同结点的方法,我们发现这里的结点名字和我们mybatis-config.xml文件的名字一致且顺序一致。
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);
}
}
我们先看看我们配置的jdbc.properties是如何解析的,进入propertiesElement方法
private void propertiesElement(XNode context) throws Exception {
if (context != null) {//结点不为空,也就是properties标签存在
Properties defaults = context.getChildrenAsProperties();//
String resource = context.getStringAttribute("resource");//通过resource的方式设置的properties文件路径
String url = context.getStringAttribute("url");//通过url的方式设置的properties文件路径
if (resource != null && url != null) {//resources和url只能同时存在一种
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {//当配置的是resource的时候
defaults.putAll(Resources.getResourceAsProperties(resource));//把resource配置的文件的内容,加到defaults中,获取文件所有的name-value,我们在propertis配置的就是driver=xxx的方式,此方法把这些name value就是map的形式,properties实现了hashtable接口,因此改方法就是把jdbc.properties文件在配置的每一项存在一个hasbtable里面
} else if (url != null) {//当配置的是url的时候
defaults.putAll(Resources.getUrlAsProperties(url));//通过urlf方式
}
//执行完以上操作,我们就完成了获取了propoerties标签配置的文件/url,并把所有内容加入到了defaults里面
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);//为解析器设置variables,这时候解析器就获得了这些properties文件在的这些与数据库连接相关的属性
configuration.setVariables(defaults);//configuration设置variables
}
}
然后我们看environments标签的解析,environmentsElement方法
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");//获取属性为default的environments标签
}
for (XNode child : context.getChildren()) {//遍历environments,即在这里解析不同环境下的配置的数据源
String id = child.getStringAttribute("id");//获取environment的id
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//获取TransactionFactory类型根据transactionManager标签
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//获取DataSourceFactory 类型根据dataSource标签
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);//以当前事务管理器和数据源build一个environmentBuilder
configuration.setEnvironment(environmentBuilder.build());//设置当前环境
}
}
}
}
然后parse就完成了,pase方法返回了一个Configuration对象
build(parser.parse())
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
然后就通过build方法构建了一个SqlSessionFactory
然后我们在执行sqlSessionFactory的openSession方法,
进入openSession方法
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
进入openSessionFromDataSource方法
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();
}
}
通过这种方法就获取到了一个SqlSession对象
然后我们执行selectOne方法获取id为1的用户
User user= sqlSession.selectOne("org.apache.demo.UserMapper.selectById",1);
然后进入到DefaultSqlSession的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);//执行selectList方法
if (list.size() == 1) {//如果查询行数为1
return list.get(0);//返回查询到的记录
} else if (list.size() > 1) {//如果查询行数大于1
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());//抛出异常
} else {
return null;
}
}
执行selectList方法后,我们发现控制台多了以下内容
因此,通过selectList方法,我们连接了数据库,并执行了select * from user where id=1这条sql语句。
这里我们重点看selectList方法。
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@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();
}
}
进入CachingExecutor的query方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
进入BaseExecutor的query方法
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) {//如果Executor已经关闭
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;
}
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;
}
执行SimpleExecutor的doQuery方法
@Override
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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
进入Configuration的newStatementHandler方法
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
进入RoutingStatementHandler的构造
方法
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
这里是Prepared,因此进入new一个PreparedStatementHandler对象,
进入PreparedStatementHandler的构造方法
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
进入PreparedStatementHandler的父类BaseStatementHandler的构造方法
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
然后看下Configuration类的getTypeHandlerRegistry方法
public TypeHandlerRegistry getTypeHandlerRegistry() {
return typeHandlerRegistry;
}
看下Configuration类的typeHandlerRegistry属性,发现该属性是一个final属性(不可变),默认new了一个TypeHandlerRegistry 对象,
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
我们看一下它的构造方法
public TypeHandlerRegistry(Configuration configuration) {
this.unknownTypeHandler = new UnknownTypeHandler(configuration);
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new StringTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, unknownTypeHandler);
register(Object.class, JdbcType.OTHER, unknownTypeHandler);
register(JdbcType.OTHER, unknownTypeHandler);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
register(Instant.class, new InstantTypeHandler());
register(LocalDateTime.class, new LocalDateTimeTypeHandler());
register(LocalDate.class, new LocalDateTypeHandler());
register(LocalTime.class, new LocalTimeTypeHandler());
register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
register(OffsetTime.class, new OffsetTimeTypeHandler());
register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
register(Month.class, new MonthTypeHandler());
register(Year.class, new YearTypeHandler());
register(YearMonth.class, new YearMonthTypeHandler());
register(JapaneseDate.class, new JapaneseDateTypeHandler());
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
然后进入register方法
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
register((Type) javaType, typeHandler);
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
register(javaType, null, typeHandler);
}
} else {
register(javaType, null, typeHandler);
}
}
发现TypeHandlerRegistry构造方法就是做了java类/类型 到数据库字段 的映射(handler),也就是每个java类型注册一个handler,比如我们看一个BooleanTypeHandler方法
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType)
throws SQLException {
ps.setBoolean(i, parameter);
}
然后我们看见了我们常用的PreparedStatement类,进入setBoolean方法
这不就是我们常用的Preparedtatement方法吗。
执行完 this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();后就获得了typeHandlerRegistry
然后我们继续刚才的BaseStatementHandler方法
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//获取mappedStatement、executor、rowBounds
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);//创建参数处理器
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);//创建结果集处理器
}
然后我们就获得了一个StatementHandler,回到刚才的Configuration的newStatementHandler方法
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();//获取configuration
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);//创建statementHandler
stmt = prepareStatement(handler, ms.getStatementLog());//获取statement
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
我们看一下preparedStatement方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
然后看一下getConnection方法
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
我们发现控制台打印出JDBC连接成功的日志
然后我们查看getConnection()方法,该方法打印了日志
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommit);
}
查看getConnection方法,
@Override
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();//先执行popConnection获取一个PooledConnection,然后获取它的代理连接getProxyConnection
}
查看popConnection方法,此方法 通过 conn = new PooledConnection(dataSource.getConnection(), this);获取连接,成功会打印日志
PooledConnection的构造方法,
```java
public PooledConnection(Connection connection, PooledDataSource dataSource) {
this.hashCode = connection.hashCode();
this.realConnection = connection;
this.dataSource = dataSource;
this.createdTimestamp = System.currentTimeMillis();
this.lastUsedTimestamp = System.currentTimeMillis();
this.valid = true;
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);//通过jdk动态代理的创建一个代理连接
}
```java
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
while (conn == null) {
synchronized (state) {
if (!state.idleConnections.isEmpty()) {
// Pool has available connection
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// Pool does not have available connection
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// Can create new connection
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {//如果日志debug级别开启
log.debug("Created connection " + conn.getRealHashCode() + ".");
}//创建连接xxxxx
} else {
// Cannot create new connection
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// Can claim overdue connection
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
state.activeConnections.remove(oldestActiveConnection);
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
/*
Just log a message for debug and continue to execute the following
statement like nothing happened.
Wrap the bad connection with a new PooledConnection, this will help
to not interrupt current executing thread and give current thread a
chance to join the next competition for another valid/good database
connection. At the end of this loop, bad {@link @conn} will be set as null.
*/
log.debug("Bad connection. Could not roll back");
}
}
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// Must wait
try {
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
if (conn != null) {
// ping to server and check the connection is valid or not
if (conn.isValid()) {
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
state.activeConnections.add(conn);
state.requestCount++;
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
state.badConnectionCount++;
localBadConnectionCount++;
conn = null;
if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}
}
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
return conn;
}
然后我们看一下dataSource.getConnection方法,
@Override
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);//传递用户名和密码
}
private Connection doGetConnection(String username, String password) throws SQLException {
Properties props = new Properties();//创建一个Properties类,
if (driverProperties != null) {
props.putAll(driverProperties);
}
if (username != null) {//把用户名传进去
props.setProperty("user", username);
}
if (password != null) {//把密码传进去
props.setProperty("password", password);
}
return doGetConnection(props);
}
进入doGetConnection方法
private Connection doGetConnection(Properties properties) throws SQLException {
initializeDriver();//初始化驱动
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);//调用DriverManager.getConnection方法获取连接
return connection;
}
这时候终于找到了我们使用jdbc连接数据库常用的DriverManager方法,
继续回到JdbcTransaction类的openConnection方法
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {//日志的debug级别开启
log.debug("Opening JDBC Connection");//打印 Opening JDBC Connection
}
connection = dataSource.getConnection();
if (level != null) {//事务隔离级别不为空
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommit);//设置事务是否自动提交,openSession方法设置默认不会自动提交
}
setDesiredAutoCommit方法(Connection类)
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if (connection.getAutoCommit() != desiredAutoCommit) {
if (log.isDebugEnabled()) {
log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException e) {
// Only a very poorly implemented driver would fail here,
// and there's not much we can do about that.
throw new TransactionException("Error configuring AutoCommit. "
+ "Your driver may not support getAutoCommit() or setAutoCommit(). "
+ "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e);
}
}
回到JdbcTransaction的getConnection方法
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
回到BaseExecutor的getConnection方法
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();//获取连接
if (statementLog.isDebugEnabled()) {//如果开启debug级别日志
return ConnectionLogger.newInstance(connection, statementLog, queryStack);//使的该连接增加日志打印
} else {
return connection;
}
}
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {//创建一个代理对象,为连接做日志功能,代理的目的就是增强,这里就是对连接过程中的操作进行日志打印
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);//jdk动态代理
}
通过openConnection方法成功的获取了一个连接
回到preparedStatement方法(SimpleExecutor类)
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
进入RoutingStatementHandler的prepare方法
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
进入BaseStatementHandler的prepare方法
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
回到SimpleHandler的prepareStatement方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);//获取连接
stmt = handler.prepare(connection, transaction.getTimeout());//获取statement
handler.parameterize(stmt);
return stmt;
}
进入parameterize方法(RoutingStatementHandler类)
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
进入parameterize方法(PreparedStatementHandler类)
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
进入 DefaultParameterHandler的setParameters方法,为statement设置参数
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
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);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
回到SimpleHandler的doQuery方法
@Override
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();//获取configuration
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);//创建statementHandler
stmt = prepareStatement(handler, ms.getStatementLog());//获取statement
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
执行RoutingStatementHandler的query方法
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.query(statement, resultHandler);
}
执行PreparedStatementHandler的query方法
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;//获取statement方法
ps.execute();//
return resultSetHandler.handleResultSets(ps);
}
然后进入execute方法,这时候我们F7调试发现进入的不是execute方法,是PreparedStatementLogger的invoke方法,因为PreparedStatementLogger实现了InvocationHandler,然后我们看一下invoke方法
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if (EXECUTE_METHODS.contains(method.getName())) {//EXECUTE_METHODS包含execute方法
if (isDebugEnabled()) {//设置debug日志级别
debug("Parameters: " + getParameterValueString(), true);//打印参数
}
clearColumnInfo();//清除列信息
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);//通过反射机制调用execute方法
}
} else if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
} else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
BaseJdbcLogger中
protected void debug(String text, boolean input) {
if (statementLog.isDebugEnabled()) {
statementLog.debug(prefix(input) + text);
}
}
static {
SET_METHODS = Arrays.stream(PreparedStatement.class.getDeclaredMethods())
.filter(method -> method.getName().startsWith("set"))
.filter(method -> method.getParameterCount() > 1)
.map(Method::getName)
.collect(Collectors.toSet());
EXECUTE_METHODS.add("execute");
EXECUTE_METHODS.add("executeUpdate");
EXECUTE_METHODS.add("executeQuery");
EXECUTE_METHODS.add("addBatch");
}
debug方法 StdOutImpl类中
@Override
public void debug(String s) {
System.out.println(s);
}
因此控制台打印了
执行 method.invoke(statement, params)方法后,通过反射机制调用了execute方法(ClientPrepartedStatement,在mysql-connector的jar包中)
public boolean execute() throws SQLException {
try {
synchronized(this.checkClosed().getConnectionMutex()) {
JdbcConnection locallyScopedConn = this.connection;
if (!this.doPingInstead && !this.checkReadOnlySafeStatement()) {
throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), "S1009", this.exceptionInterceptor);
} else {
ResultSetInternalMethods rs = null;
this.lastQueryIsOnDupKeyUpdate = false;
if (this.retrieveGeneratedKeys) {
this.lastQueryIsOnDupKeyUpdate = this.containsOnDuplicateKeyUpdateInSQL();
}
this.batchedGeneratedKeys = null;
this.resetCancelledState();
this.implicitlyCloseAllOpenResults();
this.clearWarnings();
if (this.doPingInstead) {
this.doPingInstead();
return true;
} else {
this.setupStreamingTimeout(locallyScopedConn);
Message sendPacket = ((PreparedQuery)this.query).fillSendPacket();
String oldCatalog = null;
if (!locallyScopedConn.getCatalog().equals(this.getCurrentCatalog())) {
oldCatalog = locallyScopedConn.getCatalog();
locallyScopedConn.setCatalog(this.getCurrentCatalog());
}
CachedResultSetMetaData cachedMetadata = null;
boolean cacheResultSetMetadata = (Boolean)locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue();
if (cacheResultSetMetadata) {
cachedMetadata = locallyScopedConn.getCachedMetaData(((PreparedQuery)this.query).getOriginalSql());
}
locallyScopedConn.setSessionMaxRows(((PreparedQuery)this.query).getParseInfo().getFirstStmtChar() == 'S' ? this.maxRows : -1);
rs = this.executeInternal(this.maxRows, sendPacket, this.createStreamingResultSet(), ((PreparedQuery)this.query).getParseInfo().getFirstStmtChar() == 'S', cachedMetadata, false);
if (cachedMetadata != null) {
locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery)this.query).getOriginalSql(), cachedMetadata, rs);
} else if (rs.hasRows() && cacheResultSetMetadata) {
locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery)this.query).getOriginalSql(), (CachedResultSetMetaData)null, rs);
}
if (this.retrieveGeneratedKeys) {
rs.setFirstCharOfQuery(((PreparedQuery)this.query).getParseInfo().getFirstStmtChar());
}
if (oldCatalog != null) {
locallyScopedConn.setCatalog(oldCatalog);
}
if (rs != null) {
this.lastInsertId = rs.getUpdateID();
this.results = rs;
}
return rs != null && rs.hasRows();
}
}
}
} catch (CJException var11) {
throw SQLExceptionsMapping.translateException(var11, this.getExceptionInterceptor());
}
}
回到PreparedStatement的query方法,执行完execute方法后,就获得了resultsets
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();//执行sql语句,得到resultsets
return resultSetHandler.handleResultSets(ps);//处理结果集
}
handleResultSets,(DefaultResultSetHandlerl)处理结果集
//
// HANDLE RESULT SETS
//
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
执行getFirstResultSet
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
执行getResultSet,这里也使用了反射机制
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if (EXECUTE_METHODS.contains(method.getName())) {
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true);
}
clearColumnInfo();
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
} else if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
} else if ("getResultSet".equals(method.getName())) {//方法名称为getResultSet
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
通过反射执行StatementImpl类(mysql-connector jar包) 的getResultSet方法
public ResultSet getResultSet() throws SQLException {
try {
synchronized(this.checkClosed().getConnectionMutex()) {
return this.results != null && this.results.hasRows() ? this.results : null;
}
} catch (CJException var5) {
throw SQLExceptionsMapping.translateException(var5, this.getExceptionInterceptor());
}
}
执行完getResultSet方法后返回invoke方法
else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
ResultSetLogger.newInstance方法
public static ResultSet newInstance(ResultSet rs, Log statementLog, int queryStack) {
InvocationHandler handler = new ResultSetLogger(rs, statementLog, queryStack);
ClassLoader cl = ResultSet.class.getClassLoader();
return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler);
}
后面还有好多就是结果集的处理,打印出以下日志
然后处理完后关闭statement(也是通过反射调用相应方法),这里就不一一写了,调用太多了,快晕了,这仅仅是一个查询
然后在一层一层的返回,最终返回到selectList方法
@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();//清空排错信息
}
}
最终返回到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);//执行selectList方法
if (list.size() == 1) {//如果查询行数为1
return list.get(0);//返回查询到的记录
} else if (list.size() > 1) {//如果查询行数大于1
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), " +
"but found: " + list.size());//抛出异常
} else {
return null;
}
}
User user= sqlSession.selectOne("org.apache.demo.UserMapper.selectById",1);
以上就是mybatis执行sql语句的调用过程。
我们顺便看一下jdbc是如何获取一个连接的吧
DriverManager.getConnection方法
@CallerSensitive
public static Connection getConnection(String url,
java.util.Properties info) throws SQLException {
return (getConnection(url, info, Reflection.getCallerClass()));
}
进入getConnection(url, info, Reflection.getCallerClass()))
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
查看connect方法
public Connection connect(String var1, Properties var2) throws SQLException {
if (var1.regionMatches(true, 0, "jdbc:default:connection", 0, "jdbc:default:connection".length())) {
JDBCConnection var3 = (JDBCConnection)this.threadConnection.get();
return var3 == null ? null : var3;
} else {
return getConnection(var1, var2);
}
}
initializeDriver方法
private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
这时候成功获取到driver
进入connect方法(NonRegisteringDriver类中)
public Connection connect(String url, Properties info) throws SQLException {
try {
try {
if (!ConnectionUrl.acceptsUrl(url)) {//校验url的合法性
return null;
} else {
ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
switch(conStr.getType()) {//ConnectionUrl 类型
case SINGLE_CONNECTION://单个连接
return ConnectionImpl.getInstance(conStr.getMainHost());
case LOADBALANCE_CONNECTION://负载均衡连接
return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl)conStr);
case FAILOVER_CONNECTION://故障连接
return FailoverConnectionProxy.createProxyInstance(conStr);
case REPLICATION_CONNECTION://拷贝连接
return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl)conStr);
default:
return null;
}
}
} catch (UnsupportedConnectionStringException var5) {
return null;
} catch (CJException var6) {
throw (UnableToConnectException)ExceptionFactory.createException(UnableToConnectException.class, Messages.getString("NonRegisteringDriver.17", new Object[]{var6.toString()}), var6);
}
} catch (CJException var7) {
throw SQLExceptionsMapping.translateException(var7);
}
}
我们这里这是简单的单个连接,所以进入getInstance方法
public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {
return new ConnectionImpl(hostInfo);
}
ConnectionImpl构造方法
public ConnectionImpl(HostInfo hostInfo) throws SQLException {
try {
//下面就是一些设置连接的主机、目的主机、端口、用户名、密码、数据库等信息
this.origHostInfo = hostInfo;
this.origHostToConnectTo = hostInfo.getHost();
this.origPortToConnectTo = hostInfo.getPort();
this.database = hostInfo.getDatabase();
this.user = StringUtils.isNullOrEmpty(hostInfo.getUser()) ? "" : hostInfo.getUser();
this.password = StringUtils.isNullOrEmpty(hostInfo.getPassword()) ? "" : hostInfo.getPassword();
this.props = hostInfo.exposeAsProperties();
this.propertySet = new JdbcPropertySetImpl();
this.propertySet.initializeProperties(this.props);
this.nullStatementResultSetFactory = new ResultSetFactory(this, (StatementImpl)null);
this.session = new NativeSession(hostInfo, this.propertySet);
this.session.addListener(this);
this.autoReconnectForPools = this.propertySet.getBooleanProperty(PropertyKey.autoReconnectForPools);
this.cachePrepStmts = this.propertySet.getBooleanProperty(PropertyKey.cachePrepStmts);
this.autoReconnect = this.propertySet.getBooleanProperty(PropertyKey.autoReconnect);
this.useUsageAdvisor = this.propertySet.getBooleanProperty(PropertyKey.useUsageAdvisor);
this.reconnectAtTxEnd = this.propertySet.getBooleanProperty(PropertyKey.reconnectAtTxEnd);
this.emulateUnsupportedPstmts = this.propertySet.getBooleanProperty(PropertyKey.emulateUnsupportedPstmts);
this.ignoreNonTxTables = this.propertySet.getBooleanProperty(PropertyKey.ignoreNonTxTables);
this.pedantic = this.propertySet.getBooleanProperty(PropertyKey.pedantic);
this.prepStmtCacheSqlLimit = this.propertySet.getIntegerProperty(PropertyKey.prepStmtCacheSqlLimit);
this.useLocalSessionState = this.propertySet.getBooleanProperty(PropertyKey.useLocalSessionState);
this.useServerPrepStmts = this.propertySet.getBooleanProperty(PropertyKey.useServerPrepStmts);
this.processEscapeCodesForPrepStmts = this.propertySet.getBooleanProperty(PropertyKey.processEscapeCodesForPrepStmts);
this.useLocalTransactionState = this.propertySet.getBooleanProperty(PropertyKey.useLocalTransactionState);
this.disconnectOnExpiredPasswords = this.propertySet.getBooleanProperty(PropertyKey.disconnectOnExpiredPasswords);
this.readOnlyPropagatesToServer = this.propertySet.getBooleanProperty(PropertyKey.readOnlyPropagatesToServer);
String exceptionInterceptorClasses = this.propertySet.getStringProperty(PropertyKey.exceptionInterceptors).getStringValue();
if (exceptionInterceptorClasses != null && !"".equals(exceptionInterceptorClasses)) {
this.exceptionInterceptor = new ExceptionInterceptorChain(exceptionInterceptorClasses, this.props, this.session.getLog());
}
if ((Boolean)this.cachePrepStmts.getValue()) {
this.createPreparedStatementCaches();
}
if ((Boolean)this.propertySet.getBooleanProperty(PropertyKey.cacheCallableStmts).getValue()) {
this.parsedCallableStatementCache = new LRUCache((Integer)this.propertySet.getIntegerProperty(PropertyKey.callableStmtCacheSize).getValue());
}
if ((Boolean)this.propertySet.getBooleanProperty(PropertyKey.allowMultiQueries).getValue()) {
this.propertySet.getProperty(PropertyKey.cacheResultSetMetadata).setValue(false);
}
if ((Boolean)this.propertySet.getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue()) {
this.resultSetMetadataCache = new LRUCache((Integer)this.propertySet.getIntegerProperty(PropertyKey.metadataCacheSize).getValue());
}
if (this.propertySet.getStringProperty(PropertyKey.socksProxyHost).getStringValue() != null) {
this.propertySet.getProperty(PropertyKey.socketFactory).setValue(SocksProxySocketFactory.class.getName());
}
this.pointOfOrigin = (Boolean)this.useUsageAdvisor.getValue() ? LogUtils.findCallingClassAndMethod(new Throwable()) : "";
this.dbmd = this.getMetaData(false, false);
this.initializeSafeQueryInterceptors();
} catch (CJException var5) {
throw SQLExceptionsMapping.translateException(var5, this.getExceptionInterceptor());
}
try {
this.createNewIO(false);
this.unSafeQueryInterceptors();
NonRegisteringDriver.trackConnection(this);
} catch (SQLException var3) {
this.cleanup(var3);
throw var3;
} catch (Exception var4) {
this.cleanup(var4);
throw SQLError.createSQLException((Boolean)this.propertySet.getBooleanProperty(PropertyKey.paranoid).getValue() ? Messages.getString("Connection.0") : Messages.getString("Connection.1", new Object[]{this.session.getHostInfo().getHost(), this.session.getHostInfo().getPort()}), "08S01", var4, this.getExceptionInterceptor());
}
}
createNewIO方法
public void createNewIO(boolean isForReconnect) {
try {
synchronized(this.getConnectionMutex()) {//
try {
if (!(Boolean)this.autoReconnect.getValue()) {
this.connectOneTryOnly(isForReconnect);
return;
}
this.connectWithRetries(isForReconnect);
} catch (SQLException var6) {
throw (UnableToConnectException)ExceptionFactory.createException(UnableToConnectException.class, var6.getMessage(), var6);
}
}
} catch (CJException var8) {
throw SQLExceptionsMapping.translateException(var8, this.getExceptionInterceptor());
}
}
connectOneTryOnly方法
private void connectOneTryOnly(boolean isForReconnect) throws SQLException {
Object var2 = null;
try {
JdbcConnection c = this.getProxy();//获取代理对象
this.session.connect(this.origHostInfo, this.user, this.password, this.database, DriverManager.getLoginTimeout() * 1000, c);
boolean oldAutoCommit = this.getAutoCommit();
int oldIsolationLevel = this.isolationLevel;
boolean oldReadOnly = this.isReadOnly(false);
String oldCatalog = this.getCatalog();
this.session.setQueryInterceptors(this.queryInterceptors);
this.initializePropsFromServer();
if (isForReconnect) {
this.setAutoCommit(oldAutoCommit);
this.setTransactionIsolation(oldIsolationLevel);
this.setCatalog(oldCatalog);
this.setReadOnly(oldReadOnly);
}
} catch (UnableToConnectException var8) {
this.close();
this.session.getProtocol().getSocketConnection().forceClose();
throw var8;
} catch (Exception var9) {
if (!(var9 instanceof PasswordExpiredException) && (!(var9 instanceof SQLException) || ((SQLException)var9).getErrorCode() != 1820) || (Boolean)this.disconnectOnExpiredPasswords.getValue()) {
if (this.session != null) {
this.session.forceClose();
}
if (var9 instanceof SQLException) {
throw (SQLException)var9;
} else if (var9.getCause() != null && var9.getCause() instanceof SQLException) {
throw (SQLException)var9.getCause();
} else if (var9 instanceof CJException) {
throw (CJException)var9;
} else {
SQLException chainedEx = SQLError.createSQLException(Messages.getString("Connection.UnableToConnect"), "08001", this.getExceptionInterceptor());
chainedEx.initCause(var9);
throw chainedEx;
}
}
}
}
this.session.connect方法(NativeSession类)
public void connect(HostInfo hi, String user, String password, String database, int loginTimeout, TransactionEventHandler transactionManager) throws IOException {
this.hostInfo = hi;
this.setSessionMaxRows(-1);
SocketConnection socketConnection = new NativeSocketConnection();//创建SockedConnection对象
socketConnection.connect(this.hostInfo.getHost(), this.hostInfo.getPort(), this.propertySet, this.getExceptionInterceptor(), this.log, loginTimeout);//连接
if (this.protocol == null) {
this.protocol = NativeProtocol.getInstance(this, socketConnection, this.propertySet, this.log, transactionManager);
} else {
this.protocol.init(this, socketConnection, this.propertySet, transactionManager);
}
this.protocol.connect(user, password, database);
this.protocol.getServerSession().setErrorMessageEncoding(this.protocol.getAuthenticationProvider().getEncodingForHandshake());
this.isClosed = false;
}
socketConnection.connect方法,该方法的作用是当前主机与mysql数据库服务器建立socket连接
public void connect(String hostName, int portNumber, PropertySet propSet, ExceptionInterceptor excInterceptor, Log log, int loginTimeout) {
try {
this.port = portNumber;//端口号
this.host = hostName;//主机
this.propertySet = propSet;
this.exceptionInterceptor = excInterceptor;
this.socketFactory = this.createSocketFactory(propSet.getStringProperty(PropertyKey.socketFactory).getStringValue());//创建socketFactory
this.mysqlSocket = (Socket)this.socketFactory.connect(this.host, this.port, propSet, loginTimeout);//创建mysqlSocket
int socketTimeout = (Integer)propSet.getIntegerProperty(PropertyKey.socketTimeout).getValue();
if (socketTimeout != 0) {
try {
this.mysqlSocket.setSoTimeout(socketTimeout);
} catch (Exception var9) {
}
}
this.socketFactory.beforeHandshake();//握手之前
Object rawInputStream;
if ((Boolean)propSet.getBooleanProperty(PropertyKey.useReadAheadInput).getValue()) {
rawInputStream = new ReadAheadInputStream(this.mysqlSocket.getInputStream(), 16384, (Boolean)propSet.getBooleanProperty(PropertyKey.traceProtocol).getValue(), log);
} else if ((Boolean)propSet.getBooleanProperty(PropertyKey.useUnbufferedInput).getValue()) {
rawInputStream = this.mysqlSocket.getInputStream();
} else {
rawInputStream = new BufferedInputStream(this.mysqlSocket.getInputStream(), 16384);
}
this.mysqlInput = new FullReadInputStream((InputStream)rawInputStream);//设置输入流
this.mysqlOutput = new BufferedOutputStream(this.mysqlSocket.getOutputStream(), 16384);//设置输出流,即允许练级mysql所在主机的最大连接数为16384
} catch (IOException var10) {
throw ExceptionFactory.createCommunicationsException(propSet, (ServerSession)null, new PacketSentTimeHolder() {
}, (PacketReceivedTimeHolder)null, var10, this.getExceptionInterceptor());
}
}
beforeHandshake方法
public void beforeHandshake() throws IOException {
this.resetLoginTimeCountdown();//重置登录时间计时
this.socketTimeoutBackup = this.rawSocket.getSoTimeout();//备份
this.rawSocket.setSoTimeout(this.getRealTimeout(this.socketTimeoutBackup));//设置连接的超时时间
}
以上就是DriverManger.getConnection建立连接的全过程