MyBatis
ORM 持久层开源框架
一、解决的问题(原生 JDBC 存在)
- 数据库配置消息 存在硬编码问题 - 配置文档(不常变动)
- 频繁创建、释放数据库连接,造成资源浪费 - 连接池
- SQL语句、参数设置、获取结果集 存在硬编码问题 - 配置文档(常变动)
- 手动封装结果集,较为繁琐 - 反射、内省
注: 常变动 与 不常变动 的配置文档要分开存放
二、名词解释
-
反射: 在程序运行时,可以动态获取到某个对象所有的属性及方法,且能进行调用。
// 目标类 Object target = new Object(); // Class 定义 Class<?> clazz = Class.forName("类全限定路径"); // -------- 属性 --------- // 属性定义 Field declaredField = clazz.getDeclaredField("属性名称"); // 该属性允许暴力访问 declaredField.setAccessible(true); //݇ 反射获取属性值 Object value = declaredField.get(target); // -------- 方法 --------- // 方法定义 Method method = clazz.getMethod("方法名", args); // 方法调用 method.invoke(target, args);
-
内省: 获取指定类,指定属性的 读、写 方法。
// 目标类 Object target = new Object(); // Class 定义 Class<?> clazz = Class.forName("类全限定路径"); // 属性描述器 PropertyDescriptor propertyDescriptor = new PropertyDescriptor("属性名", clazz); // ----------- 写方法 ---------- // 获得写方法 Method writeMethod = propertyDescriptor.getWriteMethod(); // 调用写方法 writeMethod.invoke(target, "值"); // ----------- 读方法 ---------- // 获得读方法 Method readMethod = propertyDescriptor.getReadMethod(); // 调用读方法 Object value = readMethod.invoke(target);
-
ORM: Object/Relation Mapping - 对象关系映射 - 通过操作 POJO 对象,达到操作数据库的目的
-
JDBC: Java Database Connectivity - Java数据库连接规范
三、动态 SQL
- where : 加上 where 1=1
- if : test 条件为true,拼接此 SQL 语句
- foreach
-
collection : 要遍历的元素集合 array - 数组,list - 集合
-
open : 开头
-
close : 结尾
-
item : 遍历出来元素的别名
-
seperator : 分割占位符
-
- sql : SQL 抽取,使用 include 重复利用
<!-- 重复SQL语句抽取 -->
<sql id="selectUser">select * from User</sql>
<select id="findById" parameterType="user" resultType="user">
<!-- 重复SQL语句使用 -->
<include refid="selectUser"></include> where id=#{id}
<!-- 条件语句 -->
<if test="username != null">
and username=#{username}
</if>
</select>
<select id="findByIds" parameterType="list" resultType="user">
select * from User
<!-- 拼接 where 1=1 -->
<where>
<!-- 遍历集合 -->
<foreach collection="array" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
四、复杂映射
如果表字段有相同名称,需要进行重命名
1. 一对一
方式 1 :
<mapper namespace="com.lagou.mapper.OrderMapper">
<resultMap id="orderMap" type="com.lagou.domain.Order">
<!-- 映射 SQL语句查出来的表字段 与 order类中的属性名与对应字段 -->
<result column="uid" property="user.id"></result>
<result column="username" property="user.username"></result>
<result column="password" property="user.password"></result>
<result column="birthday" property="user.birthday"></result>
</resultMap>
<select id="findAll" resultMap="orderMap">
select * from orders o, user u where o.uid=u.id
</select>
</mapper>
方式 2 :
<mapper namespace="com.lagou.mapper.OrderMapper">
<resultMap id="orderMap" type="com.lagou.domain.Order">
<result property="id" column="id"></result>
<result property="ordertime" column="ordertime"></result>
<result property="total" column="total"></result>
<!-- 设置 order 类中的属性名与类型 -->
<association property="user" javaType="com.lagou.domain.User">
<!-- 设置表字段与属性的对应关系 -->
<result column="uid" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
</mapper>
2. 一对多
<mapper namespace="com.lagou.mapper.UserMapper">
<resultMap id="userMap" type="com.lagou.domain.User">
<result column="id" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!-- 标记该属性为集合类型,并设置属性名称,与属性对应的类型 -->
<collection property="orderList" ofType="com.lagou.domain.Order">
<result column="oid" property="id"></result>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
<!-- 如果表字段重名,则可以进行重命名,否则 MyBatis 将无法判断 -->
select *,o.id oid from user u left join orders o on u.id=o.uid
</select>
</mapper>
3. 多对多 (两个一对多)
<mapper namespace="com.lagou.mapper.UserMapper">
<resultMap id="userRoleMap" type="com.lagou.domain.User">
<result column="id" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!-- 标记该属性为集合类型,并设置属性名称,与属性对应的类型 -->
<collection property="roleList" ofType="com.lagou.domain.Role">
<result column="rid" property="id"></result>
<result column="rolename" property="rolename"></result>
</collection>
</resultMap>
<select id="findAllUserAndRole" resultMap="userRoleMap">
select u.*,r.*,r.id rid from user u
left join user_role ur on u.id = ur.user_id
inner join role r on ur.role_id=r.id
</select>
</mapper>
五、缓存
1. 一级缓存 (默认开启)
SqlSession 级别的缓存,仅在同一个连接内有效
缓存刷新
- 中途运行 commit 操作 (增、删、改)
- 手动刷新 SqlSession.clearCache()
2. 二级缓存 (默认不开启,通常与Redis集成实现)
Mapper 级别的缓存,多个 SqlSession,在使用同一个 Mapper 内有效。
-
当SqlSession运行close()方法并且一级缓存有内容时,才会将一级缓存的内容存到二级缓存中
-
多表查找不会触发二级缓存
-
当对 Mapper 进行 commit 操作 (增、删、改),刷新缓存
-
使用 useCache = false 属性,可以禁用特定 SQL 的二级缓存
六、插件
允许拦截的类
- Executor 运行器 : 负责增、删、改、查的行为。
- StatementHandler SQL语法构建器 : 完成SQL的预编译。
- ParameterHandler 参数处理器 : 设置参数。
- ResultHandler 结果集处理器 : 负责处理返回结果集。