执行流程
示意图
如图所示,Mybatis在执行sql前都会创建一个SqlSessionFactory,在执行sql时候会创建一个SqlSession,去执行sql。
首先我们先看看SqlSessionFactoryBuilder的执行流程。在接受一个输入流,也就是mybatis-config.xml的输入流,会让XMLConfigBuilder对象去解析这个xml。其中解析最关键的部分就是解析数据库的相关信息(environment标签)和mapper.xml的位置(mappers标签)。
在解析environment标签时,会将数据库的driver、url、username、password封装进Environment对象,再将Environment封装进Configuration。注意哦,这里封装Environment并不会创建数据库连接,所以driver、url、username、password任何一个参数有误在这里都不会报错;而Configuration对象相当于一个全局的配置类,封装了数据库、sql等一系列信息,具体属性见下文。
到了解析mapper.xml,会将扫描到的mapper接口进行注册,存进Configuration中的MapperRegistry对象,MapperRegistry保存了接口的类和接口方法与sql映射关系;接着会继续扫描mapper.xml,将parameterType存进Configuration中的parameterMap,将resultType存进Configuration中的resultMap,resultMap中就存放了sql执行结果与返回类的属性对应关系。最后sql语句会封装成MappedStatement对象,同样也是封装进Configuration。以上就完成了基本的Configuration配置,把Configuration封装进SqlSessionFactory,返回工厂类。
在执行sql时,一般都会用工厂类openSession(),获得SqlSession。SqlSession会根据传入的接口类,获得mapper接口的代理对象。这一步就是日常开发@Autowire一个mapper接口所执行的逻辑。这个接口在执行某个具体方法,该方法和sql的映射关系在上述流程已经完成,因此会执行到某个sql,再根据Configuration封装的Environment、parameterMap和resultMap,创建数据库连接,预编译sql,执行sql,封装成返回类。值得注意的是,只有执行sql时候才会创建connection,也就是说,driver、url、username、password的错误,在这步才会抛错。
具体代码见下文的Main方法。
关键类
SqlSessionFactory
这是个工厂类,用来创建SqlSession,里面封装了Configuration
public class SqlSessionFactory {
private Configuration configuration;
public SqlSessionFactory(Configuration config) {
this.configuration=config;
}
public SqlSession openSession(){
return new SqlSession(new SimpleExecutor(),configuration);
};
}
XMLConfigBuilder
用来解析mybatis-config.xml,将对应信息封装进Configuration
public class XMLConfigBuilder {
private InputStream inputStream;
public XMLConfigBuilder(InputStream inputStream) {
this.inputStream = inputStream;
}
public Configuration parse() {
SAXReader reader = new SAXReader();
Configuration configuration = null;
try {
Document document = reader.read(inputStream);
Element rootElement = document.getRootElement();
//原来的mybatis是执行parseConfiguration方法后,在XMLConfigBuilder的父类属性Configuration进行赋值
//这里简化书写,直接返回Configuration
configuration = this.parseConfiguration(rootElement);
} catch (DocumentException e) {
e.printStackTrace();
}
return configuration;
}
private Configuration parseConfiguration(Element rootElement) {
//简化书写,就解析出mapper、environment标签,源码其实还解析了xml中的很多其他东西
Configuration configuration = new Configuration();
Element dataSource = rootElement.element("environments").element("environment").element("dataSource");
Element mapper = rootElement.element("mappers").element("mapper");
this.environmentsElement(configuration, dataSource);
this.mapperElement(configuration, mapper);
return configuration;
}
private void environmentsElement(Configuration configuration, Element dataSource) {
Iterator iterator = dataSource.elementIterator();
DataSource dataSource1 = new DataSource();
while (iterator.hasNext()) {
Element element = (Element) iterator.next();
String name = element.attribute("name").getValue();
String value = element.attribute("value").getValue();
if (name.equals("driver")) {
dataSource1.setDriver(value);
} else if (name.equals("url")) {
dataSource1.setUrl(value);
} else if (name.equals("username")) {
dataSource1.setUsername(value);
} else if (name.equals("password")) {
dataSource1.setPassword(value);
}
}
configuration.setEnvironment(new Environment(dataSource1));
}
private void mapperElement(Configuration configuration, Element mapper) {
//解析mapper.xml,源码中主要是调用了XMLMapperBuilder的parse方法进一步执行业务,这里做了简化
try {
Attribute resource = mapper.attribute("resource");
String resourceValue = resource.getValue();
InputStream inputStream = ClassLoader.getSystemResourceAsStream(resourceValue);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
//注册扫描到的mapper接口
String namespace = rootElement.attribute("namespace").getValue();
configuration.setMapperRegistry(new MapperRegistry());
Class<?> mapperClass = null;
try {
mapperClass = Class.forName(namespace);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
MapperProxyFactory<?> mapperProxyFactory = new MapperProxyFactory<>(mapperClass);
//接口方法与sql对应还没注册,在后面解析会进行。
configuration.getMapperRegistry().getKnownMappers().put(mapperClass, mapperProxyFactory);
configuration.setParameterMaps(new HashMap<String, ParameterMap>());
configuration.setResultMaps(new HashMap<String, ResultMap>());
configuration.setMappedStatements(new HashMap<String, MappedStatement>());
//解析mapper.xml的解析器
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder();
Iterator iterator = rootElement.elementIterator();
while (iterator.hasNext()) {
Element element = (Element) iterator.next();
String sqlType = element.getQualifiedName();
String id = element.attribute("id").getValue();
String resultType = element.attribute("resultType").getValue();
String parameterType = element.attribute("parameterType").getValue();
String sql = element.getTextTrim();
//注册接口方法和sql的对应关系
for (Method method : mapperClass.getDeclaredMethods()) {
if (method != null && method.getName().equals(id)) {
MapperProxyFactory<?> mapperProxyFactory1 = configuration.getMapperRegistry().getKnownMappers().get(mapperClass);
mapperProxyFactory1.getMethodCache().put(method,new MapperMethod(namespace+"."+id));
}
}
//注册该sql的参数
xmlMapperBuilder.parameterMapElement(id, parameterType, configuration);
//注册该sql的返回封装类
xmlMapperBuilder.resultMapElement(id, resultType, configuration);
//注册该sql
xmlMapperBuilder.sqlElement(namespace, id, sql, configuration);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
XMLMapperBuilder
XMLConfigBuilder扫描到mapper.xml的位置,读取mapper.xml,并由XMLMapperBuilder去解析
//mapper.xml的解析器
public class XMLMapperBuilder {
public void sqlElement(String namespace,String id, String sql, Configuration configuration){
ResultMap resultMap = configuration.getResultMaps().get(id);
ParameterMap parameterMap = configuration.getParameterMaps().get(id);
MappedStatement mappedStatement = new MappedStatement(parameterMap, resultMap,sql);
configuration.getStatementMap().put(namespace+"."+id,mappedStatement);
};
public void parameterMapElement(String id, String parameterType, Configuration configuration) {
ParameterMap parameterMap = null;
try {
parameterMap = new ParameterMap(id, Class.forName(parameterType));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
configuration.getParameterMaps().put(id,parameterMap);
}
public void resultMapElement(String id, String resultType, Configuration configuration) {
Class<?> resultClass = null;
try {
resultClass = Class.forName(resultType);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
List<ResultMapping> propertyResultMappings = new ArrayList<ResultMapping>();
for (Field field : resultClass.getDeclaredFields()) {
String name = field.getName();
propertyResultMappings.add(new ResultMapping(name,name));
}
ResultMap resultMap = new ResultMap(id,resultClass,propertyResultMappings);
configuration.getResultMaps().put(id,resultMap);
}
}
Configuration
全局配置类,封装了sql,返回类,属性映射关系等信息
//为了后续传参,属性全都静态处理
public class Configuration {
//封装数据源的环境
private Environment environment;
//返回结果映射
private Map<String, ResultMap> resultMaps;
//参数映射
private Map<String, ParameterMap> parameterMaps;
//Mapper.xml存的sql key=namespace+id
private Map<String, MappedStatement> mappedStatements;
//mapper接口
private MapperRegistry mapperRegistry;
public Map getStatementMap() {
return mappedStatements;
}
public MappedStatement getMappedStatement(String statement) {
if (mappedStatements.get(statement) == null) {
throw new RuntimeException("mapper.xml文件找不到对应sql语句");
}
return mappedStatements.get(statement);
}
public Environment getEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Map<String, ResultMap> getResultMaps() {
return resultMaps;
}
public void setResultMaps(Map<String, ResultMap> resultMaps) {
this.resultMaps = resultMaps;
}
public Map<String, ParameterMap> getParameterMaps() {
return parameterMaps;
}
public void setParameterMaps(Map<String, ParameterMap> parameterMaps) {
this.parameterMaps = parameterMaps;
}
public Map<String, MappedStatement> getMappedStatements() {
return mappedStatements;
}
public void setMappedStatements(Map<String, MappedStatement> mappedStatements) {
this.mappedStatements = mappedStatements;
}
public MapperRegistry getMapperRegistry() {
return mapperRegistry;
}
public void setMapperRegistry(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
public <T> T getMapper(Class<MyInfoMapper> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
}
Environment
封装数据库信息
public class Environment {
private DataSource dataSource;
public Environment(DataSource dataSource) {
this.dataSource = dataSource;
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
public class DataSource {
private String driver;
private String url;
private String username;
private String password;
public DataSource() {
}
public DataSource(String driver, String url, String username, String password) {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
ResultMap
封装返回类的信息,其中有数据库字段与类属性的映射关系
/sql结果封装类的属性映射
public class ResultMap {
private String id;
private Class<?> type;
private List<ResultMapping> propertyResultMappings;
public ResultMap() {
}
public ResultMap(String id, Class<?> type, List<ResultMapping> propertyResultMappings) {
this.id = id;
this.type = type;
this.propertyResultMappings = propertyResultMappings;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
public List<ResultMapping> getPropertyResultMappings() {
return propertyResultMappings;
}
public void setPropertyResultMappings(List<ResultMapping> propertyResultMappings) {
this.propertyResultMappings = propertyResultMappings;
}
}
public class ResultMapping {
private String property;
private String column;
public ResultMapping(String property, String column) {
this.property = property;
this.column = column;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
public String getColumn() {
return column;
}
public void setColumn(String column) {
this.column = column;
}
}
ParameterMap
sql参数的对应java类
public class ParameterMap {
private String id;
private Class<?> type;
public ParameterMap(String id, Class<?> type) {
this.id = id;
this.type = type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
}
MappedStatement
sql的封装类,会包含上述result和parameter信息
public class MappedStatement {
private ParameterMap parameterMap;
private ResultMap resultMaps;
private String sql;
public MappedStatement(ParameterMap parameterMap, ResultMap resultMaps, String sql) {
this.parameterMap = parameterMap;
this.resultMaps = resultMaps;
this.sql = sql;
}
public ParameterMap getParameterMap() {
return parameterMap;
}
public void setParameterMap(ParameterMap parameterMap) {
this.parameterMap = parameterMap;
}
public ResultMap getResultMaps() {
return resultMaps;
}
public void setResultMaps(ResultMap resultMaps) {
this.resultMaps = resultMaps;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
}
MapperRegistry
对mapper接口进行注册
public class MapperRegistry {
//源码中是个map,存放扫描到的mapper接口类
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public Map<Class<?>, MapperProxyFactory<?>> getKnownMappers() {
return knownMappers;
}
public <T> T getMapper(Class<MyInfoMapper> type, SqlSession sqlSession) {
if (!knownMappers.containsKey(type)){
throw new RuntimeException("该mapper接口未被注册");
}
MapperProxyFactory<?> mapperProxyFactory = knownMappers.get(type);
return (T) mapperProxyFactory.newInstance(sqlSession);
}
}
MapperProxyFactory
MapperRegistry还封装了MapperProxyFactory,该类是对接口进行代理时创建代理对象的具体逻辑。其中methodCache就封装了sql和接口方法的映射关系。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
public T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
MapperMethod
sql和接口方法的映射关系
//这个类是映射接口的方法对应那个sql,这里简化书写,传了mappedStatements的key来获取sql
public class MapperMethod {
private String id;
public MapperMethod(String id) {
this.id = id;
}
public Object execute(SqlSession sqlSession, Object[] args) {
return sqlSession.selectOne(id, args[0]);
}
}
SqlSession
执行sql的具体对象,主要是根据接口,创建代理对象,依照Configuration执行对应sql
public class SqlSession {
private SimpleExecutor executor;
//源码里面是没有Configuration成员变量的,这里是为了方便传参
private Configuration configuration;
public SqlSession(SimpleExecutor executor, Configuration configuration) {
this.executor = executor;
this.configuration = configuration;
}
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else {
return null;
}
}
private <T> List<T> selectList(String statement, Object parameter) {
List var5;
try {
MappedStatement ms = configuration.getMappedStatement(statement);
//mybatis这里还执行了很多代码,判断语句,缓存优化等。
var5 = this.executor.query(ms, parameter,configuration);
} catch (Exception var9) {
throw new RuntimeException("查询失败...");
}
return var5;
}
public <T> T getMapper(Class<MyInfoMapper> type) {
return configuration.getMapper(type, this);
}
public Configuration getConfiguration() {
return configuration;
}
public SimpleExecutor getExecutor() {
return executor;
}
}
SimpleExecutor
SqlSession执行sql是让Executor来执行的,Mybatis中有三种Executor(Simple、Cache、Batch),这里仅模拟了SimpleExecutor
public class SimpleExecutor {
public <E> List<E> query(MappedStatement ms, Object parameter, Configuration configuration) {
List list = this.queryFromDatabase(ms, parameter,configuration);
return list;
}
private List queryFromDatabase(MappedStatement ms, Object parameter, Configuration configuration) {
List list = this.doQuery(ms, parameter,configuration);
return list;
}
private List doQuery(MappedStatement ms, Object parameter, Configuration configuration) {
List var9;
//mybatis返回的是sql语句对象Statement,但是prepareStatement方法里面真正连接了数据库
//所以为了简化书写,这里直接返回了Connection
Connection connection = this.prepareStatement(configuration.getEnvironment());
//mybatis这里是用StatementHandler对象的query方法,执行查询
//为了书写方便,我直接传了jdbc的connection,源码里面并不是这么写的
SimpleStatementHandler simpleStatementHandler = new SimpleStatementHandler();
var9 = simpleStatementHandler.query(connection,ms,parameter);
return var9;
}
private Connection prepareStatement(Environment environment) {
//因为mybatis是对jdbc的封装,所以直接用jdbc的原生代码生成
Connection connection=null;
DataSource dataSource = environment.getDataSource();
try {
Class.forName(dataSource.getDriver()); //加载对应驱动
connection = (Connection) DriverManager.getConnection(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
SimpleStatementHandler
StatementHandler是执行sql的核心位置,Mybatis里面还会进入handler内的其他方法,最后到sql的预编译以及执行。这里也作了简化,直接执行。并且创建返回类,我也是用了暴力反射,根据Configuration的resultMap的属性映射,注入属性值,最后返回对象。
public class SimpleStatementHandler {
public List query(Connection connection, MappedStatement statement, Object parameter) {
String sql = statement.getSql();
List list = new ArrayList();
try {
PreparedStatement preparedStatement=null;
if (sql.contains("#{")){
sql = sql.replace("#{id}", "?");
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, ((Integer) parameter));
}else {
preparedStatement = connection.prepareStatement(sql);
}
ResultSet resultSet = preparedStatement.executeQuery();
Object result=null;
while (resultSet.next()) {
Class resultClass = statement.getResultMaps().getType();
result = resultClass.newInstance();
for (ResultMapping propertyResultMapping : statement.getResultMaps().getPropertyResultMappings()) {
String columnValue = resultSet.getString(propertyResultMapping.getColumn());
if (columnValue!=null && !columnValue.equals("")){
Field field = resultClass.getDeclaredField(propertyResultMapping.getColumn());
field.setAccessible(true);
field.set(result,columnValue);
}
}
}
list.add(result);
} catch (SQLException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return (List) list;
}
}
Main方法及其测试结果
Main方法
该方法的流程,也就对应上文的执行流程图。
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession session = sqlSessionFactory.openSession();
//mybatis只有在执行sql时候才会连接数据库,所以手写框架也是在这里面先连接数据库
//后执行sql
//(1)直接使用session的方法进行执行,传参需要传mapper.xml中sql的id(namespace+id)
MyInfo myInfo = session.selectOne("com.CmJava.mapper.MyInfoMapper.getMyInfo",1);
//(2)session获取mapper的代理对象,让代理对象执行,这也是日常开发中是用mybatis后台的实际操作流程
MyInfoMapper mapper = session.getMapper(MyInfoMapper.class);
MyInfo myInfo1 = mapper.getMyInfo(1);
System.out.println(myInfo);
System.out.println(myInfo1);
}
测试结果
如图所示
评论区