众所周知,MyBatis
MyBatis
是对JDBC
进行封装而成的产品,所以,聊MyBatis源码之前我们得先了解JDBC
。
JDBC案例:
public class JdbcDemo { public static final String URL = "jdbc:mysql://localhost:3306/mblog"; public static final String USER = "root"; public static final String PASSWORD = "123456"; public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1"); while(rs.next()){ System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age")); } } }
说明:
数据库驱动:
Class.forName("com.mysql.jdbc.Driver");
获取连接:
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
创建Statement
或者PreparedStatement
是对
JDBC
🎜进行封装而成的产品,所以,聊MyBatis源码之前我们得先了解🎜JDBC
🎜。🎜🎜JDBC案例:🎜
Statement stmt = conn.createStatement();
说明:🎜
数据库驱动:🎜
ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
获取连接:🎜
System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age"));
创建Statement
或者PreparedStatement
对象:🎜
Statement stmt = conn.createStatement();
执行sql数据库查询:
ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
解析结果集:
System.out.println("name: "+rs.getString("name")+" 年龄:"+rs.getInt("age"));
在使用的时候,业务处理完成后记得关闭相关资源
使用过JDCB的朋友都知道,JDBC如果用到我们项目中基本上都会存在以下几个问题:
针对上面这些问题,于是一大堆持久化框架应运而生。
做持久层的框架有很多,有orm
系和utils
系列。
orm
系列的代表有: 休眠
,eclipseLink
,topLink
;orm
系列的代表有:hibernate
,eclipseLink
,topLink
;utils
系列的代表有:MyBatis
,dbUtils
,jdbcTemplate
utils
系列的代表有:,dbUtils
,jdbcTemplate
等;
下面针对这些框架做个简单概述:
至于jpa,只是一个规范标准,不是具体框架,不直接于spring-data-jpa;同时 spring-data-jpa 也不是 jpa 的具体实现,它只是 jpa 规范标准的扩展封装。hibernate 是 jpa 常见的实现框架,当然还有 eclipseLink,topLink。MyBatis 的特点是在对 SQL 优化时,复杂 SQL 的优化可控性高,框架内部调用层次简单,除了部分可以自动生成代码,还有很多 SQL 需要自行编码。 spring-data-jpa(hibernate) 的特点是在开发过程中,剥离 SQL 编码开发,当然也支持本地 SQL 来查询,框架内部调用层次复杂。 以上就可以根据实际的业务细节和业务支撑情况做出选择了。
MyBatis
其实可以在一个项目中同时支持MyBatis 和 spring-data-jpa,复杂的SQL走mybatis,常用的SQL走spring-data-jpa。
新加入开发的朋友,估计不知道MyBatis 的前身,在2010年之前,不交MyBatis
,叫ibatis
。MyBatis
,叫ibatis
。
MyBatis
是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis
可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs
MyBatis
是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis
可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs
(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录 。🎜需要来源两个jar包:MyBatis
的jar包和MySQL
数据库连接jar包。
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency>
创建一个表t_user(数据库也是肯定要自己创建的哈)
CREATE TABLE `t_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
插入一条数据:
INSERT INTO `t_user` VALUES ('1', 'tian', '19', '1');
创建该数据库表的实体类:
public class User { private Integer id; private String name; private Integer age; //set get }
创建mapper配置文件: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="com.tian.mapper.UserMapper"> <select id="selectUserById" resultType="com.tian.domain.User"> select * from t_user where id = #{id} </select> </mapper>
创建mapper接口:UserMapper.java
import com.tian.domain.User; public interface UserMapper { User selectUserById(Integer id); }
MyBatis 整体配置文件:
<?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.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mblog?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers> </configuration>
上面这些就是我们使用MyBatis
基本开发代码。
下面我们来写一个测试类:
import com.tian.domain.User; import com.tian.mapper.UserMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MybatisApplication { public static void main(String[] args) { String resource = "mybatis-config.xml"; InputStream inputStream = null; SqlSession sqlSession =null; try { //读取配置文件 inputStream = Resources.getResourceAsStream(resource); //创建SqlSession工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //创建sql操作会话 sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper=sqlSession.getMapper(UserMapper.class); //获取数据并解析成User对象 User user = userMapper.selectUserById(1); //输出 System.out.println(user); } catch (Exception e) { e.printStackTrace(); }finally { //关闭相关资源 try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } sqlSession.close(); } } }
测试结果:
User{id=1, name='tian', age=19}
如上面的代码所示,SqlSession
是MyBatis中提供的与数据库交互的接口,SqlSession实例通过工厂模式创建。
为了创建SqlSession
对象,首先需要创建SqlSessionFactory
对象,而SqlSessionFactory
对象的创建依赖于SqlSessionFactoryBuilder
类,该类提供了一系列重载的build()方法,我们需要以主配置文件的输入流作为参数调用SqlSessionFactoryBuilder
对象的bulid()
方法,该方法返回一个SqlSessionFactory
对象。
有了SqlSessionFactory
对象之后,调用SqlSessionFactory对象的openSession()
方法即可获取一个与数据库建立连接的SqlSession
实例。SqlSessionFactory
对象之后,调用SqlSessionFactory
对象的openSession()
方法即可获取一个与数据库建立连接的SqlSession
实例。
前面我们定义了UserMapper
接口,这里需要调用SqlSession
的getMapper()
方法创建一个动态代理对象,然后调用UserMapper
代理实例的方法即可完成与数据库的交互。
针对上面这个案例,我们来梳理一下MyBatis
UserMapper
接口,这里需要调用SqlSession
的 getMapper()
方法创建一个动态代理对象,然后调用UserMapper
代理实例的方法可以完成与数据库的交互。用于描述MyBatis
的主配置信息,其他组件需要获取配置信息时,直接通过Configuration
对象获取。除此之外,MyBatis在应用启动时,将Mapper配置信息、类型别名、TypeHandler
等注册到Configuration
组件中,其他组件需要这些信息时,也可以从Configuration
对象中获取。MyBatis
的主配置信息,其他组件需要获取配置信息时,直接通过Configuration
对象获取。除此之外,MyBatis在应用启动时,将Mapper配置信息、类型别名、TypeHandler
等注册到Configuration
组件中,其他组件需要这些信息时,也可以从Configuration
对象中获取。
MappedStatement用于描述Mapper中的SQL配置信息,是对Mapper XML
配置文件中b5d5a6a8d1a81baf21ea188537c0af35
等标签或者@Select/@Update
等注解配置信息的封装。
SqlSession
是MyBatis提供的面向用户的API,表示和数据库交互时的会话对象,用于完成数据库的增删改查功能。SqlSession
Mapper XML
配置文件中b5d5a6a8d1a81baf21ea188537c0af35
等标签或者@Select/@Update
等注解配置信息的封装。🎜🎜🎜🎜SqlSession🎜🎜🎜🎜SqlSession
是MyBatis提供的面向用户的API,表示和数据库交互时的会话对象,用于完成数据库的增删改查功能。SqlSession
是Executor组件的外观,目的是对外提供易于理解和使用的数据库操作接口。🎜Executor
是MyBatis的SQL执行器,MyBatis中对数据库所有的增删改查操作都是由Executor组件完成的。Executor
是MyBatis的SQL执行器,MyBatis中对数据库所有的增删改查操作都是由Executor组件完成的。
StatementHandler
封装了对JDBC Statement
对象的操作,比如为Statement对象设置参数,调用Statement接口提供的方法与数据库交互,等等。
当MyBatis
框架使用的Statement类型为CallableStatement
和PreparedStatement
时,ParameterHandler
用于为Statement对象参数占位符设置值。
ResultSetHandler
StatementHandler
封装了对JDBC Statement
对象的操作,比如为Statement对象设置参数,调用Statement接口提供的方法与数据库交互,等等。🎜🎜🎜🎜ParameterHandler🎜🎜🎜🎜当MyBatis
框架使用的Statement类型为CallableStatement
和PreparedStatement
时,ParameterHandler
用于为Statement对象参数占位符设置值。🎜🎜🎜🎜ResultSetHandler🎜🎜🎜🎜ResultSetHandler
封装了对JDBC中的ResultSet对象操作,当执行SQL类型为SELECT语句时,ResultSetHandler用于将查询结果转换成Java对象。🎜TypeHandler
是MyBatis中的类型处理器,用于处理Java类型与JDBC类型之间的映射。它的作用主要体现在能够根据Java类型调用PreparedStatement
或CallableStatement
对象对应的setXXX()
方法为Statement对象设置值,而且能够根据Java类型调用ResultSet对象对应的getXXX()
获取SQL执行结果。TypeHandler
是MyBatis中的类型处理器,用于处理Java类型与JDBC类型之间的映射。它的作用主要体现在能够根据Java类型调用PreparedStatement
或CallableStatement
对象对应的setXXX()
方法为Statement对象设置值,而且能够根据Java类型调用ResultSet对象对应的getXXX()
获取SQL执行结果。
使用JDBC API
JDBC API
开发应用程序,其中一个比较烦琐的环节是处理JDBC类型与Java类型之间的转换。涉及Java类型和JDBC类型转换的两种情况如下:🎜PreparedStatement
对象为参数占位符设置值时,需要调用PreparedStatement
接口中提供的一系列的setXXX()
方法,将Java类型转换为对应的JDBC类型并为参数占位符赋值。PreparedStatement
对象为参数占位符设置值时,需要调用PreparedStatement
接口中提供的一系列的setXXX()
方法,将Java类型转换为对应的JDBC类型并为参数占位符赋值。ResultSet
对象后,需要调用ResultSet对象的getXXX()
方法获取字段值,此时会将JDBC类型转换为Java类型。MyBatis
提供的TypeHandler
及与Java类型和JDBC类型之间的对应关系:
我们使用到了SqlSession
组件,它是用户层面的API。实际上SqlSession
是Executor组件的外观,目的是为用户提供更友好的数据库操作接口,这是设计模式中外观模式的典型应用。
真正执行SQL操作的是Executor组件,Executor可以理解为SQL执行器,它会使用StatementHandler
ResultSet
对象后,需要调用ResultSet对象的getXXX()
方法获取字段值,此时会将JDBC类型转换为Java类型。MyBatis
提供的TypeHandler
及与Java类型和JDBC类型之间的对应关系:🎜
我们使用到了SqlSession
组件,它是用户层面的API。实际上SqlSession
是Executor组件的外观,目的是为用户提供更友好的数据库操作接口,这是设计模式中外观模式的典型应用。🎜
真正执行SQL操作的是Executor组件,Executor可以理解为SQL执行器,它会使用StatementHandler
组件对JDBC的Statement对象进行操作。🎜
当Statement类型为CallableStatement
和PreparedStatement
时,会通过ParameterHandler组件为参数占位符赋值。ParameterHandler
组件中会根据Java类型找到对应的TypeHandler
对象,TypeHandler中会通过Statement
对象提供的setXXX()
方法(例如setString()方法)为Statement对象中的参数占位符设置值。CallableStatement
和PreparedStatement
时,会通过ParameterHandler组件为参数占位符赋值。ParameterHandler
组件中会根据Java类型找到对应的TypeHandler
对象,TypeHandler中会通过Statement
对象提供的setXXX()
方法(例如setString()方法)为Statement对象中的参数占位符设置值。
StatementHandler
组件使用JDBC中的Statement对象与数据库完成交互后,当SQL语句类型为SELECT时,MyBatis通过ResultSetHandler
StatementHandler
组件使用JDBC中的Statement对象与数据库完成交互后,当SQL语句类型为SELECT时,MyBatis通过ResultSetHandler
组件从Statement对象中获取ResultSet对象,然后将ResultSet对象转换为Java对象。在 Web 应用中,缓存是必不可少的组件。通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力。作为一个重要的组件,MyBatis 自然也在内部提供了相应的支持。通过在框架层面增加缓存功能,可减轻数据库的压力,同时又可以提升查询速度,可谓一举两得。
MyBatis 缓存结构由一级缓存
和二级缓存
构成,这两级缓存均是使用 Cache 接口的实现类。因此,在接下里的章节中,我将首先会向大家介绍 Cache 几种实现类的源码,然后再分析一级和二级缓存的实现。
MyBatis一级缓存和二级缓存的使用:MyBatis一级缓存是SqlSession级别的缓存,默认就是开启的,而且无法关闭;二级缓存需要在MyBatis主配置文件中通过设置cacheEnabled参数值来开启。
一级缓存是在Executor中实现的。MyBatis的Executor组件有3种不同的实现,分别为SimpleExecutor
、ReuseExecutor
和BatchExecutor
。这些类都继承自BaseExecutor
,在BaseExecutor类的query()方法中,首先从缓存中获取查询结果,如果获取不到,则从数据库中查询结果,然后将查询结果缓存起来。而MyBatis的二级缓存则是通过装饰器模式实现的,当通过cacheEnabled参数开启了二级缓存,MyBatis框架会使用CachingExecutor对SimpleExecutor
、ReuseExecutor
或者BatchExecutor
进行装饰,当执行查询操作时,对查询结果进行缓存,执行更新操作时则更新二级缓存。本章最后介绍了MyBatis
如何整合Redis作为二级缓存。SimpleExecutor
、ReuseExecutor
和BatchExecutor
。这些类都继承自BaseExecutor
,在BaseExecutor类的query()方法中,首先从缓存中获取查询结果,如果获取不到,则从数据库中查询结果,然后将查询结果缓存起来。而MyBatis的二级缓存则是通过装饰器模式实现的,当通过cacheEnabled参数开启了二级缓存,MyBatis框架会使用CachingExecutor对SimpleExecutor
、ReuseExecutor
或者BatchExecutor
进行装饰,当执行查询操作时,对查询结果进行缓存,执行更新操作时则更新二级缓存。本章最后介绍了MyBatis
如何整合Redis作为二级缓存。
除此之外,MyBatis还支持Ehcache
、OSCache
Ehcache
、OSCache
等,这种特性并不常用。MyBatis框架支持对Executor
、ParameterHandler
、ResultSetHandler
、StatementHandler
四种组件的方法进行拦截。掌握了MyBatis插件的实现原理,然后自己实现一个分页查询插件和慢SQL统计插件,当我们需要的功能MyBatis框架无法满足时,可以考虑通过自定义插件来实现。
经典实现:PageHelper 。
MyBatis针对不同的日志框架提供对Log接口对应的实现,Log接口的实现类下图所示。从实现类可以看出,MyBatis支持7种不同的日志实现,具体如下。
下面对常用几个做一个简单说明:
MyBatis查找日志框架的顺序为
SLF4J→JCL→Log4j2→Log4j→JUL→No Logging。
如果Classpath下不存在任何日志框架,则使用NoLoggingImpl日志实现类,即不输出任何日志。
动态SQL指的是事先无法预知具体的条件,需要在运行时根据具体的情况动态地生成SQL语句。
在MyBatis中有着丰富的动态SQL标签,比如:196185dae55b7edbe154a5051db664a7
、f3bf5eff46860b27119c8dd4e92f1e57
、7c3646f85db7ce074bf2e07d38d2c6e1
、800a174bf160103126f2a6097b3e44a4
等。196185dae55b7edbe154a5051db664a7
、f3bf5eff46860b27119c8dd4e92f1e57
、7c3646f85db7ce074bf2e07d38d2c6e1
、800a174bf160103126f2a6097b3e44a4
等。
在MyBatis源码中有个SqlSource对象会作为MappedStatement对象的属性保存在MappedStatement对象中。执行Mapper时,会根据传入的参数信息调用SqlSource对象的getBoundSql()方法获取BoundSql对象,这个过程就完成了将SqlNode对象转换为SQL语句的过程。
比如:,#{}
以上是一周学完MyBatis源码,万字总结的详细内容。更多信息请关注PHP中文网其他相关文章!