😀 这里写文章的前言: 一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么?并简述你给出的答案。
可以说说你的故事:阻碍、努力、结果成果,意外与转折。
📝 MyBatis 作为一名 “优秀的” CRUD 工程师,如果不看MyBatis是个怎么大概的执行流程的话,那还真是有点可惜. 所以阅读一下其源码和执行流程,是很有帮助的. 同时,看下其代码(设计模式等对成长也是很有帮助的).
代码 github 源码地址:https://github.com/mybatis/mybatis-3
官网文档地址: https://mybatis.org/mybatis-3/zh/configuration.html#properties
先将github上的代码clone下来,导入到本地的idea中. 然后按照官方文档的demo搭建就可以了. 这里自行创建一个 resources 目录即可,也就是放置一些配置文件信息的
实体类 public class Phone { private Integer id; private String phone; public Phone () { } public Phone (Integer id, String phone) { this .id = id; this .phone = phone; } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getPhone () { return phone; } public void setPhone (String phone) { this .phone = phone; } @Override public String toString () { return "Phone{" + "id=" + id + ", phone='" + phone + '\'' + '}' ; } }
接口 再写一个Mapper的接口. 可以看到我这里使用的方法都没修改,不能遗忘 Ctrl + C/V大师功能
public interface PhoneMapper { Phone selectBlog (Integer id) ;}
配置文件 MyBatis的 xml 配置文件来一份: resouces下创建: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> <environments default ="development" > <environment id="development" > <transactionManager type="JDBC" /> <dataSource type="POOLED" > <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/two" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <mappers> <mapper resource="pojo/PhoneMapper.xml" /> </mappers> </configuration>
实体对应的xml文件 对用的pojo的Mapper的xml也给来一份 : pojo/PhoneMapper.xml , 这里是在 resouces下的pojo文件夹中
<?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.ibatis.mapper.PhoneMapper" > <select id="selectBlog" resultType="org.apache.ibatis.pojo.Phone" > select * from phone where id = #{id} </select> </mapper>
启动类 最后写一个启动类即可: StartMain
这里先运行一下,看下能不能获取到我们想要的结果,也就是有没有错误或者异常等信息给报出来. 如果没有的话,就说明是ok的. 然后就可以开始我们愉快的debug旅行了
public class StartMain { public static void main (String[] args) throws Exception { String resouce = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resouce); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); PhoneMapper phoneMapper = session.getMapper(PhoneMapper.class); System.out.println(phoneMapper.selectBlog(1 ).toString()); } }
debug 这里我们只需要跟着 StartMain 中的方法,一步一步的走下去, 弄明白每个方法是说明意思,然后在其内部是怎么走的
Resources.getResourceAsStream(resouce) 方法:
public static InputStream getResourceAsStream (ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null ) { throw new IOException ("Could not find resource " + resource); } return in; } InputStream getResourceAsStream (String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { if (null != cl) { InputStream returnValue = cl.getResourceAsStream(resource); if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null ; }
new SqlSessionFactoryBuilder().build(inputStream) 方法: 该方法可以看其返回的结果,是返回了一个SqlSessionFactroy的回话工厂.
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
该方法最后是走到了这里.
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) { } } } --------------------------------- new XMLConfigBuilder 构造方法 public XMLConfigBuilder (InputStream inputStream, String environment, Properties props) { this (new XPathParser (inputStream, true , props, new XMLMapperEntityResolver ()), environment, props); } ---------------------------------- org.apache.ibatis.builder.xml.XMLConfigBuilder#parse (parser.parse()方法) public Configuration parse () { if (parsed) { throw new BuilderException ("Each XMLConfigBuilder can only be used once." ); } parsed = true ; parseConfiguration(parser.evalNode("/configuration" )); return configuration; } private void parseConfiguration (XNode root) { try { 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); 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); } }
sqlSessionFactory.openSession() 方法:
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
该方法主要就是获取一个SqlSession会话.
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); throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
session.getMapper(PhoneMapper.class); 获取Mapper的方法
org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
这里如果细心的话,可以看到其debug的这个类的值或者使用输出给值打印出来:System.out.println(phoneMapper.getClass().toString()); –>class com.sun.proxy.$Proxy0,就可以很明显的看到这是一个代理类了
@Override public <T> T getMapper (Class<T> type) { return configuration.getMapper(type, this ); } public <T> T getMapper (Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } --------------------- public <T> T getMapper (Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null ) { throw new BindingException ("Type " + type + " is not known to the MapperRegistry." ); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException ("Error getting mapper instance. Cause: " + e, e); } }
🤗 总结归纳 总结文章的内容
📎 参考文章
💡 有关文章的问题,欢迎您在底部评论区留言,一起交流~