(一)Mybatis映射器
在JPA中,可以只写接口,不写实现,同样在Mybatis中也可以只写接口,不写实现,可以直接通过映射器,让Mybatis来管理实现接口。
- 准备
在JPA中,dao层被命名为resptory,这是规范,同样在Mybatis中,也要求dao层命名为mapper。(注:使用dao命名也不影响功能的实现)
① mapper层:
1)新建接口(不写实现的接口,并在接口内添加方法,注意接口名可以不加I,但需要根据公司具体需求),例如:UserMapper.java
每一个mapper都会对应一个映射文件
public interface UserMapper {
List<User> findAll();
}
2)新建映射文件 UserMapper.xml。
注意:xml文件内配置的命名空间使用接口的全限定名;
<mapper namespace="cn.lzj.mapper.RoleMapper">
<!--查询全部数据-->
<select id="findAll" resultType="User">
select * from user
</select>
</mapper>
3)在MyBatis的主配置文件内,引入mapper的映射文件
<mappers>
<mapper resource="cn/lzj/mapper/userMapper.xml"/>
<mapper resource="cn/lzj/mapper/roleMapper.xml"/>
</mappers>
4)测试:通过MyBatis的映射器,获取DB中的数据,能成功获取
public void testFindAll() throws Exception{
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.findAll().forEach(e-> System.out.println(e));
}
(二)使用Mybatis映射器,对数据进行增删改查
- UserMapper.java接口
public interface UserMapper {
User findOne(Long id); //查询一条
List<User> findAll(); //查询所有
void save(User user); //添加
void delete(Long id); //删除
void update(User user); //动态修改(避免未修改的值变为null)
void batchSave(List<User> list); //批量添加
void batchDelete(List<Long> id); //批量删除
List<User> selectByLike(UserQuery query); //高级查询
Long countSelect(UserQuery query); //计数-->和高级查询共同使用一组sql,抽取公共的sql
}
- UserMapper.映射文件
<?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="cn.lzj.mapper.UserMapper">
<!--查询一条-->
<select id="findOne" parameterType="long" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
<!--查询全部-->
<select id="findAll" resultType="User">
SELECT * FROM user
</select>
<!--添加-->
<insert id="save" parameterType="User">
INSERT INTO user(name,age)
VALUES (#{name},#{age})
</insert>
<!--删除-->
<delete id="delete" parameterType="long">
DELETE FROM user WHERE id = #{id}
</delete>
<!--修改-->
<update id="update" parameterType="User">
UPDATE user
<set>
<if test="name!=null and name!=''">
name = #{name},
</if>
<if test="age!=null">
age =#{age},
</if>
</set>
WHERE id = #{id}
</update>
<!--
批量添加:
foreach标签:实现遍历
collection:遍历的集合 item:集合的每一个元素 separator:分隔符
-->
<insert id="batchSave" parameterType="list">
INSERT INTO user(name,age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name},#{user.age})
</foreach>
</insert>
<!--
批量删除
open="(":开始 close=")":结束 index:索引
参数推荐使用集合,因为数组无法进行变化
-->
<delete id="batchDelete" parameterType="list">
DELETE FROM user WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<-- 传数组的方法
<delete id="batchDelete" parameterType="long[]">
delete from employee where id in
<foreach collection="array" item="v" open="(" close=")" separator=",">
#{v}
</foreach>
</delete>
-->
<!--
高级查询:
使用Query对象,封装接收前台用户所传入的查询的参数,因为用户所传入的参数是动态变化的
-->
<select id="selectByLike" parameterType="cn.lzj.domain.UserQuery" resultType="user">
SELECT * FROM user <include refid="whereSql"/>
</select>
<select id="countSelect" parameterType="cn.lzj.domain.UserQuery" resultType="long">
SELECT COUNT (id) FROM user <include refid="whereSql"/>
</select>
<!--
SQL标签:用于抽取公共的sql语句
对于特殊符号,如小于"<",可以进行转义:<:小于 >:小于 也可以使用CDATA区
where标签:用于将第一个and转换为where
-->
<sql id="whereSql">
<where>
<if test="name != null and name !=''">
name LIKE CONCAT("%",#{name},"%")
</if>
<if test="minAge != null and maxAge !=''">
AND age >= #{minAge}
</if>
<if test="maxAge != null and maxAge !=''">
AND <![CDATA[age <= #{maxAge}]]> <!--可以转义,也可以使用CDATA区,不解析内部的字符串,即不会解析到"<"-->
</if>
</where>
</sql>
</mapper>
- 注意点
①sql语句的id名,必须和接口中的方法名对应一致
分析:
②批量添加时,value值的name和age需要指明是哪一个domain对象的属性
③注意批量删除数据的时候,属性不要写错了,如:open不要写成了index
④高级查询时,使用了where标签,就不要再写where了:
细节:
a .准备一个Query对象(封装所有条件)
b. 模糊查询 concat("%",#{name},"%")
c. 遇到特殊符号 1.转义 < 2.CDATA段 <![CDATA[...]]>
d. 使用where标签(第一个and变成where)
e. if中有多个条件使用 and/or 进行关联
f. 如果出现相就的代码,可以单独抽取sql标签,引用include即可
⑤动态修改数据时,不要忘记添加",",否则只能修改一个属性,若修改多个属性就会报错
(三)通过Mybatis映射器配置多对一关系
(1) 准备
domain之间的关系,在JPA直接可以通过加注解,JPA就可以自动为我们实现,但是使用MyBatis,需要我们自己写SQL进行关系的配置
1.准备
① 新建一个role表
②在user表中添加外键字段
注意:
在此处我们只添加外键字段,一般不在表中添加外键,使用代码进行关系的维护。这样可以避免每次根据外键查询,降低性能,但是不添加外键易出现无效数据和有问题的代码,但是性能提升了,所以还需要根据公司的需求,选择是否添加外键。
③user类中配置外键
public class User {
private Integer id;
private String name;
private Integer age;
private Role role; //外键 底层会自动配置role_id
//省略getter、setter和toString方法
}
④ role类
public class Role {
private Long id;
private String name;
//省略getter、setter和toString方法
}
(2) 使用嵌套结果(1条sql)方式数据的查询
注意:
①查询的sql要关连多张表(一定要取别名,不然有些名称会产生冲突)
②当我们使用了association 后默认的映射失败,需要自己手动完成映射
- UserMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.UserMapper">
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--嵌套结果配置多对一关系
association: 多对一,一对多都是它(代表要关连一个对象)
当你使用了这个标签,自动映射就失效
property="department" 属性名称 javaType:属性的类型
-->
<association property="role" javaType="Role">
<id property="id" column="rid"/>
<result property="name" column="rname"/>
</association>
</resultMap>
<!--
查询全部数据
当有字段名称或者类型对应不上了,就需要我们写手动映射
如果在查询中出现相同名称的字段,为了区分,我们需要为它取别名
-->
<select id="findAll" resultMap="userMap">
SELECT u.* , r.id rid , r.name rname FROM user u LEFT JOIN role r ON r.id = u.role_id
</select>
</mapper>
(3) 使用嵌套查询(n+1条sql)方式数据的查询
注意:
① 会产生n+1条sql
② 需要去找到对应的那条sql并且执行
③ 保证MyBatis能找到这两个xml
- roleMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.RoleMapper">
<select id="findOne" resultMap="roleMap">
select * from role where id=#{id}
</select>
</mapper>
- userMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.UserMapper">
<!--
通过代码配置关系映射
依然需要做结果映射
column="department_id" :列名(待会执行sql需要传入的数据)
select:去获取相应的SQL
-->
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--嵌套查询,配置多对一的关系-->
<association property="role" javaType="Role" column="role_id" select="cn.lzj.mapper.RoleMapper.findOne">
</association>
</resultMap>
<!--查询全部数据-->
<select id="findAll" resultMap="userMap">
select u.*,r.id rid,r.name rname from user u LEFT JOIN role r on r.id = u.role_id
</select>
</mapper>
(4) 野方法
注意:此方法只是能实现功能,获取每一个user及其所对应的role,但可能会出现一定的问题,推荐使用嵌套查询或嵌套结果进行数据的获取
<mapper namespace="cn.lzj.mapper.UserMapper">
<resultMap id="userMap" type="User">
<result property="role.id" column="rid"/>
<result property="role.name" column="rname"/>
</resultMap>
<!--查询全部数据-->
<select id="findAll" resultMap="userMap">
select u.*,r.id rid,r.name rname from user u LEFT JOIN role r on r.id = u.role_id
</select>
</mapper>
(三)通过Mybatis映射器配置一对多关系
(1) 准备
- 一方,即role内配置外键
public class Role {
private Long id;
private String name;
private List<User> user = new ArrayList<>();
//省略getter、setter和toString方法
}
(2) 使用嵌套结果方式数据的查询
roleMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.RoleMapper">
<resultMap id="roleMap" type="Role">
<id property="id" column="rid"/>
<result property="name" column="rname"/>
<!--一对多-->
<collection property="user" ofType="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="role" column="role_id"/>
</collection>
</resultMap>
<!--注意:此处只能使用左外联查询,因为是以一方为主要查询对象-->
<select id="findAll" resultMap="roleMap">
select u.*,r.id rid,r.name rname from role r LEFT JOIN user u on r.id = u.role_id
</select>
</mapper>
(3) 使用嵌套查询方式数据的查询
userMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.UserMapper">
<select id="findByRoleId" parameterType="long" resultType="User">
select * from user u where role_id = #{id}
</select>
</mapper>
roleMapper.xml映射文件
<mapper namespace="cn.lzj.mapper.RoleMapper">
<resultMap id="roleMap" type="Role">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--
一对多 : 使用集合进行配置
-->
<collection property="user" ofType="User" column="id" select="cn.lzj.mapper.UserMapper.findByRoleId">
</collection>
</resultMap>
<!--注意:此处只能使用左外联查询,因为是以一方为主要查询对象-->
<select id="findAll" resultMap="roleMap">
select * from role
</select>
</mapper>
(4)级联保存
- 准备两个Mapper
- 保存部门后需要马上拿到它的id
- 保存员工传的是Map{List,deptId}
departmentMapper.xml
<!--保存后需要拿到id-->
<insert id="save" parameterType="department"
useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into department (name) values (#{name})
</insert>
employeeMapper.xml
<insert id="batchSave" parameterType="map">
insert into employee (name,age,sex,dept_id) values
<foreach collection="list" item="emp" separator=",">
(#{emp.name},#{emp.age},#{emp.sex},#{deptId})
</foreach>
</insert>
(四)缓存
- 自带一级级联
- 二级缓存需要加上标签
- 二级缓存的对象必需是序列化对象 … implements Serializable