一、MyBatis介绍
1.1 什么是框架
框架即一个半成品软件。开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。
如果将开发完成的软件比作是一套已经装修完毕的新房,框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,保证建筑质量和户型合理的同时可以进行风格的自由装修。
使用框架开发的好处:
- 省去大量的代码编写、减少开发时间、降低开发难度。
- 限制程序员必须使用框架规范开发,增强代码的规范性,降低程序员之间沟通及日后维护的成本。
- 将程序员的注意力从技术中抽离出来,更集中在业务层面。
使用框架就好比和世界上最优秀的软件工程师共同完成一个项目,并且他们完成的还是基础、全局的工作。
1.2什么是ORM框架
ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。
1.3什么是MyBatis
MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。
补充:
Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。
MyBatis与Hibernate的比较:
- MyBatis是一个半自动的ORM框架,需要手写SQL语句。
- Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
- 使用MyBatis的开发量要大于Hibernate。
为什么Hibernate市场占有率越来越低:
- 对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
- Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生成的SQL语句,这就导致SQL调优很难进行。
- 之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
- 在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。
二、入门案例
2.1 环境搭建
1、创建maven工程,引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zj</groupId> <artifactId>learnMyBatis</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--MyBatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!--MySQL驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <!--单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!--log4j日志--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> </dependencies> </project>
2、创建mybatis核心配置文件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="mysql"> <environment id="mysql"> <!--事务类型--> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:///mybatis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration>
3、将log4j.properties文件放入resources中,让控制台打印SQL语句。
# Set root category priority to INFO and its only appender to CONSOLE. #log4j.rootCategory=INFO, CONSOLE debug info warn error fatal log4j.rootCategory=debug, CONSOLE # Set the enterprise logger category to FATAL and its only appender to CONSOLE. #log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE # CONSOLE is set to be a ConsoleAppender using a PatternLayout. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=[%d{MM/dd HH:mm:ss}] %-6r [%15.15t] %-5p %30.30c %x - %m\n
4、创建实体类
package com.zj; public class User { private int id; private String username; private String sex; private String address; //构造、get\set略 }
2.2 创建持久层接口和映射文件
1、在java目录创建持久层接口
package com.zj.mapper; import com.zj.pojo.User; import java.util.List; /*主要操作User类和数据库之间的交互*/ public interface UserMapper { List<User> selectAllUser(); }
2、在resource目录创建映射文件
<?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"> <!--namespace;指定当前的映射文件对应哪个接口--> <mapper namespace="com.zj.mapper.UserMapper"> <!--id="selectAllUser";id属性值为接口的方法名称 resultType="com.zj.pojo.User";定义返回值类型 --> <select id="selectAllUser" resultType="com.zj.pojo.User"> SELECT * FROM user; </select> </mapper>
3、将映射文件配置到mybatis核心配置文件中
<!--注册映射文件(项目加载的时候先加载的是核心配置文件)--> <mappers> <mapper resource="com.zj.mapper.UserMapper.xml"/> </mappers>
映射文件注意事项:
- 映射文件要和接口名称相同。
- 映射文件要和接口的目录结构相同,而且映射文件所在的文件结构要一层层的建。
- 映射文件中namespace属性要写接口的全名。
- 映射文件中标签的id属性是接口方法的方法名。
- 映射文件中标签的resultType属性是接口方法的返回值类型。
- 映射文件中标签的parameterType属性是接口方法的参数类型。
- 映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。
2.3 测试持久层接口方法
import com.zj.mapper.UserMapper; import com.zj.pojo.User; 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 org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class TestUserMapper { @Test public void TestSelectAllUser() throws IOException { /*1、读取核心配置文件*/ InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); /*2、SqlSessionFactoryBuilder对象获取SqlSessionFactory对象(建造者用映射文件材料建设了工厂)*/ SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream); /*3、SqlSessionFactory对象获取SqlSession(工厂建造了了SqlSession)*/ SqlSession sqlSession = sessionFactory.openSession(); /*4、SqlSession 对象获取接口的代理对象*/ UserMapper mapper = sqlSession.getMapper(UserMapper.class); /*5、代理对象执行方法*/ List<User> users = mapper.selectAllUser(); for (User user : users) { System.out.println(user); } /*6、释放资源*/ sqlSession.close(); resourceAsStream.close(); } }
2.4 MyBatis核心对象及工作流程
MyBatis核心对象
- SqlSessionFactoryBuilder
SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。 - SqlSessionFactory
SqlSession工厂,使用工厂模式创建SqlSession对象。 - SqlSession
该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。 - Mapper
持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库。
MyBatis工作流程
- 创建SqlSessionFactoryBuilder对象
- SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式
- SqlSessionFactory对象生产了SqlSession对象:工厂模式
- SqlSession对象创建了持久层接口的代理对象:动态代理模式
- 代理对象操作数据库
2.5 使用SqlSession操作数据库(了解)
除了代理对象能够操作数据库,SqlSession也能操作数据库。只是这种方式在开发中使用的较少,接下来我们使用SqlSession操作数据库:
@Test public void testFindAll2() throws Exception { // (1)读取核心配置文件 InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); // (2)创建SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); // (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象 SqlSessionFactory factory = builder.build(is); // (4)SqlSessionFactory对象获取SqlSession对象 SqlSession session = factory.openSession(); // (5)SqlSession直接操作数据库 List<User> users = session.selectList("com.zj.mapper.UserMapper.selectAllUser"); users.forEach(System.out::println); // (6)关闭资源 session.close(); is.close(); }
2.6Mapper动态代理原理
通过源码了解MyBatis动态代理的实现。
点开测试类的getMapper
方法,发现SqlSession实际上是抽象类,找到该抽象类的默认的实现类DefaultSqlSession
查看代理方式
点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。
结论:
- SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类型就是传入的接口类型。
- MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。