Mybatis 源码分析(四)之 Mybatis 整体的执行流程
前面了解到Mybatis的执行流程,首先读取我们的mybatis-config.xml配置文件,然后构建Configuration类,这个类会像上下文信息一样会传来传去,以便我们获取其中的信息。
构建Configuration过程中,会读取我们的配置信息,其中包含读取我们的mapper的配置,并将mapper的信息以key:namespace+id,value:MapperMethod,注册到MapperRegistry中。
对于MapperMethod类包含我们的sql以及resultType,在后面执行数据库操作时,会取出来做进一步的查询处理。
最后将结果集进行封装处理后返回给用户。
下面是一个mybatis源码demo,可以帮助梳理流程。
首先我们的pojo类
com.demo.mybatis.pojo.User
package com.demo.mybatis.pojo;
/**
* com.demo.mybatis.pojo
*
* @author Zyy
* @date 2019/2/2 23:24
*/
public class User {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
然后定义mapper中的查询方法,以及对应的xml文件
com.demo.mybatis.mappers.UserMapper
package com.demo.mybatis.mappers;
import com.demo.mybatis.pojo.User;
/**
* com.demo.mybatis.mappers
*
* @author Zyy
* @date 2019/2/2 23:23
*/
public interface UserMapper {
User selectByPrimaryKey(int id);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.demo.mybatis.mappers.UserMapper">
<select id="selectByPrimaryKey" resultType="com.demo.mybatis.pojo.User">
select * from user where id = %d
</select>
</mapper>
一个JDBC工具类
DBUtil.java
package com.demo.mybatis.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* com.demo.mybatis.jdbc
*
* @author Zyy
* @date 2019/2/3 17:20
* jdbc工具类
*/
public class DBUtil {
private static String driver;//连接数据库的驱动
private static String url;
private static String username;
private static String password;
static {
driver="com.mysql.jdbc.Driver";//需要的数据库驱动
url="jdbc:mysql://192.168.5.104:3306/mybatis";//数据库名路径
username="root";
password="root";
}
public static Connection open()
{
try {
Class.forName(driver);
return (Connection) DriverManager.getConnection(url,username, password);
} catch (Exception e) {
System.out.println("数据库连接失败!");
}
return null;
}
/*
* 关闭数据库
*/
public static void close(Connection conn)
{
if(conn!=null)
{
try {
conn.close();
} catch (SQLException e) {
System.out.println("数据库关闭失败!");
}
}
}
}
下面正式开始,自定义的mybatis-config.xml配置文件,后面自己解析
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<inputStream>
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</inputStream>
解析配置文件,读取到mapper信息封装成MapperMethod,注册到MapperRegistry中
com.demo.mybatis.binding.MapperMethod
package com.demo.mybatis.binding;
/**
* com.demo.mybatis.binding
*
* @author Zyy
* @date 2019/2/2 22:27
*
* 将解析的sql加载到这个类中
* sql:select * from user
* type:user
*/
public class MapperMethod<T> {
private String sql;
private Class<T> type;
public MapperMethod(String sql, Class<T> type) {
this.sql = sql;
this.type = type;
}
public MapperMethod() {
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public Class<T> getType() {
return type;
}
public void setType(Class<T> type) {
this.type = type;
}
}
com.demo.mybatis.binding.MapperRegistry
package com.demo.mybatis.binding;
import java.util.HashMap;
import java.util.Map;
/**
* com.demo.mybatis.binding
*
* @author Zyy
* @date 2019/2/2 22:07
*/
public class MapperRegistry {
private Map<String, MapperMethod> knownMappers = new HashMap<String, MapperMethod>();
public Map<String, MapperMethod> getKnownMappers() {
return knownMappers;
}
public void setKnownMappers(Map<String, MapperMethod> knownMappers) {
this.knownMappers = knownMappers;
}
}
读取自定义配置文件,以及加载mapper使其与xml相关联
com.demo.mybatis.session.Configuration
package com.demo.mybatis.session;
import com.demo.mybatis.binding.MapperMethod;
import com.demo.mybatis.binding.MapperRegistry;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* com.demo.mybatis.session
*
* @author Zyy
* @date 2019/2/2 22:01
* 读取xml文件到内存中
*/
public class Configuration {
private InputStream inputStream;
//mapper的注册中心
private MapperRegistry mapperRegistry = new MapperRegistry();
//解析配置文件
public void loadConfigurations() throws IOException {
try {
Document document = new SAXReader().read(inputStream);
Element root = document.getRootElement();
List<Element> mappers = root.element("mappers").elements("mapper");
for (Element mapper : mappers) {
if (mapper.attribute("resource") != null) {
mapperRegistry.setKnownMappers(loadXMLConfiguration(mapper.attribute("resource").getText()));
}
}
} catch (Exception e) {
System.out.println("读取配置文件失败");
} finally {
inputStream.close();
}
}
//加载解析配置文件并将解析的内容放入到map中
private Map<String, MapperMethod> loadXMLConfiguration(String resource) throws IOException {
Map<String,MapperMethod> map = new HashMap<String, MapperMethod>();
InputStream inputStream = null;
try {
inputStream = this.getClass().getClassLoader().getResourceAsStream(resource);
Document document = new SAXReader().read(inputStream);
Element root = document.getRootElement();
if (root.getName().equalsIgnoreCase("mapper")) {
String namespace = root.attribute("namespace").getText();
for (Element select : (List<Element>)root.elements("select")) {
MapperMethod mapperModel = new MapperMethod();
//sql : select * from user;
mapperModel.setSql(select.getText().trim());
//type : resultType="com.demo.mybatis.pojo.User"
mapperModel.setType(Class.forName(select.attribute("resultType").getText()));
//namespace+id : com.demo.mybatis.mapper.UserMapper.selectByPrimaryKey
map.put(namespace + "." + select.attribute("id").getText(),mapperModel);
}
}
}catch (Exception e) {
e.printStackTrace();
} finally {
inputStream.close();
}
return map;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public MapperRegistry getMapperRegistry() {
return mapperRegistry;
}
public void setMapperRegistry(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
}
根据mybatis的流程,创建SqlSession,首先SqlSessionFactoryBuilder.build(...)
com.demo.mybatis.session.SqlSessionFactoryBuilder
package com.demo.mybatis.session;
import java.io.IOException;
/**
* com.demo.mybatis.session
*
* @author Zyy
* @date 2019/2/2 23:14
*/
public class SqlSessionFactoryBuilder {
public DefaultSqlSessionFactory build(Configuration configuration) throws IOException {
configuration.loadConfigurations();
return new DefaultSqlSessionFactory();
}
}
获取到SqlSessionFactory,在mybatis中,先抽象一个接口类,然后给予一个默认实现
com.demo.mybatis.session.SqlSessionFactory
package com.demo.mybatis.session;
/**
* com.demo.mybatis.session
*
* @author Zyy
* @date 2019/2/2 23:03
*/
public interface SqlSessionFactory {
SqlSession openSession(Configuration configuration);
}
com.demo.mybatis.session.DefaultSqlSessionFactory
package com.demo.mybatis.session;
import com.demo.mybatis.executor.SimpleExecutor;
/**
* com.demo.mybatis.session
*
* @author Zyy
* @date 2019/2/2 23:04
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory{
public SqlSession openSession(Configuration configuration) {
return new DefaultSqlSession(configuration,new SimpleExecutor(configuration));
}
}
接下来创建sqlSession类
com.demo.mybatis.session.SqlSession
package com.demo.mybatis.session;
import com.demo.mybatis.binding.MapperMethod;
import java.sql.SQLException;
/**
* com.demo.mybatis.session
*
* @author Zyy
* @date 2019/2/2 21:59
*/
public interface SqlSession {
<T> T selectOne(MapperMethod id, Object param) throws SQLException;
}
com.demo.mybatis.session.DefaultSqlSession
package com.demo.mybatis.session;
import com.demo.mybatis.binding.MapperMethod;
import com.demo.mybatis.binding.MapperProxy;
import com.demo.mybatis.executor.Executor;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
/**
* com.demo.mybatis.session
*
* @author Zyy
* @date 2019/2/2 22:00
*/
public class DefaultSqlSession implements SqlSession{
private Configuration configuration;
private Executor executor;
public <T> T selectOne(MapperMethod mapperMethod, Object statement) throws SQLException {
return executor.query(mapperMethod,statement);
}
public <T> T getMapper(Class<T> type) {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},new MapperProxy<T>(this,type));
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this.configuration = configuration;
this.executor = executor;
}
public Configuration getConfiguration() {
return configuration;
}
}
动态代理,获取我们注册的mapper信息
com.demo.mybatis.binding.MapperProxy
package com.demo.mybatis.binding;
import com.demo.mybatis.session.DefaultSqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* com.demo.mybatis.binding
*
* @author Zyy
* @date 2019/2/2 23:29
*/
public class MapperProxy<T> implements InvocationHandler {
private final DefaultSqlSession sqlSession;
private final Class<T> mapperInterface;
public MapperProxy(DefaultSqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//key : com.demo.mybatis.mapper.UserMapper.selectByPrimaryKey
MapperMethod mapperMethod = sqlSession.getConfiguration().getMapperRegistry().getKnownMappers().get(
method.getDeclaringClass().getName() + "." + method.getName());
if (null != mapperMethod) {
return sqlSession.selectOne(mapperMethod, args[0]);
}
return method.invoke(proxy, args);
}
}
当使用mapper.selectByPrimaryKey执行查询时,进入到MapperProxy.invoke,
之后会调用sqlSession.selectOne,而在sqlSession中是使用executor.query
com.demo.mybatis.executor.Executor
package com.demo.mybatis.executor;
import com.demo.mybatis.binding.MapperMethod;
import java.sql.SQLException;
/**
* com.demo.mybatis.executor
*
* @author Zyy
* @date 2019/2/2 21:57
*/
public interface Executor {
<T> T query(MapperMethod mapperMethod, Object params) throws SQLException;
}
com.demo.mybatis.executor.SimpleExecutor
package com.demo.mybatis.executor;
import com.demo.mybatis.binding.MapperMethod;
import com.demo.mybatis.session.Configuration;
import com.demo.mybatis.statement.StatementHandler;
import java.sql.SQLException;
/**
* com.demo.mybatis
*
* @author Zyy
* @date 2019/2/2 21:57
* 执行器
*/
public class SimpleExecutor implements Executor{
private Configuration configuration;
public SimpleExecutor(Configuration configuration) {
this.configuration = configuration;
}
public <T> T query(MapperMethod mapperMethod, Object params) throws SQLException {
StatementHandler statementHandler = new StatementHandler(configuration);
return statementHandler.query(mapperMethod,params);
}
}
SimpleExecutor不做具体处理,后面交给handler进行,定义一个StatementHandler
com.demo.mybatis.statement.StatementHandler
package com.demo.mybatis.statement;
import com.demo.mybatis.binding.MapperMethod;
import com.demo.mybatis.jdbc.DBUtil;
import com.demo.mybatis.resultset.DefaultResultSetHandler;
import com.demo.mybatis.resultset.ResultSetHandler;
import com.demo.mybatis.session.Configuration;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* com.demo.mybatis.statement
*
* @author Zyy
* @date 2019/2/2 23:56
* handler 真正干活的
*/
public class StatementHandler {
private Configuration configuration;
private ResultSetHandler resultSetHandler;
public StatementHandler(Configuration configuration) {
this.configuration = configuration;
this.resultSetHandler = new DefaultResultSetHandler();
}
public <T> T query(MapperMethod mapperMethod, Object params) throws SQLException {
Connection connection = DBUtil.open();
//String.format(mapperMethod.getSql(), (Integer) params)) ==> select * from user where id = 1
PreparedStatement preparedStatement = connection.prepareStatement(String.format(mapperMethod.getSql(), (Integer) params));
preparedStatement.executeQuery();
//处理结果集
return resultSetHandler.handler(preparedStatement, mapperMethod);
}
}
执行完数据库操作后,对结果集进行处理
mybatis中的ObjectFactory,以及其实现
com.demo.mybatis.resultset.ObjectFactory
package com.demo.mybatis.resultset;
import java.util.List;
import java.util.Properties;
/**
* com.demo.mybatis.resultset
*
* @author Zyy
* @date 2019/2/3 18:04
* mybatis :org.apache.ibatis.reflection.factory
*/
public interface ObjectFactory {
void setProperties(Properties var1);
<T> T create(Class<T> var1) throws Exception;
<T> T create(Class<T> var1, List<Class<?>> var2, List<Object> var3) throws Exception;
<T> boolean isCollection(Class<T> var1);
}
com.demo.mybatis.resultset.DefaultObjectFactory
package com.demo.mybatis.resultset;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.*;
/**
* com.demo.mybatis.resultset
*
* @author Zyy
* @date 2019/2/3 18:05
* 直接复制了mybatis里面的
* mybatis :org.apache.ibatis.reflection.factory
*/
public class DefaultObjectFactory implements ObjectFactory, Serializable {
private static final long serialVersionUID = -8855120656740914948L;
public DefaultObjectFactory() {
}
public <T> T create(Class<T> type) throws Exception {
return (T) this.create(type, (List)null, (List)null);
}
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws Exception {
Class<?> classToCreate = this.resolveInterface(type);
return (T) this.instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
public void setProperties(Properties properties) {
}
<T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws Exception {
try {
Constructor constructor;
if (constructorArgTypes != null && constructorArgs != null) {
constructor = type.getDeclaredConstructor((Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return (T) constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} else {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return (T) constructor.newInstance();
}
} catch (Exception var9) {
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
Iterator var6 = constructorArgTypes.iterator();
while(var6.hasNext()) {
Class<?> argType = (Class)var6.next();
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1);
}
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
Iterator var11 = constructorArgs.iterator();
while(var11.hasNext()) {
Object argValue = var11.next();
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1);
}
throw new Exception("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + var9, var9);
}
}
protected Class<?> resolveInterface(Class<?> type) {
Class classToCreate;
if (type != List.class && type != Collection.class && type != Iterable.class) {
if (type == Map.class) {
classToCreate = HashMap.class;
} else if (type == SortedSet.class) {
classToCreate = TreeSet.class;
} else if (type == Set.class) {
classToCreate = HashSet.class;
} else {
classToCreate = type;
}
} else {
classToCreate = ArrayList.class;
}
return classToCreate;
}
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
处理结果集
com.demo.mybatis.resultset.ResultSetHandler
package com.demo.mybatis.resultset;
import com.demo.mybatis.binding.MapperMethod;
import java.sql.PreparedStatement;
/**
* com.demo.mybatis.resultset
*
* @author Zyy
* @date 2019/2/3 17:52
*/
public interface ResultSetHandler {
<T> T handler(PreparedStatement preparedStatement, MapperMethod mapperMethod);
}
com.demo.mybatis.resultset.DefaultResultSetHandler
package com.demo.mybatis.resultset;
import com.demo.mybatis.binding.MapperMethod;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author Zyy
* @date 2019/2/3 18:01
*
* 将结果反射賦值
*/
public class DefaultResultSetHandler implements ResultSetHandler {
public <T> T handler(PreparedStatement preparedStatement, MapperMethod mapperMethod) {
Object resultObject = null;
try {
resultObject = new DefaultObjectFactory().create(mapperMethod.getType());
ResultSet resultSet = preparedStatement.getResultSet();
if (resultSet.next()) {
int i = 0;
//循环赋值
for (Field field : resultObject.getClass().getDeclaredFields()) {
setValue(resultObject, field, resultSet, i);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return (T) resultObject;
}
private void setValue(Object resultObject, Field field, ResultSet resultSet, int i) throws NoSuchMethodException, SQLException, InvocationTargetException, IllegalAccessException {
Method method = resultObject.getClass().getMethod("set" + upperCapital(field.getName()), field.getType());
method.invoke(resultObject, getResult(field, resultSet));
}
//首字母大写,例如:Name
private String upperCapital(String name) {
String first = name.substring(0, 1);
String tail = name.substring(1);
return first.toUpperCase() + tail;
}
//获取结果
private Object getResult(Field field, ResultSet resultSet) throws SQLException {
Class<?> type = field.getType();
if (Integer.class.equals(type)) {
return resultSet.getInt(field.getName());
}
if (String.class.equals(type)) {
return resultSet.getString(field.getName());
}
return resultSet.getString(field.getName());
}
}
测试
package com.demo.mybatis;
import com.demo.mybatis.mappers.UserMapper;
import com.demo.mybatis.pojo.User;
import com.demo.mybatis.session.*;
import java.io.IOException;
import java.io.InputStream;
/**
* PACKAGE_NAME
*
* @author Zyy
* @date 2019/2/2 23:17
*/
public class MybatisTest {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = MybatisTest.class.getClassLoader().getResourceAsStream(resource);
Configuration configuration = new Configuration();
configuration.setInputStream(inputStream);
DefaultSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
DefaultSqlSession sqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(configuration);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectByPrimaryKey(1);
System.out.println(user);
}
}
执行结果
User{id=1, name='ayang', age=18}
Github地址: https://github.com/zhaoyybalabala/mybatis-demo
如有问题,欢迎留言:)