框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另外一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面然后者是从目的方面给出的定义。
简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你本身的系统,或者说是使用别人搭好的舞台,你来作表演。框架通常是成熟的,不断升级的软件。java
框架要解决的最重要的一个问题是技术整合的问题,在JavaEE
的 框架中,有着各类各样的技术,不一样的软件企业须要从JavaEE
中选择不一样的技术,这就使得软件企业最终的应用依赖于这些技术,技术自身的复杂性和技术的风险性将会直接对应用形成冲击。而应用是软件企业的核心,是竞争力的关键所在,所以应该将应用自身的设计和具体的实现技术解耦。这样,软件企业的研发将集中在应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不该该直接对应用产生影响。mysql
框架的重要性在于它实现了部分功能,而且可以很好的将低层应用平台和高层业务逻辑进行了缓和。为了实现软件工程中的“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源。咱们常见的MVC
软件设计思想就是很好的分层思想。sql
经过分层更好的实现了各个部分的职责,在每一层将再细化出不一样的框架,分别解决各层关注的问题。数据库
Mybatis
apache
MyBatis
本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation
迁移到了google code
,而且更名为MyBatis
。2013年11月迁移到Github
。编程
做为持久层的框架,还有一个封装程度更高的框架就是Hibernate
,但这个框架由于各类缘由目前在国内的流行程度降低太多,如今公司开发也愈来愈少使用。目前Mybatis
框架是主流,将来使用Spring Data
来实现数据持久化也是一种趋势。设计模式
SpringMVC
服务器
Spring MVC
属于SpringFrameWork
的后续产品,已经融合在Spring Web Flow
里面。Spring
框架提供了构建 Web
应用程序的全功能 MVC
模块。使用 Spring
可插入的 MVC
架构,从而在使用Spring
进行WEB
开发时,能够选择使用Spring
的SpringMVC
框架或集成其余MVC
开发框架,如Struts1
(如今通常不用),Struts2
(通常老项目使用)等。session
Spring
框架是因为软件开发的复杂性而建立的。Spring
使用的是基本的JavaBean
来完成之前只可能由EJB
完成的事情。然而,Spring的用途不只仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用均可以从Spring中受益。mybatis
目的:解决企业应用开发的复杂性
功能:使用基本的JavaBean
代替EJB
,并提供了更多的企业应用功能
范围:任何Java应用
Spring是一个轻量级控制反转(IoC
)和面向切面(AOP
)的容器框架。
Mybatis
框架概述 Mybatis
是一个优秀的基于Java的持久层框架,它内部封装了Jdbc
,使开发者只须要关注sql
语句自己,而不须要花费精力去处理加载驱动、建立链接、建立statement等繁杂的过程。
Mybatis
经过xml
或注解的方式将要执行的各类statement配置起来,并经过java
对象和statement中sql
的动态参数进行映射生成最终执行的sql
语句,最后由Mybatis
框架执行sql
并将结果映射为java
对象并返回。
采用ORM
思想解决了实体和数据库映射的问题,对Jdbc
进行了封装,屏蔽了Jdbc Api
底层访问细节,使咱们不用与Jdbc Api
打交道,就能够完成对数据库的持久化操做。
为了咱们可以更好掌握框架运行的内部过程,而且有更好的体验,下面咱们将从自定义Mybatis
框架开始来学习框架。此时咱们将会体验框架从无到有的过程体验,也可以很好的综合前面阶段所学的基础。
JDBC
编程的分析一、建立数据库,数据库名 : mybatis
二、将以下Sql
语句在数据库中执行,初始化测试数据
-- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL COMMENT '用户名称', `sex` char(1) DEFAULT NULL COMMENT '性别', `birthday` date DEFAULT NULL COMMENT '生日', `address` varchar(256) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', '张三', '1', '2018-07-10', '北京'); INSERT INTO `user` VALUES ('2', '李四', '1', '2018-07-10', '上海'); INSERT INTO `user` VALUES ('3', '王五', '1', '2018-07-10', '广州'); INSERT INTO `user` VALUES ('4', '王六', '1', '2018-07-10', '深圳');
JDBC
程序回顾public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 一、加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); // 二、获取数据库连接 connection = DriverManager. getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root"); // 三、定义sql语句 ?表示占位符 String sql = "select * from user where username = ?"; // 四、获取预处理statement preparedStatement = connection.prepareStatement(sql); // 五、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 preparedStatement.setString(1, "张三"); // 六、向数据库发出sql执行查询,查询出结果集 resultSet = preparedStatement.executeQuery(); // 七、解析处理结果集 while (resultSet.next()) { System.out.println(resultSet.getString("id") + " " + resultSet.getString("username")); } } catch (Exception e) { e.printStackTrace(); } finally { // 八、释放资源 if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
上边使用JDBC
的原始方法(未经封装)实现了查询数据库表记录的操做。
JDBC
问题分析数据库连接建立、释放频繁形成系统资源浪费从而影响系统性能,若是使用数据库连接池可解决此问题。
Sql
语句在代码中硬编码,形成代码不易维护,实际应用sql
变化的可能较大,sql
变更须要改变Java代码。
使用preparedStatement
向占有位符号传参数存在硬编码,由于sql
语句的where条件不必定,可能多也可能少,修改sql
还要修改代码,系统不易维护。
对结果集解析存在硬编码(查询列名),sql
变化致使解析代码变化,系统不易维护,若是能将数据库记录封装成pojo
对象解析比较方便。
下面咱们将经过本身定义编写一个持久层框架的方式来解决Jdbc
编程存在的部分问题。
Mybatis
框架Mybatis
框架的前期准备 本章咱们将使用前面所学的基础知识来构建一个属于本身的持久层框架,将会涉及到的一些知识点:工厂模式(Factory工厂模式)、构建者模式(Builder模式)、反射、Xml
解析,数据库元数据等。
工厂模式是咱们最经常使用的实例化对象模式了,是用工厂方法代替new操做的一种模式。著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统能够说是随处可见。由于工厂模式就至关于建立实例对象的new,咱们常常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来建立实例对象的,因此之后new时就要多个心眼,是否能够考虑使用工厂模式,虽然这样作,可能多作一些工做,但会给你系统带来更大的可扩展性和尽可能少的修改量。
构建者模式,又称建造者模式,将一部负责对象的构建分为许多小对象的构建,最后在整合构建的模式。
成员分析:
Director:控制者类,这是控制整个组合过程,在这个类内部有个Construct()方法,这个方法的做用就是经过调用Builder内部的各个组件的生成方法来完成组装;
Builder:构建者接口,定义各部件生成的方法;
ConcreteBuilder
:具体构建者类:实现Builder构建者接口,具体定义如何生成各个部件;依赖于Product成品类,其中还有获取成品组装结构的方法GetResult()
方法;
Product:成品类
JDK
环境 : JDK 1.8 64bit
在Idea中建立Maven工程,名字为customMybatis
在pom.xml
中引入所需坐标
<dependencies> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.6</version> </dependency> </dependencies>
Mybatis
框架配置文件结构约定Sql
文件约定 做为一个框架,一般都会定义本身的配置文件(能够有多个),用户只须要按照框架的要求编写相应的配置文件就能够了。由于咱们自定义的Mybatis
框架是一个持久层框架(操做数据库的),因此须要配置文件用于存放Sql
语句,此类配置文件一般称为Sql
映射文件。配置文件的名称能够任意,可是最好有一个统一的规范,方便见名知意,此处咱们的约定是XXMapper.xml
(XX为该文件所对应的表名,如User表为UserMapper.xml
)。具体格式以下:
<?xml version="1.0" encoding="utf-8" ?> <mapper namespace="com.hyl.pojo.User"> <select id="findAllUsers" resultType="com.hyl.pojo.User"> select * from user </select> </mapper>
除了映射文件(存放SQL
语句的文件),还须要有一个框架的核心配置文件,主要是存放数据源相关信息的。由于不一样的用户使用的数据库类型不一样,数据库名称不一样,使用的数据库用户和密码也都不相同,因此这些信息应该是由用户本身指定。用户经过这个核心配置文件就能够指定数据源相关信息了。此处咱们约定配置文件名称为SqlMapConfig.xml
。具体格式以下:
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
经过前面Jdbc
存在问题的分析,咱们已经知道了要将SQL
语句放入配置文件中,这样未来修改SQL
语句会比较方便,但放在配置文件中的SQL
语句还须要读取出来,这样咱们就能够基于面向对象思惟定义一个Mapper类,用于将配置文件中的SQL
语句保存起来,使用时更方便。定义以下:
package frame.pojo; public class Mapper { private String sql; private String resultType; public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } }
咱们以面向对象思惟操做自定义框架的核心配置文件时,须要有一个实体类与之对应。Configuration 配置类主要用于保存SqlMapConfig.xml
文件中读取的xml
结点的信息,以及映射的SQL
语句的集合。定义以下:
package frame.pojo; import java.util.HashMap; import java.util.Map; public class Configuration { private String driver; private String url; private String username; private String password; private Map<String,Mapper> mappers = new HashMap<>(); public Map<String, Mapper> getMappers() { return mappers; } public void setMappers(Map<String, Mapper> mappers) { this.mappers = mappers; } 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; } }
SqlSession
开发 咱们开发的是框架,所以封装的对象是不固定的,所以这里使用泛型
package frame.core; import java.util.List; public interface SqlSession { public <T> List<T> selectList(String mapperId) throws Exception; }
SqlSessionImpl
实现类开发package frame.core; import frame.core.Executor; import frame.pojo.Configuration; import java.util.List; public class SqlSessionImpl implements SqlSession { private Configuration configuration; public void setConfiguration(Configuration configuration) { this.configuration = configuration; } @Override public <T> List<T> selectList(String mapperId) throws Exception { List<T> list = new ArrayList<>(); Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { Class.forName(configuration.getDriver()); connection = DriverManager.getConnection(configuration.getUrl(),configuration.getUsername(),configuration.getPassword()); Mapper mapper = configuration.getMappers().get(mapperId); String sql = mapper.getSql(); preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); String resultType = mapper.getResultType(); Class clazz = Class.forName(resultType); Method[] mehtods = clazz.getMethods(); List<String> columnNames = new ArrayList<>(); ResultSetMetaData metaData = resultSet.getMetaData(); int count = metaData.getColumnCount(); for(int i = 1; i <= count; i++) { columnNames.add(metaData.getColumnLabel(i)); } Object object = null; while (resultSet.next()) { object = clazz.newInstance(); for(String columnName : columnNames) { for(Method method: mehtods) { if(method.getName().equalsIgnoreCase("set" + columnName)) { method.invoke(object,resultSet.getObject(columnName)); } } } list.add((T) object); } } catch (Exception e) { throw e; } finally { if(resultSet != null) { resultSet.close(); } if(preparedStatement != null) { preparedStatement.close(); } if(connection != null){ connection.close(); } } return list; } }
SqlSessionFactory
开发 SqlSessionFactory
的开发基于工厂模式,工厂模式是咱们最经常使用的用来实例化对象的设计模式,是用工厂方法代替new操做的一种模式。建立对象的时候使用工厂模式会带来更大的可扩展性和尽可能少的修改量。
package frame.factory; import frame.core.SqlSession; import frame.core.SqlSessionImpl; import frame.pojo.Configuration; import frame.pojo.Mapper; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.List; public class SqlSessionFactory { public void setInputStream(InputStream inputStream) { this.inputStream = inputStream; } private InputStream inputStream; public SqlSession openSession() { SqlSessionImpl sqlSession = new SqlSessionImpl(); Configuration configuration = loadXmlConfig(); sqlSession.setConfiguration(configuration); return sqlSession; } private Configuration loadXmlConfig() { Configuration configuration = new Configuration(); try { SAXReader saxReader = new SAXReader(); Document document = saxReader.read(inputStream); Element root = document.getRootElement(); List<Element> list = root.selectNodes("//property"); for (int i = 0; i < list.size(); i++) { Element element = list.get(i); String name = element.attributeValue("name"); String value = element.attributeValue("value"); if("driver".equalsIgnoreCase(name)) { configuration.setDriver(value); } if("url".equalsIgnoreCase(name)){ configuration.setUrl(value); } if("username".equalsIgnoreCase(name)) { configuration.setUsername(value); } if("password".equalsIgnoreCase(name)) { configuration.setPassword(value); } } List<Element> mappers = root.selectNodes("//mapper"); for (int i = 0; i < mappers.size(); i++) { Element element = mappers.get(i); String mapperPath = element.attributeValue("resource"); // 读取mapper文件 loadXmlMapper(configuration,mapperPath); } } catch (Exception e) { e.printStackTrace(); } return configuration; } private void loadXmlMapper(Configuration configuration,String mapperPath) { SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(this.getClass().getClassLoader().getResourceAsStream(mapperPath)); Element root = document.getRootElement(); String namespace = root.attributeValue("namespace"); List<Element> list = root.selectNodes("//select"); for (int i = 0; i < list.size(); i++) { Element element = list.get(i); String id = element.attributeValue("id"); String resultType = element.attributeValue("resultType"); String sql = element.getText(); Mapper mapper = new Mapper(); mapper.setResultType(resultType); mapper.setSql(sql); configuration.getMappers().put(namespace + "." + id,mapper); } } catch (DocumentException e) { e.printStackTrace(); } } }
注:XML 文件解析采用的是Dom4j
结合xpath
实现。
SqlSessionFactoryBuilder
开发使用构建者模式,应对不一样的场景变化。
package frame.factory; import frame.factory.SqlSessionFactory; import java.io.InputStream; public class SqlSessionFactoryBuilder { public SqlSessionFactory build() { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(); sqlSessionFactory.setInputStream(inputStream); return sqlSessionFactory; } public SqlSessionFactory build(String filename) { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(filename); SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(); sqlSessionFactory.setInputStream(inputStream); return sqlSessionFactory; } public SqlSessionFactory build(InputStream inputStream) { SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(); sqlSessionFactory.setInputStream(inputStream); return sqlSessionFactory; } }
自定义Mybatis框架架构图以下:
1.通过上面的开发过程,工程的结构以下:
使用Idea自带的打包和安装功能将当前工程打成Jar包,而后安装到到本地Maven仓库,双击Install图标便可:
查看本地Maven仓库
基于自定义Mybatis框架和已有的Mysql数据库,查询全部用户信息。
建立一个Maven工程testCustomMybatis
修改testCustomMybatis工程的pom.xml文件,添加自定义Mybatis框架的Maven坐标和Mysql数据库驱动的坐标
<dependencies> <dependency> <groupId>com.hyl.mybatis</groupId> <artifactId>customMybatis</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
package com.hyl.mybatis.pojo; import java.util.Date; public class User { private Integer id; private String username; private String sex; private Date birthday; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + ", address='" + address + '\'' + '}'; } }
<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="test"> <!-- select 查询 --> <select id="queryUserList" resultType="com.hyl.mybatis.pojo.User"> select * from user </select> </mapper>
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" ></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8" ></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"></mapper> </mappers> </configuration>
@Test public void testQueryUserList() throws Exception { SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build("SqlMapConfig.xml"); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> list = sqlSession.selectList("test.queryUserList"); for (User user : list) { System.out.println(user); } }
测试结果
以上经过自定义Mybatis框架的学习,咱们将前面的基础知识很好的结合在一块儿,而且强化了咱们对设计模式的使用。但愿你们可以抽时间多练习,这也是系统架构师的必由之路。
Mybatis
框架快速入门 经过前面的学习,咱们已经可以使用所学的基础知识构建自定义的Mybatis
框架了。这个过程是基本功的考验,咱们已经强大了很多,但现实是残酷的,咱们所定义的Mybatis
框架和真正的Mybatis
框架相比,仍是显得眇小。行业内所流行的Mybatis
框架如今咱们将开启学习。
Mybatis
框架进入官方,https://mybatis.org/mybatis-3/
快速开发,咱们使用3.4.5的版本
Mybatis
开发环境在pom.xml文件中添加坐标,以下:
<dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
log4j.properties
在resources下建立log4j.properties,内容以下:
# Global logging configuration log4j.rootLogger=DEBUG, stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
Mybatis默认使用log4j做为输出日志组件。
package com.hyl.mybatis.pojo; import java.util.Date; public class User { private Integer id; private String username; private String sex; private Date birthday; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + ", address='" + address + '\'' + '}'; } }
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="test"> <!-- select 查询 --> <select id="queryUserList" resultType="com.hyl.mybatis.pojo.User"> select * from user </select> </mapper>
SqlMapConfig.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/mybatis?characterEncoding=utf8" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"></mapper> </mappers> </configuration>
@Test public void testQueryUserList() throws Exception { InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> list = sqlSession.selectList("test.queryUserList"); for (int i = 0; i < list.size(); i++) { User user = list.get(i); System.out.println(user); } }
经过快速入门示例,咱们发现前面的自定义Mybatis
框架和咱们官方的Mybatis
框架不少是相同的,经过这个推导过程告诉咱们,只要基础扎实,咱们彻底能够运用基础知识,将一个框架的基本原理掌握好。