Mybatis
一、Mybatis简介
1.1 认识mybatis
MyBatis 是Apache的一个开源项目iBatis,是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
简单的说:MyBatis是一个半自动ORM框架,其本质是对JDBC的封装。使用MyBatis重点需要程序员编写SQL命令,不需要写一行JDBC代码。
1.2 mybatis和Hibernate
- Mybatis半自动化框架
表需要手动进行设计 |
---|
依赖数据库平台 |
优化灵活,合适做互联网项目 |
- Hibernate自动化ORM框架
表可以通过框架自动创建 |
---|
省略一些基本的SQL |
不依赖于数据库平台 |
优化难度大,不适合做大型互联网项目 |
二、Mybatis环境搭建
2.1 创建java项目
2.2 导入jar包
2.2.1 mybatis核心包
2.2.2 mybatis依赖jar包
2.2.3 数据库驱动jar包
2.3 配置mybatis.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>
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/xxxx/mappers/DeptMapper.xml"/>
<mapper resource="com/homework/mapper/EmpMapper.xml"/>
</mappers>
</configuration>
2.4 mybatis SQL映射文件
<?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:映射文件的包名+xml的名称-->
<mapper namespace="com.xxxx.mappers.DeptMapper">
<select id="queryAll" resultType="com.xxxx.pojo.Dept">
select deptno,dname,loc from dept
</select>
</mapper>
2.5 log4j
log4j分为五个级别:DEBUG(人为调试信息)、INFO(普通信息)、WARN(警告)、ERROR(错误)和FATAL(系统错误)。
这五个级别的顺序分别为:DEBUG < INFO < WARN < ERROR < FATAL。
注意:使用log4j打印时除了要导入相应的jar包之外,还要在src下建立一个log4j.properties的配置文件
# Set root category priority to INFO and its only appender to CONSOLE.
# log4j.rootCategory=DEBUG, CONSOLE
#log4j.rootCategory=INFO, CONSOLE, LOGFILE
log4j.rootLogger=info,stdout
# 单独设置SQL语句的输出级别为DEBUG级别
# 方法级别
# log4j.logger.com.xxxx.mappers.EmpMapper.queryAll=DEBUG
# 类级别
# log4j.logger.com.xxxx.mappers.EmpMapper=DEBUG
# 包级别
log4j.logger.com.xxxx.mappers=DEBUG
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=D:/test.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n
2.6测试
public class DeptDemo01 {
public static void main(String[] args) throws IOException {
//获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
//获取会话对象
SqlSession sqlSession = sessionFactory.openSession();
//执行sql语句
List<Dept> list = sqlSession.selectList("com.xxxx.mappers.DeptMapper.queryAll");
list.forEach(System.out::println);
sqlSession.close();
}
}
三、Mybatis配置的完善
3.1 使用别名alias
作用:用来减少类完成全限定名的多于部分,别名都是大小写不敏感的
不使用别名
<!--要指定返回类型的具体类型-->
<select id="queryAll" resultType="com.xxxx.pojo.dept">
select deptno,dname,loc from dept
</select>
使用package指定某个包下所有类的默认别名
<!--表示pojo包下的所有实体类的别名就是类名,存在mybatis的配置文件中-->
<typeAliases>
<package name="com.xxxx.pojo"/>
</typeAliases>
使用别名之后
<select id="queryAll" resultType="dept">
select deptno,dname,loc from dept
</select>
3.2 引入属性文件(properties)
用法:将driver,url,userName,passWord分装进一个src目录下的jdbc.properties当中,需要使用的时候取出
<configuration>
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/xxxx/mappers/DeptMapper.xml"/>
</mappers>
</configuration>
四、parameterType入参类型
4.1 传入一个参数的查询
<select id="queryById" parameterType="int" resultType="emp">
select * from emp where empno=#{empno}
</select>
//通过ID查询员工信息
Emp emp = sqlSession.selectOne("com.xxxx.mappers.EmpMapper.queryById", 7369);
4.2 多个参数查询
<!-- 根据员工姓名和薪资查询员工信息 -->
<select id="queryByEmp" parameterType="emp" resultType="emp">
select * from emp where ename = #{ename} and sal = #{sal}
</select>
Emp e = new Emp();
e.setename("SMITH");
e.setSal(800.00);
List<Emp> list4 = sqlSession.selectList("com.xxxx.mappers.EmpMapper.queryByEmp",e);
4.3 入参类型
parameterType类型有:基本数据类型(四类八种) 包装类 String Date Javabean Map List 数组
4.3.1 基本数据类型、包装类、String
<!-- 通过ID查询员工信息 -->
<select id="queryById" parameterType="int" resultType="emp">
select * from emp where empno=#{empno}
</select>
Emp emp = sqlSession.selectOne("com.xxxx.mappers.EmpMapper.queryById", 7369);
4.3.2 javaBean类型
<!-- 根据员工姓名和薪资查询员工信息 -->
<select id="queryByEmp" parameterType="emp" resultType="emp">
select * from emp where ename = #{ename} and sal = #{sal}
</select>
Emp e = new Emp();
e.setename("SMITH");
e.setSal(800.00);
List<Emp> list4 = sqlSession.selectList("com.xxxx.mappers.EmpMapper.queryByEmp",e);
4.3.3 map
<!-- 入参类型: Map 根据多个id,查询用户信息-->
<select id="queryUserByIdMap" parameterType="map"
resultType="user">
select * from t_user where id = #{id1} or id=#{id2}
</select>
Map<String,Object> paras = new HashMap();
paras.put("id1", 131);
paras.put("id2", 132);
List<User>
list=session.selectList("com.shsxt.mapper.UserMapper.queryUserByIdMap", paras);
4.3.4 List、数组
<!-- 根据员工编号信息,collection表示传入的集合 -->
<select id="queryByList" resultType="emp">
select * from emp where empno in (
<foreach collection="list" item="args" separator=",">
#{args}
</foreach>
)
</select>
List list5= Arrays.asList(7369,7521,7900);
List<Emp> list6 = sqlSession.selectList("com.xxxx.mappers.EmpMapper.queryByList", list5);
4.3.5 Date
<!-- 查看指定日期入职的员工信息 -->
<select id="queryByDate" parameterType="date" resultType="emp">
select * from emp where hiredate=#{hiredate}
</select>
//查看指定日期入职的员工信息
Date date = new SimpleDateFormat("yyyy/MM/dd").parse("1981/12/3");
List<Emp> list3 = sqlSession.selectList("com.xxxx.mappers.EmpMapper.queryByDate",date);
五、普通查询方式
5.1 三个查询方法(无接口)
- selectList(“命名空间.id”) 用户查询多条数据情况,返回一个List集合, 没有
查到数据返回空集合,不是null
List<User> list =
session.selectList("com.shsxt.mappers.UserMapper.queryAll");
System.out.println(list);
- selectOne(“命名空间.id”) 用于查询单条数据,返回一个数据, 如果没有查
到返回null
String name = session.selectOne("com.xxxx.mappers.EmpMapper2.queryNameById",7369);
- selectMap(“命名空间.id”,key的字段名) 用于查询多条记录情况, 返回Map
集合, 需要指定那个属性作为key, sql查询结果作为value,指定的字段值作
为key, 如果查不到, 返回一个空map集合,不是null
//3)selectMap("namespace.id","执行作为key的属性")
Map<Integer,User> map =
session.selectMap("com.shsxt.mappers.UserMapper.queryAll",
"id");
六、使用Mapper代理(接口绑定)(重点)
前面已经使用MyBatis完成了对Emp表的CRUD操作,都是由SqlSession调用自身方法发送SQL命令并得到结果的。
6.1 使用Mapper代理的方式
6.1.1 接口的定义
public interface EmpMapper {
//查询所有
List<Emp> findAll();
}
6.1.2 映射文件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="com.xxxx.mappers.EmpMapper">
<select id="findAll" resultType="emp">
select * from emp
</select>
</mapper>
6.1.3 在核心配置文件中扫描接口
(1)扫描单个接口
<mappers>
<mapper class="com.shsxt.mapper.UserMapper"/>
</mappers>
(2)扫描多个接口
<mappers>
<package name="com.shsxt.mapper"/>
</mappers>
6.1.4 使用方式
public void test(){
//获取session会话对象,MybatisUtils为自己封装的工具类
SqlSession session = MybatisUtils.getSession();
//获取接口的实现类对象
UserMapper mapper=session.getMapper(UserMapper.class);
//通过对象调用方法
List<User> list = mapper.queryAll();
list.foreach(System.out::println);
session.close();
}
6.2 Mapper代理模式下的参数处理
6.2.1 多参数传递
-
方式一:在接口中直接传递多个参数
映射文件中,参数可以使用param1,param2…表示,或者使用arg0,arg1…表示,可读性低。
<select id="selectStudent" resultType="student">
select * from t_student where sname=#{param1} and spassword=#{param2}
</select>
-
方式二:在接口中使用Param注解传递多个参数
映射文件中参数可以使用Param注解指定的名称来表示,同时保留使用param1, param2…表示,但是不可以再使用arg0,arg1…表示。
<select id="selectStudent" resultType="student">
select * from t_student where sname=#{sName} and spassword=#{sPassword}
</select>
Student selectStudent(@Param("sName") String sName,@Param("sPassword") String sPassword);
-
方式三:使用JavaBean传递多个参数
映射文件中的参数直接使用JavaBean的属性来接收,可读性高。底层调用是相应 属性的getter方法。
<insert id="insertStudent" parameterType="student">
insert into t_student(sno,sname,gender,sbirthday,spassword) values(sq_class_id.nextval,#{sName},#{gender},#{sBirthday},#{sPassword})
</insert>
int insertStudent(Student student);
-
方式四:使用Map传递多个参数
映射文件中使用相应参数在map中的key来表示。
七、动态SQL
7.1 if
用于进行条件判断, test 属性用于指定判断条件,为了拼接条件, 在 SQL 语句
后强行添加 1=1 的恒成立条件。(test中的username就是#{username})
<select id="sel" resultType="user">
select * from t_user where 1=1
<if test="username != null and username != ''">
and username=#{username}
</if>
<if test="password != null and password != ''">
and password=#{password}
</if>
</select>
7.2 where
使用这个就不用提供where 1=1 这样的条件了。它的功能有:
- 会自动添加where关键字
- 如果第一个条件中存在and,那么将它除去
<select id="selectEmpNameDeptno" resultType="emp">
select * from emp
<where>
<if test="ename!=null and ename!=''">
and ename=#{ename}
</if>
<if test="deptno!=null and deptno!=''">
and deptno=#{deptno}
</if>
</where>
</select>
7.3 choose…when…otherwise
这个相当于switch…case…default
<!--只能根据其中任意一个条件去查-->
<select id="selectEmpno" resultType="emp">
select * from emp
<where>
<choose>
<when test="ename!=null and ename!=''">
and ename=#{ename}
</when>
<when test="deptno!=null">
and deptno=#{deptno}
</when>
</choose>
</where>
</select>
7.4 set
功能有:
- 会自动添加set关键字
- 会自动除去最后一条语句后面的逗号
<update id="updateEmp" parameterType="Emp">
update emp
<set>
empno=#{empno},
<if test="sal>0">
sal=#{sal},
</if>
<if test="ename!=null and ename!=''">
ename=#{ename}
</if>
</set>
where empno=#{empno}
</update>
7.5 trim
用于在前后添加或删除一些内容
-
prefix, 在前面添加内容
-
prefixOverrides, 从前面去除内容
-
suffix, 向后面添加内容
-
suffixOverrides, 从后面去除内容
<update id="updateEmp" parameterType="emp">
update emp
<trim prefix="set" suffixOverrides=",">
empno=#{empno},
<if test="sal>0">
sal=#{sal},
</if>
<if test="ename!=null and ename!=''">
ename=#{ename},
</if>
</trim>
where empno=#{empno}
</update>
7.6 bind
用于数据的连接,一般多用于模糊查询上面,对所需的数据进行加工
<select id="selectEmpByEname" resultType="emp">
select <include refid="emp_all"/> from emp
<where>
<if test="ename!=null and ename!=''">
<bind name="ename" value="'%'+ename+'%'"/>
and ename like #{ename}
</if>
</where>
</select>
7.7 foreach
用于在 SQL 语句中遍历集合参数, 在 in 查询中使用
-
collection: 待遍历的集合
-
open: 设置开始符号
-
item: 迭代变量
-
separator: 项目分隔符
-
close: 设置结束符
<select id="selIn" parameterType="list" resultType="user">
select * from t_user where id in
<foreach collection="list" open="(" separator="," close=")"item="item">
#{item}
</foreach>
</select>
7.8 sql…include
sql用于将相同的、常用的属性给提取出来,include用于引用提取出来的属性
<!--写在与select同级之下-->
<sql id="mySql"> id, username, password </sql>
<select id="selIn" parameterType="list" resultType="user">
select <include refid="mySql" /> from t_user where id in
<foreach collection="list" open="(" separator=","close=")"item="item">
#{item}
</foreach>
</select>
八、 mybatis中的批量处理操作
8.1 批量插入
在测试类中同时创建多个对象,将这些对象批量插入进数据库当中
//批量插入
Emp emp1 = new Emp();
emp1.setEmpno(1);
emp1.setename("zhangsan");
emp1.setDeptno(10);
Emp emp2 = new Emp();
emp2.setEmpno(2);
emp2.setename("李lisi");
emp2.setDeptno(20);
List<Emp> list = Arrays.asList(emp1,emp2);
int rows = mapper.insertEmpSome(list);//Mapper代理接口
System.out.println(rows);
<!-- 批量插入-->
<insert id="insertEmpSome" parameterType="list">
insert into emp(empno,ename,deptno)
<foreach collection="list" item="item" separator="union">
select #{item.empno},#{item.ename},#{item.deptno} from dual
</foreach>
</insert>
注意:这样写每条插入语句中的select这一段语句是通过union进行连接
insert into emp(empno,ename,deptno)
select #{item.empno},#{item.ename},#{item.deptno} from dual
union
select #{item.empno},#{item.ename},#{item.deptno} from dual
8.2 批量修改
Emp emp1 = new Emp();
emp1.setEmpno(1);
emp1.setename("张三丰");//未修改前为zhangsan,想要修改为张三丰
emp1.setDeptno(10);
Emp emp2 = new Emp();
emp2.setEmpno(2);
emp2.setename("李四丰");//未修改前为lisi,想要修改为李四丰
emp2.setDeptno(20);
List<Emp> list = Arrays.asList(emp1,emp2);
mapper.updateEmpSome(list);
<!--批量修改-->
<update id="updateEmpSome" parameterType="list">
<foreach collection="list" item="item" open="begin" close=";end;" separator=";">
update emp set ename=#{item.ename} where empno=#{item.empno}
</foreach>
</update>
注意:这个实际的sql语句是以begin开头,用分号进行间隔,用;end;结尾
begin
update emp set ename=#{item.ename} where empno=#{item.empno};
update emp set ename=#{item.ename} where empno=#{item.empno};
end;
8.3 批量删除
List<Integer> list2=Arrays.asList(1,2);//传入需要删除的条件(empno)
int rows2=mapper.deleteEmpSome(list2);
System.out.println(rows2);
<!--批量删除-->
<delete id="deleteEmpSome" parameterType="list">
delete from emp where empno in
<foreach collection="list" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</delete>
注意:这个实际上就是先循环获取传入的empno,循环完毕之后再进行删除
delete from emp where empno in(#{item},#{item},#{item})
九、 mybatis中的缓存机制
9.1 一级缓存
- 线程级别的缓存,sqlsession级别的缓存,默认是开启的
- 在一个sqlsession生命周期中有效,当使用同一个会话去对数据库进行查询操作时,只会与数据库进行一次交互
- 在同一个sqlsession中,mybatis会把执行的方法和参数通过算法生成的键值,将键值存放到一个map中,如果后面有一样的键,那么就能拿到map中的值,从而不再与数据库进行交互,提高了效率
- 不同的sqlsession之间的缓存是相互隔离的
- 当sqlsession关闭了,缓存清空
- 使用update,insert,delete语句会清空缓存
9.2 二级缓存
-
进程级别的缓存,sqlsessionFactory级别的缓存,默认关闭
-
在一个sqlsessionFactory生命周期中有效,当使用同一个sqlsessionFactory创建会话去对数据库进行查询操作时,只会与数据库进行一次交互
-
二级缓存是以namespace为单位的,不同namespace下的操作互不影响
-
使用insert、update、delete会清空缓存
开启二级缓存的三个开关:
1.mybatis核心配置文件中全面开启二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
2.Mapper映射文件中开启局部二级缓存
<mapper namespace="com.bjsxt.mapper.EmployeeMapper"> <cache/> </mapper>
注意:只有在测试类中执行了commit()操作或者close()操作之后才能将查询的结果从一级缓存放到二级缓存当中
SqlSession sqlSession = factory.openSession();
EmpMapper3 mapper = sqlSession.getMapper(EmpMapper3.class);
Emp emp = mapper.findEmp(7499);
System.out.println(emp);
// sqlSession 只有提交或者close之后才会将数据放入二级缓存
sqlSession.commit();
sqlSession.close();
十、 数据库中列名和实体类的属性名不一致
查询的时候时候使用resultType属性,表示采用mybatis中的Auto-Mapping(自动映射)机制,既相同的列名和表名的属性会自动匹配,但是当数据库的列名和类的属性名不同时,这种自动映射机制就会失效,这时候便需要我们进行手动映射。
10.1 给列取别名
写SQL语句进行查询的时候,对查询的数据库中的列取别名,别名和类的属性名要一致,但是这种方式十分的麻烦。
<select id="selAll" resultType="user">
select id id1, username username1, password password2 from t_user
</select>
10.2 使用resultMap
使用resultMap去代替resultType。
注意:column等号后面的属性名是数据库中的列名,property等号后面的属性名是实体类中的属性名
<resultMap type="user" id="umap">
<id column="id" property="id1" />
<result column="username" property="username1" />
<result column="password" property="password1" />
</resultMap>
<select id="selAll" resultMap="umap">
select id,username,password from t_user
</select>
十一、 多表联合查询
11.1 一对一关联查询
例子:一个员工只能有一个所属部门,emp表中的一条数据对应的dept表中的一条数据
在emp实体类中:
public class Emp implements Serializable {
// 对应Emp表格的八个属性
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
// 对应部门的属性
private Dept dept;
在映射文件中配置:
<mapper namespace="com.bjsxt.mapper.EmpMapper">
<!--使用resultMap定义一对一映射关系-->
<resultMap id="empJoinDept" type="emp">
<!--处理emp原有的8个属性 名称相同也不能省略 -->
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="hiredate" column="hiredate"></result>
<result property="mgr" column="mgr"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
<association property="dept" javaType="dept">
<!--处理一对一属性映射 名称相同也不要省略 -->
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
</association>
</resultMap>
<select id="findEmpJoinDept" resultMap="empJoinDept">
select * from emp e left outer join dept d on e.deptno=d.deptno where e.empno =#{empno}
</select>
</mapper>
11.2 一对多关联查询
例子:一个部门中可以有多个员工,dept表中的一条数据可以对应emp表中的多条数据。
在Dept实体类中:
public class Dept implements Serializable {
private Integer deptno;
private String dname;
private String loc;
private List<Emp> emps;
在映射文件当中:
<mapper namespace="com.bjsxt.mapper.DeptMapper">
<!--使用resultMap 处理一对多映射关系-->
<resultMap id="deptJoinEmps" type="dept">
<!--dept原本的三个属性的映射关系-->
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!--dept 关联多个emp对象的映射关系-->
<collection property="emps" ofType="emp">
<!--处理一对多的每个字段映射关系 -->
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="hiredate" column="hiredate"></result>
<result property="mgr" column="mgr"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
</collection>
</resultMap>
<select id="getDeptJoinEmps" resultMap="deptJoinEmps">
select * from dept d left outer join emp e on d.deptno =e.deptno where d.deptno =#{deptno}
</select>
</mapper>
11.3 多对多关联查询
**例子:**一个员工可能参与了多个项目的研发,一个项目由多个员工参与,员工表中的一条数据对应项目表中的多条数据,项目表中的一条数据对应员工表中的多条,这就是多对多,多对多关系要借助中间表来体现,中间表将一个多对多关系转换为两个一对多。
**需求:**根据员工工号查询该员工参与了哪些项目的研发
项目类:
public class Projects implements Serializable {
private Integer pid;
private String pname;
private Integer money;
项目数(中间表):
public class ProjectRecords implements Serializable {
private Integer empno;
private Integer pid;
/*关联一个对应的项目属性*/
private Projects projects;
员工类:
public class Emp implements Serializable {
// 对应Emp表格的八个属性
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
// 对应项目记录的属性
private List<ProjectRecords> records;
映射文件:
<resultMap id="empJoinProjects" type="emp">
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="hiredate" column="hiredate"></result>
<result property="mgr" column="mgr"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
<collection property="records" ofType="projectRecords">
<id property="empno" column="empno"></id>
<id property="pid" column="pid"></id>
<association property="projects" javaType="projects">
<id property="pid" column="pid"></id>
<result property="pname" column="pname"></result>
<result property="money" column="money"></result>
</association>
</collection>
</resultMap>
<select id="findEmpJoinProjects" resultMap="empJoinProjects">
select * from
emp e left outer join projectrecords p
on e.empno =p.empno
left outer join projects pc
on p.pid =pc.pid
where e.empno = #{empno}
</select>
十二、 多表级联查询
级联查询分为:积极加载策略、延迟加载策略
12.1 级联查询之积极加载
需求:
查询10号部门及该部门的员工信息 。
分析:
可以将需求分为两个部分,一个是根据部门编号查询一个部门,另一个是根据部门编号查询所有员工。
1. 实体类:
public class Dept implements Serializable {
private Integer deptno;
private String dname;
private String loc;
/*组合部门下所有的员工List集合作为属性*/
private List<Emp> emps;
public class Emp implements Serializable {
// 对应Emp表格的八个属性
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
2. 两个接口中定义的查询方法
接口1
public interface EmpMapper {
List<Emp> getEmpByDeptno(int deptno);
}
接口2
public interface DeptMapper {
Dept getDeptByDeptno(int deptno);
}
3. Mapper映射文件
EmpMapper映射文件
<mapper namespace="com.bjsxt.mapper.EmpMapper">
<select id="getEmpByDeptno" resultType="emp">
select * from emp where deptno =#{deptno}
</select>
</mapper>
DeptMapper映射文件
<mapper namespace="com.bjsxt.mapper.DeptMapper">
<resultMap id="deptJoinEmp" type="dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<collection
property="emps"
ofType="emp"
column="deptno"
select="com.bjsxt.mapper.EmpMapper.getEmpByDeptno"
fetchType="eager"
/>
</resultMap>
<select id="getDeptByDeptno" resultMap="deptJoinEmp">
select * from dept where deptno = #{deptno}
</select>
</mapper>
总结:
级联查询通过collection/association标签实现, property代表关联多个对象的属性 ofType/javaType代表关联属性或者集合中元素的数据类型.select属性表示要调用的其他sql语句column表示用上一级中的那个字段的值作为下一级查询的参数。fetchType用来表示级联查询策略
12.2 级联查询之延迟加载
延迟加载又称为按需加载。延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中。
延迟加载的设置:
第一步:全局开关:在mybatis.xml中打开延迟加载的开关。配置完成后所有的association和collection元素都生效
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
lazyLoadingEnabled:是否开启延迟加载。是Mybatis是否启用懒加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
aggressiveLazyLoading:当开启时,任何方法的调用都会懒加载对象的所有属性。否则,每个属性会按需加载,
第二步:fetchType=“lazy”。
十三、 注解开发
当进行简单的CRUD对数据库进行基本操作的话,使用注解的方式是十分的方便的。但如果需要配置resultMap的时候还使用注解的话,这个时候便比较麻烦了,这个时候建议使用mapper.xml进行配置。
注意:当即使用注解开发又使用xml配置,这个时候xml配置的方式会覆盖注解方式。
13.1 CRUD注解
- @Select:类似于select标签
- @Insert:类似于insert标签
- @Update:类似于update标签
- @Delete:类似于delete标签
public interface EmpMapper {
@Select("select * from emp")
public List<Employee> findAll();
@Select("select * from emp where empno = #{param1}")
public Employee findById(int empno);
@Select(value= "select * from emp where job =#{job} and deptno = #{deptno}")
public List<Employee> findEmp(String job,int deptno);
@Insert("insert into emp values(null,#{ename},#{job}," +
"#{mgr},#{hireDate},#{sal},#{comm},#{deptno})")
int saveEmp(Employee emp);
@Update("update emp set job =#{job},sal =#{sal} where empno =#{empno}")
int updateEmp(Employee emp);
@Delete("delete from emp where empno = #{param1}")
int deleteEmp(Employee emp);
}
13.2 其它注解
- @Results:类似于resultMap标签
- @Result:类似于的子标签
- @One:类似于association标签
- @Many:类似于collection标签
public interface StudentMapper {
@Select("select * from t_student")
@Results(
value = { @Result(column="id", property="id",
id=true),
@Result(column="name", property="name"),
@Result(column="age", property="age"),
@Result(column="gender", property="gender"),
@Result(column="cid", property="cid"),
@Result(property="clazz",
one=@One(select="com.bjsxt.mapper.ClazzMapper.selById"),
column="cid")
}
)
List<Student> sel();
}
十五、 注意点
10.1 mybatis中的sql语句的书写
sql语句后面不能带";",如果写了分号会出现找不到标识符的错误。
<insert id="insertDept" parameterType="dept">
insert into dept values(#{deptno},#{dname},#{loc})
</insert>
10.2 mybatis的主配置文件中标签顺序
循序为:properties,settings,typeAliases,typeHandlers,objectFactory,objectWrapperFactory, plugins,environments,databaseIdProvider, mappers
<properties resource="db.properties"/>
<typeAliases>
<package name="com.xxxx.pojo"/>
</typeAliases>
<environments default="development">
10.3 当传入的参数为一个集合
当需要往Mapper.xml中的sql语句中传入一个集合或者数组时,这个集合或者数组中含有多个值,那么这时parameterType可以省略不写。
<!-- 根据员工编号信息,collection表示传入的集合 -->
<select id="queryByList" resultType="emp">
select * from emp where empno in (
<foreach collection="list" item="args" separator=",">
#{args}
</foreach>
)
</select>
List list5= Arrays.asList(7369,7521,7900);
List<Emp> list6 = sqlSession.selectList("com.xxxx.mappers.EmpMapper.queryByList", list5);
10.4 使用Mapper接口代理要满足的要求
-
接口中不能出现重载方法
-
xml和接口要放在同一个包下面
-
xml文件名要和接口名一致
-
namespace属性值必须与接口的全限定名一致
-
id属性名必须与抽象方法名保持一致
-
xml中resultType的值必须与抽象方法的返回值一致
10.5 在核心配置文件中对写sql语句的xml进行扫描
10.5.1 不存在Mapper代理接口
<!--只能用mapper resource这种方式扫描xml文件-->
<mappers>
<mapper resource="com/xxxx/mappers/DeptMapper.xml"/>
<mapper resource="com/homework/mapper/EmpMapper.xml"/>
</mappers>
10.5.2 存在Mapper代理接口
<!--这种两种方式可以扫描mappers包中的接口-->
<mappers>
<mapper class="com.shsxt.mapper.UserMapper"/>
<package name="com.xxxx.mappers"/>
</mappers>
10.6 mybatis中对数据库中数据的查询顺序
(1)二级缓存
(2)一级缓存
```10.3 当传入的参数为一个集合
当需要往Mapper.xml中的sql语句中传入一个集合或者数组时,这个集合或者数组中含有多个值,那么这时parameterType可以省略不写。
<!-- 根据员工编号信息,collection表示传入的集合 -->
<select id="queryByList" resultType="emp">
select * from emp where empno in (
<foreach collection="list" item="args" separator=",">
#{args}
</foreach>
)
</select>
List list5= Arrays.asList(7369,7521,7900);
List<Emp> list6 = sqlSession.selectList("com.xxxx.mappers.EmpMapper.queryByList", list5);
10.4 使用Mapper接口代理要满足的要求
-
接口中不能出现重载方法
-
xml和接口要放在同一个包下面
-
xml文件名要和接口名一致
-
namespace属性值必须与接口的全限定名一致
-
id属性名必须与抽象方法名保持一致
-
xml中resultType的值必须与抽象方法的返回值一致
10.5 在核心配置文件中对写sql语句的xml进行扫描
10.5.1 不存在Mapper代理接口
<!--只能用mapper resource这种方式扫描xml文件-->
<mappers>
<mapper resource="com/xxxx/mappers/DeptMapper.xml"/>
<mapper resource="com/homework/mapper/EmpMapper.xml"/>
</mappers>
10.5.2 存在Mapper代理接口
<!--这种两种方式可以扫描mappers包中的接口-->
<mappers>
<mapper class="com.shsxt.mapper.UserMapper"/>
<package name="com.xxxx.mappers"/>
</mappers>
10.6 mybatis中对数据库中数据的查询顺序
(1)二级缓存
(2)一级缓存
(3)数据库