目录
4.2.2.1 properties、environment
4.2.2.3.2 Insert, Update, Delete标签
4.2.1 MyBatis概述
4.2.1.1 什么是MyBati
ORM(Object-Relationship-Mapping):是对象关系映射的意思,它是一种思想,是指将数据库中的每一行数据用对象的形式表现出来。
简单来说就是,student表中列有id(类型int),name(类型String),age(类型int)。ORM思想就是创建student的javabean类,student类属性有int id;String name;int age;
JPA(Java-Persistence-API):是Java持久化接口的意思,它是JavaEE关于 ORM 思想的一套标准接口,仅仅是一套接口,不是具体的实现。 早期有 2 个著名的 ORM 映射框架,它们就是 Hibernate 和 Ibatis
- MyBatis的前身就是iBatis,轻量级持久成框架
- Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
- MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- 通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返 回 result 的过程)。
4.2.1.2 MyBatis的优缺点
4.2.1.2.1 优点
- 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用。
- 与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
- 很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持)。
- 能够与 Spring 很好的集成;
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射 标签,支持对象关系组件维护。
4.2.1.2.2 缺点
- SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有一定要求。
- SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
4.2.1.3 MyBatis与IBatis区别
MyBatis 增加了接口绑定
MyBatis 动态sql由原来的节点配置变成了OGNL表达式
MyBatis 一对一引入了association,一对多引入collection
IBatis的核心处理类是SqlMapClient,MyBatis核心处理类是SqlSession。
4.2.1.4 MyBatis与Hibernate区别
Hibernate:
- JPA
- 完全ORM,sql语句不需要自己书写
- 使用 Hibernate ,可以通过一套程序搞定所有关系型数据库,可以用最小的代价完成关系型数据库的替换。但是完全的封装造成的就是效率不高,调优困难。
- 因为封装的问题,在数据库操作方面,给出了5套解决方案,操作上变得繁琐。
- 有公司会使用的 Spring-Data-JPA 技术便是基于Hibernate的实现
- Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用 hibernate 开发可以节省很多代码,提高效率
使用Hibernate可以对对象与对象的关系映射设计的好
MyBatis:
- 没有实现JPA
- 半个 ORM
- 编写原生的SQL,效率高
- 甚至可以不用管对象与对象的关系映射设计
- Mybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高,非常 适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需 求变化要求迅速输出成果。但是灵活的前提是 mybatis 无法做到数据库无关性, 如果需要实现支持多种数据库的软件,则需要自定义多套 sql 映射文件,工作量大
总的来说:
Hibernate是全ORM框架,mybatis是半ORM框架,
Hibernate实现了JPA接口,操作数据库无需书写sql语句
Mybatis没有实现JPA接口,需要书写原生的sql语句进行数据库操作
Hibernate在sql优化方面以及书写效率存在问题(相比较Mybatis)
Hibernate内部封装完善,学习成本高
4.2.2 全局配置文件
mybatis-config.xml是系统的核心配置文档(全局配置文档),文档格式如下
由于约束的存在,全局配置的顺序必须要以上要求按顺序书写。
其他的配置参数涉及到mybatis底层配置,不用配置,默认即可。
重点是配置properties,environment和mapper。
4.2.2.1 properties、environment
如果单独定义jdbc.properties,则按如下格式搭配
environment的default可以任意取,但是id要与default一致。
4.2.2.2 Mapper映射方式
4.2.2.2.1 什么是Mapper
Mapper:映射器:
- 用来在mybatis初始化的时候,告诉mybatis需要引入哪些Mapper映射文件
- mapper逐个注册SQL映射文件
通常命名格式为:类名Mapper.xml或类名Dao.xml
Mapper有三种映射方式
4.2.2.2.2 映射方式一:resource属性
resource属性:单独引入相应xml配置(可以通过mapper配置中的namespace与id动态执行)
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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:///mydb?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 加载sql的映射文件-->
<mappers>
<mapper resource="mapper/Usermapper1.xml"/>
</mappers>
</
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--名称空间 namespace 可以随便起
namespace唯一表示此名字下的crud语句
-->
<mapper namespace="a">
<!--
id:在此命名空间下唯一标识
resultType:查询结果的返回类型或者集合的泛型
-->
<select id="selectAll" resultType="com.javaBean.User">
select * from t_user
</select>
</mapper>
//自己定义了一个连接数据库的工具类,主要是方便
class MyBatisUtil {
private MyBatisUtil(){}
private static SqlSessionFactory sqlSessionFactory;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (Exception e) {
}
}
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}
//通过单元测试来运行 @Test
public class MyBatisDemo {
@Test
public void fi(){
SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession(true);
List<User> users=sqlSession.selectList("a.selectAll");
for (User user : users) {
System.out.println(user);
}
//5、关闭资源
sqlSession.close();
}
}
Resources.getResourceAsStream("mybatis-config.xml")读取到全局配置文件信息,然后调用自带的selectList("a.selectAll")方法
4.2.2.2.3 映射方式二:class属性
class属性:创建对应mapper接口对象(可以自动扫描与mapper接口名字相同的xml)
会自动识别指定接口对应包下的接口相关的xml配置
注意
1、对应接口的路径要与对应xml路径相同,如由于UserMapper.xml在resources的mapper包下,所以UserMapper接口也需要在Java的mapper包下。
在mapper下创建Usermapper接口,
注意,接口名必须和resources/mapper下对应的xml名字一致,否则系统运行报错。
2、引入一个重要术语—接口绑定:在MyBatis中任意定义接口,并把定义的接口和xml里的sql语句绑定。这样我们就可以直接调用接口方法,相比于sqlsession提供的方法,前者可以更加灵活选择和设置。
Usermapper 接口
public interface Usermapper {
public List<User> selectAll();
}
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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:///mydb?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 加载sql的映射文件-->
<mappers>
<!-- 引入接口-->
<mapper class="mapper.Usermapper"/>
</mappers>
</configuration>
Usermapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--名称空间-->
<mapper namespace="mapper.Usermapper">
<select id="selectAll" resultType="com.lb.javaBean.User">
select * from t_user
</select>
</mapper>
测试
public class MyBatisDemo {
@Test
public void fi(){
SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession(true);
//调用session的方法获取对应的mapper
//会自动根据xml生成对应接口的实现类
Usermapper usermapper=sqlSession.getMapper(Usermapper.class);
//直接调用对应的方法操作数据库即可
List<User> users=usermapper.selectAll();
for (User user : users) {
System.out.println(user);
}
//5、关闭资源
sqlSession.close();
}
}
MyBatis调用Mapper接口要注意:
1、mapper接口方法名和mapper.xml中定义的sql的id相同
2、mapper接口方法的输入参数的类型和mapper.xml中定义的sql的parameterType类型相同
3、mapper接口方法的输出参数的类型和mapper.xml中定义的sql的resultType类型相同
4、mapper.xml文件中的namespace就是mapper接口的类路径
4.2.2.2.4 映射方式三:package属性
package属性:引入指定包下接口对象(可以自动扫描与mapper接口名字相同的xml,class与xml同一目录)
方式三基于方式二,只是在 mybatis-config.xml文件中吧mappers设置为
<mappers>
<!-- 引入方式3:基于方式二的形式将书写接口的包直接引入扫描
自动匹配所有的包与对应的xml
-->
<package name="mapper"/>
</mappers>
其他的配置与方式二一样。
4.2.2.3 Mapper
MyBatis 的真正强大在于它的映射语句 ,与JDBC相比节省了大量的代码。
mapper中的标签(注意顺序)
常用操作为增删改查,其他按默认配置即可。
4.2.2.3.1 Select标签
Select标签的属性
属性 | 描述 |
---|---|
id | 名称空间唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
resultType | 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。 |
resultMap | 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。 |
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。 |
useCache | 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 |
fetchSize | 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。 |
*select标签重点学习传递参数
传递单个参数 | #{参数 } | 参数名随便起, 一般按照如下格式 sex=#{sex} |
传递多个参数 | #{arg0}、#{arg1}...#{arg n-1} 或者 #{param1}、#{param2}...#{paramn} | 使用arg0 arg1 或param1 param2 代表第一个参数与第二个参数 |
自定义参数类型 | #{} | #{}中通过属性名的形式传入对应的值 mybatis会自动调用对应的getter方法获取值;可防止sql注入 ${}中书写的内容会自动当成传递引用类型数据的属性 调用对应的getter方法获取 |
List集合 | #{list[索引]} 或者 #{collection[索引]} | 使用Collection 或list 代表传递的集合数据 通过#{list[索引]}或者#{collection[索引]}的形式 获取对应的数据 |
Map集合 | #{key} | 通过指定的key获取value |
需要注意一些细节:
如果你只接传多个参数:X.select(1,"张三"); 传入id和name
那么mapper.xml中应这样写:
-------------------------------------------------------------------------------------------------------------------------
<select id="select">
select * from student where id=#{arg0} and name=#{arg1}
</select>
-------------------------------------------------------------------------------------------------------------------------
如果这样写
-------------------------------------------------------------------------------------------------------------------------
<select id="select">
select * from student where id=#{id} and name=#{name}
</select>
-------------------------------------------------------------------------------------------------------------------------
会报错,原因是id和name没有get方法
但是如果先new 一个对象,如student对象
Student student = new Student(id:1,name:"张三");
X.select(student);
-------------------------------------------------------------------------------------------------------------------------
<select id="select">
select * from student where id=#{id} and name=#{name}
</select>
-------------------------------------------------------------------------------------------------------------------------
就不会报错,因为在student中有相应属性的get方法
后面的学习中很多sql语句都需要注意此细节
StudentMapper.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">
<!--
接口绑定:
namespace唯一表示此名字下的crud(增删改查)语句
注意命名格式 java/mapper/StudentMapper 名字就为mapper.StudentMapper
-->
<mapper namespace="mapper.StudentMapper">
<!--
id:在此命名空间下唯一标识 与接口中方法名相同
parameterType:参数类型 一般不书写 mybatis会自动推断
resultType:查询结果的返回类型或者集合的泛型
-->
<!-- resultType:使用别名代替返回的类型
不建议使用别名 因为使用完全限定名在修改时比较方便 -->
<select id="selectAllStudent" resultType="student">
select * from student
</select>
<!-- 传递单个参数 使用#{}代替占位符?
由于传递的是一个参数 所以可以自动识别#{}中书写任意内容都可以获取
一般书写传递参数的形参名
-->
<select id="selectStudentBySex" resultType="student">
select * from student where sex=#{sex}
</select>
<!-- 传递多个基本类型参数
由于多个参数类型可能不同 所以不配置parameterType 交由mybatis自动识别即可
注意:由于传递多个参数 所以使用#{}的时候内容需要进行限制
使用arg0 arg1 或param1 param2 代表第一个参数与第二个参数
-->
<select id="selectStudentByNameAndAge" resultType="student">
select * from student where name=#{arg0} and age=#{arg1}
</select>
<!-- 当参数类型为自定义类型时
#{}中通过属性名的形式传入对应的值 mybatis会自动调用对应的getter方法获取值
${}中书写的内容会自动当成传递引用类型数据的属性 调用对应的getter方法获取
-->
<select id="selectStudentByName" resultType="student">
select * from student where name='${name}'
</select>
<!-- #{}与${}的区别 -->
<!-- 都是对指定的参数进行获取填入,#{}使用的是占位符的形式进行填值 可以有效解决sql注入
${} 使用的是字符串拼接形式 需要注意sql语句的书写规则 不能防止sql注入
#{}在执行中日志可以看见传递的参数信息 ${}直接将数据拼接在sql语句中执行
-->
<!-- 传递list集合数据时 使用Collection 或list 代表传递的集合数据
通过#{list[索引]}的形式 获取对应的数据
-->
<select id="selectStudentByList" resultType="student">
select * from student where name in (#{list[0]},#{list[1]})
</select>
<!-- map集合数据其实跟自定义类型很像
获取map集合参数时 通过#{key}的形式获取对应的value
-->
<select id="selectStudentByMap" resultType="student">
select * from student where name=#{name}
</select>
<select id="selectStudentByArray" resultType="student">
select * from student where name in (#{array[0]},#{array[1]})
</select>
</mapper>
StudentMapper接口
public interface StudentMapper {
//查询所有数据
ArrayList<Student> selectAllStudent();
//根据单个基本类型参数查询数据
//根据姓名查询数据
ArrayList<Student> selectStudentBySex(String sex);
//根据多个不同基本类型参数查询数据
//根据姓名与年龄查询数据
ArrayList<Student> selectStudentByNameAndAge(String name,int age);
//不能防止sql注入的查询
//自定义引用类型传递
ArrayList<Student> selectStudentByName(Student s);
//集合类型数据传递
//根据名字集合查询指定数据
ArrayList<Student> selectStudentByList(ArrayList<String> list);
//根据map集合查询指定key对应的数据
ArrayList<Student> selectStudentByMap(HashMap<String,String> map);
ArrayList<Student> selectStudentByArray(String [] arr);
}
4.2.2.3.2 Insert, Update, Delete标签
insert、update、delete标签的属性一致。
属性 | 描述 |
---|---|
id | 命名空间中的唯一标识符,可被用来代表这条语句。 |
parameterType | 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
keyColumn | (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
<?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唯一表示此名字下的crud语句
-->
<mapper namespace="mapper.UserMapper">
<!--
id:在此命名空间下唯一标识
resultType:查询结果的返回类型或者集合的泛型
-->
<!-- resultType:返回值类型 可用别名或类全限定名 -->
<select id="selectAllUser" resultType="com.sq.javabean.User">
select * from t_user
</select>
//注意,Dao层的insertUser传入的不是变量,而是一个User类,所以这里面才能写类的属性名而非arg0或param1之类的
<insert id="insertUser">
insert into t_user (name,age,addr)values(#{name},#{age},#{addr})
</insert>
<update id="updateSalById">
update t_user set sal=#{arg1} where id=#{arg0}
</update>
<delete id="deleteById">
delete from t_user where id=#{id}
</delete>
</mapper>
4.2.2.3.3 sql、include 标签
sql标签 | 可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中 |
include标签 | 可以调用定义的sql标签,可以设置property标签 设置name属性与value属性为sql标签中设置的参数赋值 |
refid 属性 | 引入sql标签的id |
<sql id="sel">
select * from t_user
</sql>
<!-- 可以通过include标签 设置指定sql标签的id进行引用 -->
<select id="selectAll" resultType="user">
<include refid="sel"/>
</select>
实际开发过程中经常只将查询的列放入sql标签
<sql id="selCol">id,name,age</sql>
<select id="selectById" resultType="user">
select
<include refid="selCol"></include>
from t_user where id=#{id}
</select>
<select id="selectByList" resultType="user">
select
<include refid="selCol"></include>
from t_user where id in(#{list[0]},#{list[1]},#{list[2]})
</select>
如果要对表起别名 那么列名书写时 就必须使用别名.列名的形式。需要额外定义property标签 name:与sql中${X},中的X一致
value:是对表起的别名
需要注意:定义时使用${}进行sql的拼接 (如果#{}会变成?占位符)
<sql id="sel">
${alias}.sid,${alias}.sname,${alias}.age,${alias}.phone
</sql>
<select id="select" resultType="com.lb.javabean.Student">
select
<include refid="sel">
<property name="alias" value="s"/>
</include>
from student s
</select>
这里有道面试题:模糊查询
LIKE '%${ XXX}%' | 可能引起sql注入,不推荐 |
LIKE "%"#{XXX}"%" | #{}解析sql语句时,会在变量外侧自动添加单引号',所以这里%需要用双引号包裹 |
LIKE CONCAT('%',#{XXX}),'%' | 使用CONCAT函数,推荐 |
使用bind标签 | 不推荐 |
主键自增
1.通过属性进行获取
属性 | 描述 |
---|---|
useGeneratedKeys | 是否使用自动生成主键,默认为false |
keyProperty | 实体类中属性名 |
keyColumn | 数据库主键字段名(如果id是第一列可以省略不写) |
User类
public class User{
private String uid;
----省略----
}
mysql表格格式
--------------
uid| 省略----
---------------
keyProperty:类中属性 uid
keyColumn:表中的列 uid
在执行添加后会自动将新增数据的主键赋值给对对应参数的对应属性值
<insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="uid" keyColumn="uid">
insert into user (uusername,upassword) values (#{uusername},#{upassword})
</insert>
2.通过selectKey标签获取
该标签可以在原有sql执行的前后进行二次执行
原理:在添加数据后立刻获取最后添加数据的主键 并赋值给对应的实体类
属性
resultType | 查询主键结果类型,可以不写会自动识别 |
order=“BEFORE 或 AFTER” | 指定sql执行的顺序 |
keyProperty | 实体类中属性名 |
keyColumn | 数据库主键字段名(如果id是第一列可以省略不写) |
<insert id="insert" >
<selectKey resultType="int" keyProperty="sid" keyColumn="sid" order="AFTER">
select LAST_INSERT_ID() as id
</selectKey>
insert into student (sname,age,phone) values (#{sname},#{age},#{phone})
</insert>
UUID实现主键自增
先来看两条语句,uuid是个函数,用来获取uuid,dual是个虚拟表,用来构成select语法规则。
replace替换函数,把uuid里的“-” 替换为“”
方式1:使用selectKey标签 在sql语句执行前 通过sql获取uuid并存入实体类对象中
StudentMapper.xml
<mapper namespace="mapper.StudentMapper">
<insert id="insert" parameterType="com.lb.javabean.Student">
<selectKey keyProperty="id" order="BEFORE" keyColumn="id" resultType="java.lang.String">
select replace(UUID(),"-","") id from dual
</selectKey>
insert into student (id,name,age)values(#{id},#{name},#{age})
</insert>
</mapper>
测试
public void test3() throws Exception {
SqlSessionFactory sqlSessionFactory;
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(true);
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student s= new Student("","张三",18);
int insert = mapper.insert(s);
sqlSession.close();
}
方式2:在java代码中生成uuid封装为实体类直接存入数据库
StudentMapper.xml
<mapper namespace="mapper.StudentMapper">
<insert id="insert" parameterType="com.lb.javabean.Student">
insert into student (id,name,age)values(#{id},#{name},#{age})
</insert>
测试
@Test
public void test3() throws Exception {
SqlSessionFactory sqlSessionFactory;
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(true);
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
String id=UUID.randomUUID().toString().replace("-","");
Student s= new Student(id,"dbz",18);
int insert = mapper.insert(s);
sqlSession.close();
}
*4.2.2.3.4 resultMap结果映射
resultMap :用于处理复杂的返回结果集
1.为查询结果列与对象不同进行映射
Student类
public class Student{
private String id;
---省略----
}
mysql中student表
select * from student
根据ORM思想创建的相应student类中属性为id,而数据库中表列为sid,试问,是否能正常查询出数据?
结果如下所示
为什么会这样?
原因就在于,数据库字段与对象属性名不一致。
通常情况下,数据库字段与对象的属性名要一致,但是,如果不用一致就需要进行不同的映射,这时候就需要使用resultMap。
resultMap结果映射
标签
标签 | 说明 |
resultMap | 处理复杂的返回结果集 (数据库字段与属性名不相同时,就需要映射) |
id | 标识主键列 |
result | 标识非主键列 |
colleaction | 标识集合属性 |
association | 标识类属性 |
属性
属性 | 说明 |
property | 定义java中id的属性名 |
column | 定义数据结果中对应的列名 |
property | 实体类字段(属性) |
ofType | 集合存储数据类型属性 |
select | 懒加载执行时调用获取数据的方法(也可以调用mapper中的方法,通过namespace.id使用) |
fetchType | fetchType="lazy":懒加载,默认false |
javatype、 jdbctype 、typehandler | 自动识别不需要定义 |
<mapper namespace="mapper.StudentMapper">
<resultMap id="student" type="com.lb.javabean.Student">
<id column="sid" property="id"></id>
<result property="username" column="username"/>
<result property="age" column="age"/>
</resultMap>
<select id="selectAll" resultMap="student" >
select * from student
</select>
对比普通的select查询语句
<!-- <select id="selectAll" resultType="com.lb.javabean.Student">-->
<!-- select * from student-->
<!-- </select>-->
</mapper>
2.一对一查询
先来思考一个问题:什么是一对一查询?
比如,创建一张学生表和学院表,一个学生只归属于一个学院,那么学生表和学院表之间的关系就是一种一对一的关系。
模板
方法一: <resultMap id=" 表示主键" type="返回类的全限定名"> 设置主键列 <id column="" property=""></id> 设置其它的列 property:类属性的名字 column:表中列的名字 <result column="" property=""></result> 一对一映射属性也是当前类的属性,需要额外使用association标签配置 property:类属性的名字 javaType 一对一映射必须书写该属性指定类属性的类型(自定义类型无法自动识别) <association property="属性(但是个映射类)的名字" javaType="映射的类的全限定名"> <id column="" property=""></id> <result column="" property=""></result> </association> </resultMap> 方法二: 通过设置autoMapping 来自动映射 默认false <resultMap id="" type="" autoMapping="true"> <association property="" javaType="" autoMapping="true"></association> </resultMap>
Student类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int sid;
private String sname;
private String age;
private String phone;
private Department department; //这里就是一对一的体现,一个student(学生)只能属于一个department(学院)
}
Department类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {
private int depid;
private String depname;
private String principal;
}
UserMapper.xml
法一:
<mapper namespace="mapper.StudentMapper">
<resultMap id="sid" type="com.lb.javabean.Student">
<!-- 属性映射正常书写 无论列名与属性名是否匹配都需要书写 -->
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<result column="age" property="age"/>
<result column="phone" property="phone"/>
到Role属性,由于Role是User对应的Role类,所以通过association来映射
<association property="department" javaType="com.lb.javabean.Department">
<id column="depid" property="depid"/>
<result column="depname" property="depname"/>
<result column="principal" property="principal"/>
</association>
</resultMap>
<select id="select" resultMap="sid">
select s.*,d.* from student s,department d,student_dept sd where s.sid=sd.sid and d.depid=sd.depid
</select>
</mapper>
法二:
<resultMap id="sid2" type="com.lb.javabean.Student" autoMapping="true">
<association property="department" javaType="com.lb.javabean.Department" autoMapping="true"></association>
</resultMap>
<select id="select" resultMap="sid2"> //或修改为resultMap="sid2" 使用方法二
select语句
</select>
查询结果
3.一对多查询
对比了一对一,一对多的关系,就好比,学生表和课程表,一个学生对应很多课程,这就是一对多关系。
student类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student{
private int sid;
private String sname;
private ArrayList<Object> objList;
}
Object类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Object{
private int oid;
private String oname;
}
StudentMapper.xml
<mapper namespace="mapper.StudentMapper">
<resultMap id="so" type="com.lb.javabean.Student" >
<id column="sid" property="sid"></id>
<result column="sname" property="sname"></result>
<!-- 集合映射 会将重复数据存入指定的集合
property 集合属性的名称
ofType 集合存储数据的泛型类型
内部定义与resultMap映射相同
-->
<collection property="objList" ofType="com.lb.javabean.Object">
<id column="oid" property="oid"></id>
<result column="oname" property="oname"></result>
</collection>
</resultMap>
<select id="selectAll" resultMap="so">
select 语句
</select>
</mapper>
注:
和association一样 collection也可设置自动映射
<collection property=" " ofType=" " autoMapping="true">
</collection>
里面不需要再手动写,由mybatis自动映射
<association property=" " javaType=" " autoMapping="true">
</association>
注意:一对多如果开启自动映射 可能导致每条数据单独映射 不会存入集合为一个对象赋值
4.懒加载/延迟加载
什么是懒加载:只显示你当前需要的数据,其他数据等到需要时再动态加载。
场景:比如张三的空间有1000张照片,手机屏幕只能同时展现3张照片,如果把这1000张照片都查询好后展示出现非常浪费内存,并且张三只会看几张照片。懒加载或者延迟加载就是只先查询出三张照片,张三如果继续往后浏览就再查询出三张照片展示。
Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载
在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。
基本原理
使用CGLIB(一种动态代理机制,在《4.3JavaEE-Spring》会讲到)创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,查询B,再调用a.getB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。
4.2.3 动态SQL标签
MyBatis动态sql可以让我们在xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。
执行原理是,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql。
4.2.3.1 if 标签
当uid为0,利用username来查询,当username为null,就全部查询
<select id="select" resultType="com.lb.javabean.User">
select * from user where 1=1
<if test="arg0!=0">
and uid=#{arg0}
</if>
<if test="arg1!=null">
and username=#{arg1}
</if>
</select>
测试
public void test4() throws Exception {
SqlSessionFactory sqlSessionFactory;
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
ArrayList<User> users = mapper.select(1,null);
System.out.println(users);
sqlSession.close();
4.2.3.2 where标签
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。(可以用于代替where 1=1)
<select id="selectByUidAndUserName" resultType="user">
select * from user
<where>
<if test="arg0!=0">
and uid=#{arg0}
</if>
<if test="arg1!=null and arg1!=''">
and username like '%' #{arg1} '%' //注意:like模糊查询 前后有空格
</if>
</where>
</select>
测试
ArrayList<User> users = mapper.select(0,"zhang");
4.2.3.3 trim标签
trim 元素来 自定义where 功能
属性
prefix | 添加到sql语句前缀的关键字 |
prefixOverrides | 忽略sql语句的前缀 |
suffix | 添加到sql语句后缀的关键字 |
suffixOverrides | 忽略sql语句后缀 |
<select id="select" resultType="com.lb.javabean.User">
select * from user
<trim prefix="where" prefixOverrides="Q" >
<if test="arg0!=0">
Q uid=#{arg0}
</if>
<if test="arg1!=null">
Q username=#{arg1}
</if>
</trim>
</select>
4.2.3.4 choose、when标签
choose和when搭配可以实现if-else的效果。
<select id="select" resultType="com.lb.javabean.User">
select * from user where
<choose>
<when test="uid!=0">
uid =#{uid}
</when>
<when test="username!=null and username!=''">
username = #{username}
</when>
<when test="password!=null and password!=''">
password = #{password}
</when>
<otherwise>1=1</otherwise>
</choose>
</select>
4.2.3.5 set标签
如下更新语句,问:能否实现单单修改username,而不修改password。
<update id="update" parameterType="user">
update set username=#{username},password=#{password}
where uid=#{uid}
</update>
显然是不能的,即使password什么也不填,也会将password修改为null。
而set标签可以
<update id="update" parameterType="user">
update user
<set>
<if test="username!=null">
username=#{username} ,
</if>
<if test="password!=null">
password=#{password} ,
</if>
<if test="phone!=null">
phone=#{phone} ,
</if>
</set>
where uid=#{uid}
</update>
只更新uid=1 的user的name为lisi,其余不变
mapper.update(new User(1, "lisi", null, null));
4.2.3.6 foreach标签
4.2.3.6.1 属性
item | 遍历取出的数据 |
index | 遍历数据的索引 |
colleaction | 遍历的数据集合 集合:list 数组:array |
open | 开始拼接时添加的字符串 |
separator | 分隔符 |
close | 结束后最后添加的字符串 |
<select id="select" resultType="user">
select * from user where uid in
<foreach collection="array" item="value" open="(" close=")" separator=",">
#{value}
</foreach>
</select>
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int [] arr={7,4,6,2};
ArrayList<User> users = mapper.select(arr);
4.2.3.6.2 批量操作
<!-- 批量删除 -->
<delete id="deleteByArray">
delete from user where uid in
<foreach collection="array" item="value" open="(" close=")" separator=",">
#{value}
</foreach>
</delete>
<!-- 批量添加 -->
<insert id="insertUserList">
insert into user (username,password,phone) values
<foreach collection="list" item="value" separator=",">
(#{value.username},#{value.password})
</foreach>
</insert>
4.2.4 注解开发
使用注解的方式,Dao层接口里书写简单的sql语句,代替mapper.xml文件。
但是注意,复杂的sql语句还是要在mapper.xml文件中书写,注解不适合复杂的sql语句。
4.2.4.1 @Select()
@Select("select * from user")
ArrayList select();
4.2.4.2 @param()
int selectByNameAndJob(@Param("name")String name,@Param("age")int age);
4.2.4.3 @Insert()
传入的参数是对象,所以可以用类的属性名(因为属性有get方法)
@Insert("insert into student(sname,scoll) value(#{sname},#{scoll})")
int insertStu(Student student);
4.2.4.4 @Update()
@Update("update student set sname=#{sname} where sid=#{sid}")
int updateStu(Student student);
4.2.4.5 @Delete()
@Delete("delete from student where sid=#{sid}")
int deleteStu(int sid);
4.2.4.6 @Results()、@Result()
@Results(id="唯一标识",value={result标签数组})
@Result(id="true/false是否为主键",column="数据库列",property="属性名")
4.2.4.7 @resultMap()
面试题:MyBatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
映射形式有两种情况:
第一种情况:数据库字段名与实体属性名一致。(这种情况下系统会自动完成封装)
第二种情况:数据库字段名与实体属性名不一致。
(1)使用标签 <resultMap> 逐一地定义数据库字段名和实体属性名之间的映射关系。
(2)使用 sql 的设置别名功能,将数据库字段名的别名书写为实体属性名。
4.2.4.8 @One() 一对一查询
one=@One(select="执行方法",fetchType="加载方式(FetchType.LAZY:懒加载)")
类似于mapper.xml中的association
数据库表结构
user类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int uid;
private String username;
private String password;
private Role role;
}
userMapper接口
public interface UserMapper {
@Select("select * from user")
@Results(id="uid",value = {
@Result(id = true,column ="uid",property = "uid"),
@Result(id=false,column = "username",property = "username"),
@Result(id=false,column = "password",property = "password"),
// column代表查询条件对应的值 一般使用主键
@Result(column = "uid",property = "role",one = @One(
select = "mapper.RoleMapper.selectByUid",fetchType = FetchType.LAZY
))
})
ArrayList<User> select();
}
role类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
private int rid;
private String name;
}
roleMapper接口
public interface RoleMapper {
@Select("select r.* from user_role ur,role r where r.rid=ur.rid and uid=#{uid}")
Role selectByUid(@Param("uid") int uid);
}
由于设置的懒加载,所以数据是动态加载的(sql执行了两次),而不是一次全部加载。
4.2.4.9 @Many() 一对多查询
用法和@one一样
:@Many(select="执行方法",fetchType="加载方式"(FetchType.LAZY :懒加载))
类似于mapper.xml中的colleaction
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
private int rid;
private String name;
//一对多,一个role可能对应一个或多个admin
private ArrayList<Admin> adlist;
}
@Select("select * from role")
@Results(id = "rid" ,value = {
@Result(id = true,column = "rid" ,property = "rid"),
@Result(column = "name",property = "name"),
@Result(column = "rid",property = "plist",many = @Many(
select = "mapper.PermissionMapper.selectByRid",fetchType = FetchType.LAZY
))
})
ArrayList<Role> select();
4.2.5 深入理解MyBatis
4.2.5.1 MyBatis核心组件
在Mybatis中,SqlSession对数据库的操作,委托给执行器Executor来完成
Mybatis执行过程,主要的执行模块是: SqlSession->Executor->StatementHandler->数据库
核心组件:
1.动态代理 MapperProxy
2.SQL会话 SqlSession
3.执行器 Executor
4.JDBC处理器 StatementHandler
4.2.5.1.1 MapperProxy
代理
4.2.5.1.2 SqlSession
SqlSession采用了门面模式方便来让我们使用。他不能跨线程使用,一级缓存生命周期和它一致;
基本功能:增删改查基本的操作功能;
辅助功能:提交、关闭会话;
门面模式:提供一个统一的门面接口API,使得系统更加容易使用。
4.2.5.1.3 Executor
基本功能:改、查、 维护缓存;
辅助功能:提交、关闭执行器、 批处理刷新;
Executor 主要负责维护一级缓存和二级缓存,并提供事务管理的相关操作,它会将数据库相关操作委托给 StatementHandler完成。
我们先回顾一下JDBC的执行器
JDBC有三种执行器:
Statement(简单执行器):执行静态SQL
PreparedStatement(预处理执行器):设置预编译,防止SQL注入
CallableStatement(存储过程执行器):设置参数、读取参数(用于执行存储过程)
MyBatis的执行器
BaseExecutor(基础执行器)实现了Executor接口,其中简单执行器是默认的执行器。
CachingExecutor(二级缓存执行器)你开启二级缓存则会实例化它,在 BaseExecutor 的基础上,实现二级缓存功能。 (注意: BaseExecutor 的本地缓存,就是一级缓存)
BaseExecutor下有三个执行器:
- SimpleExecutor(简单执行器)
- ResuseExecutor(可重用执行器)
- BathExecutor(批处理执行器)
MyBatis核心源码剖析 ⑧ 核心执行器executor详解 - 知乎
4.2.5.1.4 StatementHandler
经过执行器处理后交给了StatementHandler(声明处理器);
主要作用就是:参数处理、结果处理;
StatementHandler 首先通过 ParameterHandler 完成 SQL 语句的实参绑定,然后通过java.sql.Statement
对象执行 SQL 语句并得到结果集,最后通过 ResultSetHandler 完成结果集的映射,得到结果对象并返回。
4.2.5.1 MyBatis与JDBC
1、JDBC数据库连接频繁创建、释放造成系统资源的浪费,影响系统性能。MyBatis中可在SqlMapConfig.xml中配置数据库连接池。
2、传统sql语句和代码书写在一起,由于sql变换较大,导致代码维护不易。MyBatis将sql与代码分离。
3、向sql语句传参数麻烦,sql语句的where条件各不相同,而且必须保证占位符和参数一一对应。MyBatis自动将java对象映射至sql语句。
4、对结果集解析麻烦,且解析前需要对结果集遍历。MyBatis自动将sql执行的结果集映射至java对象。
如;
<select id="selectAll" resultType="com.javaBean.User">
select * from t_user
</select>resultType:就是将结果集映射成指定的对象
4.2.5.2 MyBatis步骤
- 创建SqlSessionFactory
- 通过SqlSessionFactory创建SqlSession
- SqlSession执行数据库操作
- session.commit()提交事务
- session.close()关闭会话
4.2.5.3 MyBatis实现分页
有四种实现分页方式
- 查询出所有信息,自定义分页
- 使用limit分页
- RowBounds分页
- pageHelper插件分页
4.2.6.1
4.2.5.4 MyBatis批处理
4.2.5.4.1 Foreach标签
批量插入
<!-- 批量添加 -->
<insert id="insertUserList">
insert into user (username,password,phone) values
<foreach collection="list" item="value" separator=",">
(
#{value.username},
#{value.password},
......
)
</foreach>
</insert>
批量更新
<update id="updateByBatch" parameterType="java.util.List">
<foreach collection="list" item="value" index="index" open="begin" close=";end;" separator=";">
UPDATE user
<set>
PRC_RESULT = #{value.password},
PRC_ERRMSG = #{value.name}
</set>
WHERE id= #{id}
</foreach>
</update>
批量删除
<!-- 批量删除 -->
<delete id="deleteByArray">
delete from user where uid in
<foreach collection="array" item="value" open="(" close=")" separator=",">
#{value}
</foreach>
</delete>
4.2.5.4.2 BATCH
要批量执行的话,JDBC连接URL字符串中需要新增一个参数:rewriteBatchedStatements=true
master.jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&rewriteBatchedStatements=true
MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效
publicvoid test() {
// Spring 集成的情况下,事务连接由 Spring 管理,所以可以不手动提交
//打开批处理
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
OrderDetailMapper mapper = sqlSession.getMapper(OrderDetailMapper.class);
for(int i=0;i<=List.size()-1;i++){
mapper.insert(List.get(i));
}
}
添加rewriteBatchedStatements=true这个参数后的执行速度比较:
同个表插入一万条数据时间近似值:
JDBC BATCH 1.1秒左右 > Mybatis BATCH 2.2秒左右 > 拼接SQL 4.5秒左右
4.2.5.5 Mapper接口可以重载吗
在mybatis框架中,写Dao层的Mapper接口时是不可以进行方法的重载的
MyBatis使用package+Mapper+method全限名作为key去调用方法。
假设有方法重载
public interface Mapper{
public int function(int id);
public int function(String name);
}
<select id="function" />
这里的id指的是哪个?所以是不允许Mapper中方法重载的
4.2.5.6 MyBatis一级二级缓存
4.2.5.6.1 三个问题
什么是缓存
缓存就是存储在内存中的临时数据,因为内存读取速度快,但是只能放少量的数据
为什么需要缓存
对数据库的操作,最多的是查询操作。使用了缓存之后,服务器只需要查询一次数据库,然后将数据保存到服务器主机的内存中,以后读取时就直接取内存中的数据,而不需要每次都查数据库,这种方案除了降低数据库压力之外,还提高了响应速度
哪些数据会被放到缓存中
通常情况下,都会将那些变化较少且经常用到的数据会放到缓存中,比如像字典、系统参数、有固定值的状态码等等;另外将用户保存到缓存也是一种很好的策略,这样登录的时候就可以极速响应了。
4.2.5.6.2 一级缓存
一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空
一级缓存是默认开启的,它在一个sqlSession会话里面的所有查询操作都会保存到缓存中,一般来说一个请求中的所有增删改查操作都是在同一个sqlSession里面的,所以我们可以认为每个请求都有自己的一级缓存
4.2.5.6.3 二级缓存
二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为Mapper(Namespace),全局。也就是说;多个请求可以共用一个缓存,二级缓存需要手动开启。要开启二级缓存,使用二级缓存属性类需要 实现 Serializable 序列化接口(可用来保存对象的状态),可在它的映射文件中配置
<cache/>
//就一个这
缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中;
开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找
对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存 Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将 被 clear。
4.2.5.7 Mapper 接口
提出个问题:通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, 请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法, 参数不同时,方法能重载吗?
Dao 接口即 Mapper 接口。
接口的全限名,就是映射文件中的 namespace 的值;
接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值;
接口方法内的 参数,就是传递给 sql 的参数。
Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符 串作为 key 值,可唯一定位一个 MapperStatement。在 Mybatis 中,每一个标签,如<select>、<insert>,都会被解析为一个 MapperStatement 对象
Mapper 接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。