mybatis执行过程-爱代码爱编程
文章目录
- 一、SpringBoot 集成MyBatis
- 二、sql执行过程
- 1.加载mapper
- 2.将UserMapper.selectUserById方法映射为Mapper.xml的sql
- 3. SqlSessionTemplate拦截器SqlSessionInterceptor
- 4. Mybatis的插件机制
- 5. DefaultSelSession
- 6. BaseExecutor
- 7. SimpleExecutor
- 8. newStatementHandler
- 9.InterceptorChain
- 10.PreparedStatementHandler
- 11.PreparedStatement
- 12.BaseExecutor#closeStatement
- 13.SqlSesionUtils#closeSqlSession
一、SpringBoot 集成MyBatis
1. pom.xml添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
2.yml文件配置
server:
port: 9994 #服务端口号
logging:
level:
root: INFO
spring:
application:
name: mybatis-test #提供者的服务名称--调用的时候根据改名称来调用对应服务的方法
datasource:
url: jdbc:mysql://127.0.0.1:3326/db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.johar.mybatis.mybatistest.mapper
3.定义PO
public class Company {
private Integer id;
private String name;
private String address;
}
public class User {
private Integer id;
private String name;
private Integer age;
private Integer sex;
private Company company;
}
4. 定义mapper
public interface CompanyMapper {
Company selectCompanyById(Integer id);
}
public interface UserMapper {
User selectUserById(Integer id);
}
<?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="com.johar.mybatis.mybatistest.mapper.CompanyMapper">
<select id="selectCompanyById" resultType="com.johar.mybatis.mybatistest.po.Company">
select * from t_company where id = #{id}
</select>
</mapper>
<?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="com.johar.mybatis.mybatistest.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.johar.mybatis.mybatistest.po.User">
<id property="id" javaType="java.lang.Integer" column="id" jdbcType="INTEGER"/>
<result property="name" javaType="java.lang.String" column="name" jdbcType="VARCHAR"/>
<result property="age" javaType="java.lang.Integer" column="age" jdbcType="TINYINT" />
<result property="sex" javaType="java.lang.Integer" column="sex" jdbcType="TINYINT"/>
<association property="company" javaType="com.johar.mybatis.mybatistest.po.Company" column="company_id" select="com.johar.mybatis.mybatistest.mapper.CompanyMapper.selectCompanyById">
<id property="id" javaType="java.lang.Integer" column="id" jdbcType="INTEGER"/>
<result property="name" javaType="java.lang.String" column="name" jdbcType="VARCHAR"/>
<result property="address" javaType="java.lang.String" column="address" jdbcType="VARCHAR"/>
</association>
</resultMap>
<select id="selectUserById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select * from t_user where id = #{id}
</select>
</mapper>
5.启动类
@SpringBootApplication
@MapperScan(basePackages = {"com.johar.mybatis.mybatistest.mapper"})
public class MybatisTestApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisTestApplication.class, args);
}
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getById(Integer id){
return userMapper.selectUserById(id);
}
}
二、sql执行过程
MyBatis作为常用的ORM框架之一,了解MyBatis中sql执行过程是应用开发必备的技能。如下图是MyBatis 的整体架构:
MyBatis的整体流程如下所示:
MyBatis的详细执行流程如下所示:
1.加载mapper
@MapperScan注解中默认的factoryBean是MapperFactoryBean,负责将CompanyMapper,UserMapper注入到Spring IOC。
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
最终调用的是MapperProxyFactory的newInstance,最终生成的是一个MapperProxy。
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
2.将UserMapper.selectUserById方法映射为Mapper.xml的sql
前面说到,Mapper实际上时一个MapperProxy的实例,因此调用selectUserById实际调用的是MapperProxy的invoke方法。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
// Object的方法直接调用
return method.invoke(this, args);
} else {
// 将mapper中方法映射为Mapper.xml的sql
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {
// 默认方法执行逻辑
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 非默认方法执行逻辑
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
接下来就是实际映射逻辑:
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// 映射逻辑见其构造函数中调用的resolveMappedStatement
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
// 实际执行的逻辑,将方法转换成sql:insert,update,delete,select
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ "' attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// 接口类名.方法名 作为唯一值,查询对应的sql
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}