本文最后更新于:May 13, 2023 pm
积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里,不积小流无以成江海。齐骥一跃,不能十步,驽马十驾,功不在舍。面对悬崖峭壁,一百年也看不出一条裂缝来,但用斧凿,能进一寸进一寸,能进一尺进一尺,不断积累,飞跃必来,突破随之。
目录
优缺点
优点
- 灵活度高。可以自定义编写SQL。
- 内置缓存机制。通过缓存机制,可以大大减少与数据库的交互次数,提高性能。
- 简化代码。与传统JDBC代码相比,只需要接口方法的声明和mapper.xml配置。
- 良好的兼容性。几乎支持市场上所有主流的数据库。
- 支持与Spring整合。
缺点
- 需要开发者具有良好的SQL功底。
- 移植性差。SQL依赖数据库,不同的数据库有不同的SQL语句。
ORM
ORM(Object Relational Mapping),即对象关系映射。是一种程序设计技术。主要思想是通过指定对象和关系型数据库之间的映射关系,可以使程序开发时可以使用面向对象思想操作关系型数据库。
MyBatis中是否必须有接口
MyBatis中并不是必须
要求有接口。MyBatis提供了三种引入的方式:
只编写mapper.xml文件、编写接口和mapper.xml文件、编写接口并使用注解。对应的在主配置文件中相应的配置为:
| <mappers> <mapper resource="com/tothefor/dao/StudentDao.xml"/> <package name="com.tothefor.dao"/> <mapper class="com.tothefor.MyStudentDao" /> </mappers>
|
只编写mapper文件
不需要写接口实现。
StudentDao.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="NoEntity">
<select id="DouQueryById" parameterType="int" resultType="com.tothefor.entity.Student"> select * from Student where id = #{id} </select>
</mapper>
|
主配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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> <properties resource="mysql.properties" /> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${mysql.driver}"/> <property name="url" value="${mysql.url}"/> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/tothefor/dao/StudentDao.xml"/> </mappers> </configuration>
|
测试
见下一个示例。但需要注意修改namespace。
编写接口和Mapper文件
接口:
| package com.tothefor.dao;
import com.tothefor.entity.Student;
public interface StudentDao { public Student queryById(String id); }
|
对应mapper文件:
| <?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.tothefor.dao.StudentDao">
<select id="DouQueryById" parameterType="int" resultType="com.tothefor.entity.Student"> select * from Student where id = #{id} </select>
</mapper>
|
主配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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> <properties resource="mysql.properties" /> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${mysql.driver}"/> <property name="url" value="${mysql.url}"/> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> </dataSource> </environment> </environments> <mappers> <package name="com.tothefor.dao"/> </mappers> </configuration>
|
测试
| @Test public void testDouQueryById() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("myBatis.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession session = build.openSession(); Student student = session.selectOne("com.tothefor.dao.StudentDao.DouQueryById", 2); System.out.println(student); session.close(); }
|
接口和注解
接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.tothefor;
import com.tothefor.entity.Student; import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface MyStudentDao { @Select("select * from Student") public List<Student> all(); }
|
主配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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> <properties resource="mysql.properties" /> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${mysql.driver}"/> <property name="url" value="${mysql.url}"/> <property name="username" value="${mysql.username}"/> <property name="password" value="${mysql.password}"/> </dataSource> </environment> </environments> <mappers> <mapper class="com.tothefor.MyStudentDao" /> </mappers> </configuration>
|
测试
| @Test public void testNo() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("myBatis.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession session = build.openSession(); MyStudentDao mapper = session.getMapper(MyStudentDao.class); List<Student> all = mapper.all(); for(Student it: all){ System.out.println(it); } session.close(); }
|
延伸
为什么在项目中需要提供一个接口?
- 主要原因就是:在其他层可以通过接口快速注入接口对象,从而方便调用方法。
MyBatis是如何实现接口和mapper文件绑定的
MyBatis是通过动态代理(JDK动态代理)来实现绑定的。当获取到接口对象的时候,会产生一个接口的动态代理对象,然后通过接口代理对象调用里面的方法时就是通过动态代理去new真实的需要使用的SQL语句。
应用步骤:
- 在主配置文件中进行指定要扫描的包。包中提供了
同名
的接口文件和mapper文件。
| <mappers> <package name="com.tothefor.dao"/> </mappers>
|
- 在mapper文件中的namespace指定接口的全限定名称。
| <mapper namespace="com.tothefor.dao.StudentDao"></mapper>
|
- 在编写的标签的id属性时,设置为接口中方法的名称。
| <select id="queryById" resultType="com.tothefor.entity.Student"> select * from Student where id = #{id} </select>
|
MyBatis接口中是否支持方法的重载
在Java类中是支持方法的重载,但是在MyBatis中是不允许
方法重载的。
原因
- 在上一个问题中我们也知道了mapper文件中的namespace是接口的全限定名称,并且标签的id属性的值是方法的名称。而且在mapper文件中的每个标签(select、update、delete、insert)最终存储时都是以namespace+id作为key进行存储的。
- 所以,如果接口中存在两个同名方法(重载),那么在同一个mapper文件下就会提供两个同名的id。
namespace
- namespace属性必须存在,不能为空。如果没有配置namespace或取值为空时会抛出BuilderException。
- 多个mapper文件中允许出现同名的namespace,但是一般都是用接口的全限定名称。且一个接口对应一个mapper文件,所以一般是不会出现重名的。
MyBatis的mapper文件中支持哪些标签
共24个标签。
最基本的:select、update、delete、insert
- 子标签:if、choose、trim、where、set、foreach、bind(动态SQL的7个标签)、selectKey(新增时回填主键)、include(引用SQL)
结果集映射:resultMap
- 子标签:id、result、association、collection、constructor、discriminator。
参数映射:parameterMap
二级缓存配置:cache
二级缓存引用:cache-ref
SQL片段:sql
mapper文件获取方法参数
详见《MyBatis学习-(三)参数 》
结果映射的方式
分为三种方式:自动映射、别名映射、手动映射。
自动映射
这种要求实体类属性和数据库字段名称相同。
| <select id="selectAll" resultType="Stu"> select id,name,age,phone from stu; </select>
|
id,name,age,phone为实体类Stu的属性名称。
别名映射
通过设置别名,本质和自动映射类似。
| <select id="selectAll" resultType="Stu"> select t_id id,t_name name from stu; </select>
|
其中,t_id、t_name为数据库字段名称;id、name为实体类属性名称。
手动映射
通过resultMap实现映射。
| <resultMap id="stuMap" type="Stu"> <id column="t_id" property="id" /> <result column="t_name" property="name" /> </resultMap> <select id="selectAll" resultType="stuMap"> select t_id,t_name from stu; </select>
|
MyBatis实现关联对象查询
MyBatis支持两种方式实现关联对象查询:N+1查询、多表联合查询。
- N+1查询支持延迟加载,多表查询不支持。
- 如果关联的是一个对象,使用resultMap中的association进行查询。
- 如果关联的是一个集合,使用resultMap中的Collection进行查询。
MyBatis实现延迟加载
MyBatis中的延迟加载只能出现在N+1查询方式,默认是没有开启延迟加载的。有两种配置方式:在主配置文件中配置两个属性,从而开启延迟加载;在association或者Collection中的fetchType控制,lazy表示延迟加载,eager表示立即加载。当fetchType和主配置文件同时配置时,fetchType生效。
主配置文件:
| <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings>
|
MyBatis的缓存机制
MyBatis提供了两种缓存,一级缓存和二级缓存。通过缓存可以减少对数据库的访问,提高程序执行性能。
- 一级缓存又称为SqlSession缓存,默认开启。有效范围是同一个SqlSession对象,每次缓存同一个方法中相同的SQL语句。在缓存时把SQL当成Key,结果当成Value进行缓存。关闭后释放缓存中的内容。
- 二级缓存又称为SqlSessionFactory缓存,有效范围同一个SqlSessionFactory对象,默认关闭。可以在对应映射文件中添加
cache
标签启用二级缓存。
- 当SqlSession提交或关闭时,才把一级缓存的内容放到二级缓存中。因为项目中SqlSessionFactory是不关闭的,所以二级缓存内容默认一直存在。二级缓存中放经常被查询、但很少被修改的内容。还需要注意的是,对象需要序列化(实现Serializable接口)。
- 每次查询时先判断二级缓存,如果没有,再判断一级缓存,如果也没有,则访问数据库。
MyBatis中执行器的类型
MyBatis的执行器都实现了Executor接口,作用是SQL执行的流程。共分为三个类型:SimpleExecutor
、ReuseExecutor
、BatchExecutor
。
SimpleExecutor
是默认的执行器类型,每次执行SQL都需要预编译、设置参数、执行。
ReuseExecutor
只预编译一次,复用Statement对象,然后设置参数,并执行。
BatchExecutor
先预编译,批量执行时设置参数,最后统一提交给数据库执行。
- 可以通过
factory.openSession()
方法参数设置执行器类型。通过枚举类型ExecutorType
进行设置。
MyBatis的四大核心接口
四大核心接口:Executor(执行器)、ParameterHandler(参数处理器)、StatementHandler、ResultSetHandler(结果集处理器)。
- Executor:负责SQL执行过程的总体控制。
- ParameterHandler:负责SQL语句的参数设置。
- StatementHandler:负责与JDBC代码的交互,包含prepare预处理、query查询、update增删改操作。
- ResultSetHandler:负责把查询结果映射为Java对象。
MyBatis的执行流程
MyBatis使用的设计模式
代理模式(Mapper)、适配器模式(log)、工厂模式(SqlSession)、装饰器模式(CachingExecutor)、建造者模式(SqlSessionFactoryBuilder)、策略模式(openSession可以控制ExecutorType)、模板模式(BaseExecutor)、责任链模式(Interceptor)。