MyBatis基础

一、MyBatis入门
1.认识框架
框----支撑性,架----约束性,框架对于java来说,就是就是为了解决特定问题而定义的一系列的接口和实现类。
框架就是一个半成品,已经对基础的代码进行了封装并提供相应的API,开发者使用框架就是直接调用已经封装好的API,可以省去很对代码的编写,从而提高工作效率和开发速度
2.认识ORM
2.1什么是ORM?
O---------Object 对象
R---------Relational 关系
M--------Mapping 映射
ORM指对象–关系映射,用于实现面向对象编程语言里不同类型系统的数据之间的转换
对象关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象–关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射
2.2什么是"持久化"?
把数据保存到可永久保存的存储设备中
持久化的主要应用是将内存中的数据存储在关系型的数据库中,也可以存储在磁盘文件、XML文件中
2.3什么是"持久层"?
专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联,在JDBC中使用的DAO层和MyBatis中使用的Mapper层,就是持久层,主要负责与数据库进行交互设计,用来处理数据的持久化工作
2.4为什么要使用MyBatis?
因为MyBatis具有相对轻量级、封装少、映射多样化、支持存储过程、可以进行SQL语句的优化等,符合互联网高并发、大数据、高性能、高响应的要求
二、MyBatis的使用
1.导入jar包
【MyBatis jar包】
链接:https://pan.baidu.com/s/16os9LeyoLCDvojhXKCMybQ
提取码:u95n
【MySql jar包】
链接:https://pan.baidu.com/s/10A3qs_3G21tG_XAMNY0F9Q
提取码:bygc
1.1新建项目(模块)-----在src下建lib包
导入MySql jar包
mysql-connector-java-8.0.22.jar
在这里插入图片描述导入MyBatis jar包
mybatis-3.5.6.jar和lib文件中的所有jar包
在这里插入图片描述在这里插入图片描述

1.2导入包之后右击项目中的lib包—Add As Library
2.准备核心配置文件
2.1在src下新建file
mybatis.xml 核心配置文件
【mybatis帮助文档】
链接:https://pan.baidu.com/s/1jX7Q2gYQjbaiMm6yqLK3Jw
提取码:71gp
打开帮助文档----入门-----找到【从 XML 中构建 SqlSessionFactory】
复制以下代码到创建好的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>
  <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="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

db.properties 数据库配置文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/要访问的数据库名?useSSL=false&useUnicode=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=自己访问数据库的用户名
password=密码

log4j.properties == 日志的配置文件(复制即可)==

log4j.rootLogger=debug,stdout
log4j.logger.com.gsau.mapper=debug
### 把日志信息输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
### 把日志信息输出到文件:jbit.log ###
#log4j.appender.logfile=org.apache.log4j.FileAppender
#log4j.appender.logfile.File=jbit.log
#log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
#log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

2.2对mybatis.xml文件进行配置
mybatis配置细节:
1)在mybatis中可以配置哪些东西?
在这里插入图片描述2)properties属性
外部属性配置文件存储数据库连接信息
外部属性配置文件 db.properties
引用外部属性配置文件

//resource 数据源
<properties resource="db.properties"></properties>

3)settings设置
日志设置

	<settings>
        <setting name="logImpl " value=" LOG4J "/>
    </settings>

4)类型别名
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。

<typeAliases>
        <!--指定一个包名 例如com.gsau.pojo,MyBatis会在包名下面搜索需要的Java Bean
        其类型别名为首字母小写的类名
        -->
        <package name="com.gsau.pojo"/>
    </typeAliases>

5)environments环境配置
默认使用的环境ID:environments default=“development”
每个environment元素定义的环境ID:environment id=“development”
必须保证默认的环境ID要匹配其中一个环境ID
事务管理器的配置:transactionManager type=“JDBC”
在mybatis中有两种类型的事务管理器 JDBC和MANAGED
数据源的配置:dataSource type=“POOLED”
POOLED:采用连接池,这种数据源比较常用
6)映射文件的配置

<mappers>
        <!--使用相对于类路径的资源引用-->
        <mapper resource="com/gsau/mapper/EmpMapper.xml"/>
        <!--通过包扫描的形式加载所有的接口和映射文件
        通常在基于mapper代理模式下使用
		-->
        <package name="com.gsau.mapper"/>
    </mappers>

使用完全限定资源定位符(url)和使用映射接口实现类的完全限定类名(class)引用方式参考帮助文档
整体配置(参考):

<?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">
  //可直接修改value值配置数据库信息,但一般会引入db.properties外部属性文件
<configuration>
	<!--引入外部属性文件-->
    <properties resource="db.properties">
    </properties>
    <!--日志配置-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <typeAliases>
        <package name="com.gsau.pojo"/>
    </typeAliases>
    <!--配置数据库连接信息-->
  <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="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

在mybatis.xml配置文件中的各标签在mybatis帮助文档中即可查阅
3.准备Mapper映射文件
在项目的src下新建包mapper-----创建mapper映射文件
例如:EmpMapper.xml
mybatis帮助文档-----入门—探究已映射的 SQL 语句
复制以下代码到创建的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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog" parameterType="int">
    select * from Blog where id = #{id}
  </select>
</mapper>

其中,id属性:调用此sql语句的标识
resultType:sql语句的返回值类型
parameterType:参数类型 可省略

4.测试代码
新建util工具包,抽取SqlSession工具类,在测试代码中调用工具类即可

public class SqlSessionUtil {
    private static SqlSessionFactory build =null;
    static{
        //获取SqlSession
        //用于SqlSessionFactory创建工具
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        //SqlSessionFactory sql语句会话对象工厂,用于生产SqlSession对象
        //build方法需要传入一个输入流作为参数,输入流指向我们的mybatis核心配置文件
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        build = sqlSessionFactoryBuilder.build(resourceAsStream);
    }
    //autoCommit  事务是否自动提交
    public static SqlSession getSqlSession(boolean autoCommit) {
        //sql语句会话对象,专门用于执行sql语句返回结果
        SqlSession sqlSession = build.openSession(autoCommit);
        return sqlSession;
    }
}

三、Mybatis基于传统DAO模式的开发
参数传递的方式有三种:
在sql语句中可以使用两个符号接受参数:
①${} 代表mybatis底层使用Statement语句对象处理参数,参数采用字符串拼接的方式放入sql语句中
②#{} 代表mybatis底层使用PreparedStatement预编译语句对象处理参数,参数采用?作为占位符,sql语句进行预处理后执行
建议使用#{}
1)单个基本数据类型作为参数
#{}中可以随便写

	<!--单个基本数据类型作为参数-->
    <select id="selectEmpByEmpno" resultType="emp">
        select * from emp where empno = #{empno}
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        Emp emp = sqlSession.selectOne("selectOne",7788);
        System.out.println(emp);
    }

2)多个基本数据类型的Map集合作为参数
#{}中放的是Map集合中的键
parameterType=“map” 因为Map集合的别名为map
在帮助文档的类型别名中可查看常用数据类型的别名

<select id="selectEmpByDeptnoAndSal" resultType="emp" parameterType="map">
        select * from emp where deptno=#{deptno} and sal>#{sal}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        Map<String, Integer> map=new HashMap<>();
        map.put("deptno",10);
        map.put("sal",1500);
        List<Emp> emps = sqlSession.selectList("selectEmpByDeptnoAndSal",map);
        for(Emp emp:emps){
            System.out.println(emp);
        }
    }

3)单个引用类型作为参数
#{}中应该写参数的属性名

<select id="selectEmpByDeptnoAndSal2" resultType="emp" parameterType="emp">
        select * from emp where deptno=#{deptno} and sal>#{sal}
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        Emp emp=new Emp();
        emp.setDeptno(10);
        emp.setSal(1500.0);
        List<Emp> emps = sqlSession.selectList("selectEmpByDeptnoAndSal",emp);
        for(Emp e:emps){
            System.out.println(e);
        }
    }

查询方式有三种:
1)查询单个对象

<select id="selectOne" resultType="emp">
        select * from emp where empno = #{empno}
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        Emp emp=sqlSession.selectOne("selectOne",7788);
        System.out.println(emp);
    }

2)查询多个对象的List集合
resultType中写的是集合中每个元素的数据类型

	<select id="selectAll" resultType="emp">
        select * from emp
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        List<Emp> emps=sqlSession.selectList("selectAll");
        for(Emp e:emps) {
            System.out.println(e);
        }
    }

3)查询多个对象的Map集合

<select id="selectEmpMap" resultType="map">
        select * from emp
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        //第一个参数:SQL语句的id  第二个参数:map集合中键的名称
        Map<Integer, Emp> map = sqlSession.selectMap("selectEmpMap","empno");
        Set<Integer> empnos = map.keySet();
        for(Integer empno:empnos) {
            System.out.println(empno+":"+map.get(empno));
        }
    }

四、基于mapper接口代理模式实现查询
三个相同:
①接口和映射文件名相同
②namespace中的内容必须是接口的全限定名
③sql语句的id必须和接口的方法名相同
1)准备接口:

 List<Emp> findEmp();

2)准备mapper映射文件

<mapper namespace="com.gsau.mapper.EmpMapper">
    <select id="findEmp" resultType="emp">
        select * from emp
    </select>
</mapper>

3)在mybatis配置文件中使用包扫描形式加载所有的接口和映射文件

<mappers>
        <!--使用包扫描的形式进行资源引用-->
        <package name="com.gsau.mapper"/>
    </mappers>

4)测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        //通过getMapper方法获得接口下的实现类
        //getMapper方法中的参数:通过反射获得的接口的字节码文件
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Emp> emps = mapper.findEmp();
        for(Emp emp:emps){
            System.out.println(emp);
        }
    }

五、基于mapper接口代理模式下参数传递
多参数传递
1)单个基本数据类型
接口

//根据员工编号查询员工信息
Emp findEmpByEmpno(int empno);

映射文件

	<select id="findEmpByEmpno" resultType="emp">
        select * from emp where Empno=#{empno}
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.findEmpByEmpno(7788);
        System.out.println(emp);
    }

2)多个基本数据类型作为参数
接口

//根据部门编号和工资查询员工信息
List<Emp> findEmpByDeptnoAndSal(int deptno,double sal);

List<Emp> findEmpByDeptnoAndSal(@Param("a") int deptno,@Param("b") double sal);

映射文件
①#{}中可以写arg0、arg1…arg* 参数的索引,从0开始
②#{}中可以写param1、param2…param
* 参数的位置编号,从1开始
③#{}接口上使用@param注解为参数起别名

 <select id="findEmpByDeptnoAndSal" resultType="emp">
        select * from emp where deptno=#{arg0} and sal>#{arg1}
    </select>
<select id="findEmpByDeptnoAndSal" resultType="emp">
        select * from emp where deptno=#{a} and sal>#{b}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Emp> emps = mapper.findEmpByDeptnoAndSal(10, 1500.0);
        for(Emp emp:emps){
            System.out.println(emp);
        }
    }

3)单个引用类型作为参数
接口

//根据部门编号和工资查询员工信息
   List<Emp> findEmpByDeptnoAndSal2(Emp emp);

映射文件
#{}中放的是对象的属性名

	<select id="findEmpByDeptnoAndSal2" resultType="emp">
        select * from emp where deptno=#{deptno} and sal>#{sal}
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp =new Emp();
        emp.setDeptno(10);
        emp.setSal(1500.0);
        List<Emp> emps = mapper.findEmpByDeptnoAndSal2(emp);
        for(Emp e:emps){
            System.out.println(e);
        }
    }

4)多个引用类型作为参数
接口

//根据部门编号和工资查询员工信息
   List<Emp> findEmpByDeptnoAndSal3(Emp empDeptno,Emp empSal);

映射文件
①#{} 中可以写arg*.对象属性名
②#{}中可以写param*.对象属性名
③#{}接口上可以为参数使用@param注解为参数起别名 别名.对象属性名

 	<select id="findEmpByDeptnoAndSal3" resultType="emp">
        select * from emp where deptno=#{param1.deptno} and sal>#{param2.sal}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp empDeptno =new Emp();
        empDeptno.setDeptno(10);
        Emp empSal=new Emp();
        empSal.setSal(1500.0);
        List<Emp> emps = mapper.findEmpByDeptnoAndSal3(empDeptno,empSal);
        for(Emp e:emps){
            System.out.println(e);
        }
    }

模糊查询
接口

//根据员工姓名查询员工信息
   List<Emp> findEmpByEname(String ename);

映射文件

	<select id="findEmpByEname" resultType="emp">
        select * from emp where ename like concat("%",#{ename},"%")
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Emp> emps = mapper.findEmpByEname("a");
        for(Emp e:emps){
            System.out.println(e);
        }
    }

主键自动递增回填
映射文件
useGeneratedKeys 返回数据库帮我们生成的主键
keyProperty 生成的主键用我们对象的那个属性存储

<insert id="addDept" parameterType="dept" useGeneratedKeys="true" keyProperty="deptno">
        insert into dept values (null,#{dname},#{loc})
    </insert>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept =new Dept();
        dept.setDname("hah");
        dept.setLoc("ee");
        int row = mapper.addDept(dept);
        System.out.println(row);
        System.out.println(dept.getDeptno());
    }

mybatis基于传统DAO模式和基于mapper接口代理模式开发对比
在传统的DAO模式下,项目的DAO要有接口和实现类
在DAO的实现类中,每个方法中调用selectOne、selectList、selectMap、insert、update、delete等方法实现对数据的增删改查
特征:
①有接口和实现类
②使用sqlsession对象的API进行CRUD略微繁琐
③sqlsession并没有遵循我们自己定义的接口,sqlsession的API并不完全符合我们的业务逻辑
④参数的传递方式比较单一,因为参数要符合sqlsession的API
在基于mapper接口代理模式下,项目的DAO只有接口的代理文件
由mybatis结合配置文件给接口生成实现类对象,我们直接使用实现类对象即可
特征:
①有接口和映射文件,但是不需要我们自己实现接口,由mybatis生成
②使用的我们自己定义的接口的API,更符合我们自己的业务逻辑
③参数列表完全由我们自己定义,参数传递多样化
理解mapper代理
什么是代理模式?
java代理模式:23种java常用设计模式之一
代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问
静态代理:在不修改被代理对象代码的情况下,实现功能的增强和功能的增加,让功能的增加和撤销不影响原有的代码,可以极大地降低代码的耦合度
动态代理
六、动态SQL
1.if标签和where标签
if标签:简单的条件判断,一般用于多值结合查询,可以动态的选择查询条件
where标签:会自动去掉第一个if标签中的and
接口

List<Emp> findEmpByCondition(Emp emp);

映射文件

<select id="findEmpByCondition" resultType="emp">
        select * from emp
        <where>
            <if test="empno!=null and empno!=0">
                and empno=#{empno}
            </if>
            <if test="ename!=null and ename!=''">
                and ename=#{ename}
            </if>
            <if test="job!=null and job!=''">
                and job=#{job}
            </if>
            <if test="mgr!=null and mgr!=0">
                and mgr=#{mgr}
            </if>
            <if test="hiredate!=null and hiredate!=''">
                and hiredate=#{hiredate}
            </if>
            <if test="sal!=null">
                and sal=#{sal}
            </if>
            <if test="comm!=null">
                and comm=#{comm}
            </if>
            <if test="deptno!=null and deptno!=0" >
                and deptno=#{deptno}
            </if>
        </where>
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp =new Emp();
        emp.setDeptno(10);
        emp.setEname("king");
        List<Emp> emps = mapper.findEmpByCondition(emp);
        for(Emp e:emps){
            System.out.println(e);
        }
    }

2.choose标签
多条件任选其一,相当于switch语句,通常与when搭配使用
接口

List<Emp> findEmpByCondition(Emp emp);

映射文件

<select id="findEmpByCondition" resultType="emp">
        select * from emp
        <where>
        <choose>
            <when test="empno!=null and empno!=0">
                and empno=#{empno}
            </when>
            <when test="ename!=null and ename!=''">
                and ename=#{ename}
            </when>
            <when test="job!=null and job!=''">
                and job=#{job}
            </when>
            <when test="mgr!=null and mgr!=0">
                and mgr=#{mgr}
            </when>
            <when test="hiredate!=null and hiredate!=''">
                and hiredate=#{hiredate}
            </when>
            <when test="sal!=null">
                and sal=#{sal}
            </when>
            <when test="comm!=null">
                and comm=#{comm}
            </when>
            <when test="deptno!=null and deptno!=0" >
                and deptno=#{deptno}
            </when>
        </choose>
        </where>
    </select>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp =new Emp();
        emp.setDeptno(10);
        emp.setEname("king");
        List<Emp> emps = mapper.findEmpByCondition(emp);
        for(Emp e:emps){
            System.out.println(e);
        }
    }

3.set标签
自动去掉最后一个if标签中的逗号
接口

int updateEmpByCondition(Emp emp);

映射文件

<update id="updateEmpByCondition">
        update emp
        <set>
            <if test="ename!=null and ename!=''">
                ename=#{ename},
            </if>
            <if test="job!=null and job!=''">
                job=#{job},
            </if>
            <if test="mgr!=null and mgr!=0">
                mgr=#{mgr},
            </if>
            <if test="hiredate!=null and hiredate!=''">
                hiredate=#{hiredate},
            </if>
            <if test="sal!=null">
                sal=#{sal},
            </if>
            <if test="comm!=null">
                comm=#{comm},
            </if>
            <if test="deptno!=null and deptno!=0" >
                deptno=#{deptno},
            </if>
        </set>
        where empno=#{empno}
    </update>

测试代码

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp =new Emp();
        emp.setEmpno(7788);
        emp.setSal(6000.0);
        int rows = mapper.updateEmpByCondition(emp);
        System.out.println(rows);
    }

4.trim标签
可以自定义添加前缀和后缀
Prefix 要增加什么前缀
PrefixOverride 要去除什么前缀
Suffix 要增加什么后缀
SuffixOverride 要去除什么后缀
Trim 可以替代set和where
映射文件
代替where

<select id="findEmpByCondition" resultType="emp">
        select * from emp
        <trim prefix="where" prefixOverrides="and" >
            <if test="empno!=null and empno!=0">
                and empno=#{empno}
            </if>
            <if test="ename!=null and ename!=''">
                ename=#{ename},
            </if>
            <if test="job!=null and job!=''">
                job=#{job},
            </if>
            <if test="mgr!=null and mgr!=0">
                mgr=#{mgr},
            </if>
            <if test="hiredate!=null and hiredate!=''">
                hiredate=#{hiredate},
            </if>
            <if test="sal!=null">
                sal=#{sal},
            </if>
            <if test="comm!=null">
                comm=#{comm},
            </if>
            <if test="deptno!=null and deptno!=0" >
                deptno=#{deptno},
            </if>
        </trim>
    </select>

代替set

<update id="updateEmpByCondition">
        update emp
        <trim prefix="set" suffixOverrides=",">
            <if test="ename!=null and ename!=''">
                ename=#{ename},
            </if>
            <if test="job!=null and job!=''">
                job=#{job},
            </if>
            <if test="mgr!=null and mgr!=0">
                mgr=#{mgr},
            </if>
            <if test="hiredate!=null and hiredate!=''">
                hiredate=#{hiredate},
            </if>
            <if test="sal!=null">
                sal=#{sal},
            </if>
            <if test="comm!=null">
                comm=#{comm},
            </if>
            <if test="deptno!=null and deptno!=0" >
                deptno=#{deptno},
            </if>
        </trim>
        where empno=#{empno}
    </update>

5.foreach标签
常用于sql的批量操作,如批量删除,批量添加数据等
Collection 要遍历的集合或数组
Item 本次迭代获取到的元素
Open 表示以什么开始
Separator 以什么符号作为分隔符
Close 表示以什么结束
接口

//根据多个部门编号查询多个员工信息
   List<Emp> findEmpByDeptnos(List<Integer> deptnos);
   List<Emp> findEmpByDeptnos2(Arrays...deptnos);

映射文件

 <select id="findEmpByDeptnos" resultType="emp" parameterType="map">
        select * from emp where deptno in
        <foreach collection="list" item="deptno" open="(" separator="," close=")">
            #{deptno}
        </foreach>
    </select>
    <select id="findEmpByDeptnos2" resultType="emp" parameterType="map">
        select * from emp where deptno in
        <foreach collection="array" item="deptno" open="(" separator="," close=")">
            #{deptno}
        </foreach>
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Integer> deptno=new ArrayList<>();
        deptno.add(10);
        deptno.add(20);
        List<Emp> emps = mapper.findEmpByDeptnos(deptno);
        for(Emp e:emps){
            System.out.println(e);
        }
    }
public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Integer[] deptno=new Integer[]{10,20};
        List<Emp> emps = mapper.findEmpByDeptnos2(deptno);
        for(Emp e:emps){
            System.out.println(e);
        }
    }

6.bind标签
用于解决模糊查询
接口

//根据员工姓名查询员工信息
   List<Emp> findEmpByEname(String ename);

映射文件

<select id="findEmpByEname" resultType="emp">
        <bind name="likePattern" value="'%'+ename+'%'"/>
        select * from emp where ename like #{likePattern}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Emp> emps = mapper.findEmpByEname("a");
        for(Emp e:emps){
            System.out.println(e);
        }
    }

7.sql片段
将sql语句共同的部分抽出来形成一个独立的片段,哪里需要该片段就引用该片段
可以嵌套使用

<select id="findEmpByEname" resultType="emp">
        <bind name="likePattern" value="'%'+ename+'%'"/>
        <include refid="baseSelect"></include>
        emp where ename like #{likePattern}
    </select>
    
    <sql id="baseSelect">
        select
        <include refid="empColumns"></include>
        from
    </sql>
    <sql id="empColumns">
        empno,ename,job,mgr,hiredate,sal,comm,deptno
    </sql>

七、缓存
是一种临时存储少量数据至内存或磁盘的一种技术,可以降低工作量,提高程序的响应速度,MyBatis的缓存将相同查询条件的SQL执行一遍后所得到的结果存在内存或者某种缓冲介质中,当下次遇到一模一样的查询时不再执行SQL与数据库的交互,直接中缓存中获取结果
MyBatis缓存处理机制
MyBatis分为一级缓存和二级缓存
一级缓存基于sqlSession,默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。一级缓存的作用域是SqlSession范围的,当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
需要注意的是,如果SqlSession执行了DML操作(增删改),并且提交到数据库(commit方法会刷新一级缓存),MyBatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了。关闭一级缓存后,再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常。
二级缓存是mapper级别的缓存,默认关闭。使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMap进行数据存储。相比一级缓存SqlSession,二级缓存的范围更大,多个Sqlsession可以共用二级缓存,二级缓存是SqlSession的。二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。
如何开启二级缓存?
1)全局开关:在mybatis.xml文件中的settings标签配置开启二级缓存

<settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

2)分开关:在要开启二级缓存的mapper文件中开启缓存

<mapper namespace="com.gsau.mapper.EmpMapper">
    <cache/>

3)缓存中javabean对象必须实现序列化接口,因为二级缓存未必完全使用内存,有可能占用磁盘文件
在commit()或close()的时候将数据放入二级缓存

 public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(false);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp =new Emp();
        emp.setEmpno(7788);
        List<Emp> emps = mapper.findEmpByCondition(emp);
        for(Emp e:emps){
            System.out.println(e);
        }
        
        sqlSession.commit();
        
        SqlSession sqlSession1 = SqlSessionUtil.getSqlSession(false);
        EmpMapper mapper1 = sqlSession1.getMapper(EmpMapper.class);
        Emp emp1 =new Emp();
        emp.setEmpno(7788);
        List<Emp> emps1 = mapper1.findEmpByCondition(emp);
        for(Emp e:emps1){
            System.out.println(e);
        }
    }

4)如果在加入Cache元素的前提下让个别select语句不使用缓存,可以通过参数来设置
UseCache控制当前SQL语句是否启用缓存
FlushCache控制当前sql语句执行一次后是否刷新缓存
八、MyBatis实现多表查询
1.关联查询
1.1手动处理映射关系
手动处理数据库查询字段和封装实体类属性之间的映射关系
主键一般使用id属性
当属姓名和查询出的数据库字段名相同,可以不写映射关系

<resultMap id="empJoinDept" type="emp">
        <id property="empno" column="empno"></id>
        <result property="ename" column="ename"></result>
        <result property="job" column="job"></result>
        <result property="mgr" column="mgr"></result>
        <result property="hiredate" column="hiredate"></result>
        <result property="sal" column="sal"></result>
        <result property="comm" column="comm"></result>
        <result property="deptno" column="deptno"></result>
    </resultMap>

    <select id="findEmpJoinDept" resultMap="empJoinDept">
        select * from emp e left outer join dept d on e.deptno=d.deptno where empno=#{empno}
    </select>

1.2一对一关系映射处理
根据员工编号查询员工信息和对应的部门信息
接口

Emp findEmpJoinDept(Emp emp);

实体类

public class Emp implements Serializable {
    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;

映射关系

<resultMap id="empJoinDept" type="emp">
        <!--处理原有的八个属性-->
        <id property="empno" column="empno"></id>
        <result property="ename" column="ename"></result>
        <result property="job" column="job"></result>
        <result property="mgr" column="mgr"></result>
        <result property="hiredate" column="hiredate"></result>
        <result property="sal" column="sal"></result>
        <result property="comm" column="comm"></result>
        <result property="deptno" column="deptno"></result>
        <!--将Dept对象的属性映射进Emp对象的dept属性
        association    处理一对一关系映射的标签
        property       要给那个属性赋值
        javaType       要映射的属性是哪个类
        -->
        <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 empno=#{empno}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(false);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp=new Emp();
        emp.setEmpno(7839);
        Emp e = mapper.findEmpJoinDept(emp);
        System.out.println(e);
        System.out.println(e.getDept());
    }

1.3一对多关系映射处理
根据部门编号查询该部门所有的员工信息
接口

Dept findEmpsAndDeptByDeptno(Dept dept);

实体类

public class Dept implements Serializable {
    private Integer deptno;
    private String dname;
    private String loc;
    //在Dept实体类中加入Emp对象
    private List<Emp> emp;

映射关系

<resultMap id="deptJoinEmp" type="dept">
        <!--处理原来的三个属性-->
        <id property="deptno" column="deptno"></id>
        <result property="dname" column="dname"></result>
        <result property="loc" column="loc"></result>
        <!--将Emp对象的属性映射进Dept对象的emp属性
        collection    处理一对多关系映射的标签
        property      要给那个属性赋值
        ofType        要映射的属性是哪个类
        -->
        <collection property="emp" ofType="emp">
            <id property="empno" column="empno"></id>
            <result property="ename" column="ename"></result>
            <result property="job" column="job"></result>
            <result property="mgr" column="mgr"></result>
            <result property="hiredate" column="hiredate"></result>
            <result property="sal" column="sal"></result>
            <result property="comm" column="comm"></result>
            <result property="deptno" column="deptno"></result>
        </collection>
    </resultMap>

测完代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(false);
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept=new Dept();
        dept.setDeptno(10);
        Dept dept1 = mapper.findEmpsAndDeptByDeptno(dept);
        System.out.println(dept1);
        List<Emp> emps = dept1.getEmp();
        for(Emp emp:emps){
            System.out.println(emp);
        }
    }

1.4多对多关系映射处理
一个员工参与过哪些项目?
接口

Emp findEmpAndProject(int empno);

实体类

public class Emp implements Serializable {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;
	//在Emp实体类中加入ProjectRecords对象
    private List<ProjectRecords> projectRecords;
public class ProjectRecords {
    private Integer empno;
    private Integer pid;
	//在ProjectRecords实体类中加入Projects对象
    private Projects projects;

映射关系

<resultMap id="empJoinprojrct" type="emp">
        <id property="empno" column="empno"></id>
        <result property="ename" column="ename"></result>
        <result property="job" column="job"></result>
        <result property="mgr" column="mgr"></result>
        <result property="hiredate" column="hiredate"></result>
        <result property="sal" column="sal"></result>
        <result property="comm" column="comm"></result>
        <result property="deptno" column="deptno"></result>
        <collection property="projectRecords" 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="findEmpAndProject" resultMap="empJoinprojrct">
        select * from emp e left outer join projectRecords ps on e.empno=ps.empno
        left outer join projects p on ps.pid=p.pid
        where e.empno=#{empno}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(false);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.findEmpAndProject(7788);
        System.out.println(emp);
        List<ProjectRecords> projectRecords = emp.getProjectRecords();
        for(ProjectRecords records:projectRecords){
            System.out.println(records);
            Projects projects = records.getProjects();
            System.out.println(projects);
        }
    }

一个项目被那些员工参与过?
接口

Projects findProjectsAndEmps(int pid);

实体类

public class Projects implements Serializable {
    private Integer pid;
    private String pname;
    private Integer money;
    //在Projects实体类中加入ProjectRecords对象
    List<ProjectRecords> records;
public class ProjectRecords implements Serializable {
    private Integer empno;
    private Integer pid;
    //在ProjectRecords实体类中加入Emp对象
    private Emp emp;

映射关系

<resultMap id="projectJoinEmp" type="projects">
        <id property="pid" column="pid"></id>
        <result property="pname" column="pname"></result>
        <result property="money" column="money"></result>
        <collection property="records" ofType="projectRecords">
            <id property="empno" column="empno"></id>
            <id property="pid" column="pid"></id>
            <association property="emp" javaType="emp">
                <id property="empno" column="empno"></id>
                <result property="ename" column="ename"></result>
                <result property="job" column="job"></result>
                <result property="mgr" column="mgr"></result>
                <result property="hiredate" column="hiredate"></result>
                <result property="sal" column="sal"></result>
                <result property="comm" column="comm"></result>
                <result property="deptno" column="deptno"></result>
            </association>
        </collection>
    </resultMap>
    <select id="findProjectsAndEmps" resultMap="projectJoinEmp">
        select * from projects p left outer join projectRecords ps on p.pid=ps.pid
        left outer join emp e on e.empno=ps.empno
        where p.pid=#{pid}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(false);
        ProjectsMapper mapper = sqlSession.getMapper(ProjectsMapper.class);
        Projects projects = mapper.findProjectsAndEmps(2);
        System.out.println(projects);
        List<ProjectRecords> projectsRecords = projects.getRecords();
        for(ProjectRecords records:projectsRecords){
            System.out.println(records);
            Emp emp = records.getEmp();
            System.out.println(emp);
        }
    }

2.级联查询
将一个多表关联查询转换为多个单表查询
接口
EmpMapper中接口

Dept findEmpsBydeptno(int deptno);

DeptMapper中接口

Dept findEmpsBydeptno(int deptno);

实体类

public class Dept implements Serializable {
    private Integer deptno;
    private String dname;
    private String loc;
    //在Dept实体类中加入Emp对象
    private List<Emp> emp;

映射文件
EmpMapper中映射文件

	<select id="findEmpByDeptno" resultType="emp">
        select * from emp where deptno=#{deptno}
    </select>

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="emp"
                ofType="emp"
                column="deptno"
                select="com.gsau.mapper.EmpMapper.findEmpByDeptno"
        />
    </resultMap>
    <select id="findEmpsBydeptno" resultMap="deptJoinEmp">
        select * from dept where deptno=#{deptno}
    </select>

测试代码

public static void main(String[] args) throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession(false);
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept1 = mapper.findEmpsBydeptno(10);
        System.out.println(dept1);
        List<Emp> emps = dept1.getEmp();
        for(Emp emp:emps){
            System.out.println(emp);
        }
    }

resultMap的常见属性

属性描述
property需要映射到javabean的属性名称
javaTypeproperty的类型,一个完整的类名或一个类型别名
column数据库表的列名或列别名
jdbcTypecolumn在数据库表中的类型,这个属性只在insert、update、delete的时候针对允许空的列有用,JDBC需要这项,mybatis不需要
fetchType自动延迟加载
selectassociation和collection的属性,使用哪个查询查询属性的值,要求指定namespace+id的全名称
ofTypecollection的属性,指明集合中元素类型(即泛型类型)

3.积极加载和延迟加载
3.1积极加载
即使我们不需要第二条SQL语句也会加载出来
在这里插入图片描述在这里插入图片描述3.2延迟加载设置
1)全局开关
在mybatis.xml中打开延迟加载的开关。配置完成后所有的association和collection都生效
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
aggressiveLazyLoading :开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载
具体查看mybatis帮助文档

<settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

2)分开关:指定的association和collection元素配置fetchType属性
将覆盖全局开关中的配置
enger:表示积极加载

fetchType="eager"

lazy:表示延迟加载

fetchType="lazy"

延迟加载的优点:延迟加载可以根据用户的需要动态的决定查询数据的时机,可以将查询的工作在时间上分散,减少单位时间内对于数据库的查询次数,进而减少数据库单位时间内的运算压力,提高数据库的响应速度
级联查询的优点:级联查询将一个多表关联查询转换为多个单表查询,sql语句变得简单了,单次单表查询比单次多表查询效率更高
级联查询的缺点:多表关联查询转换为级联查询,由原来的一次查询转换为多次查询,查询次数增加,工作量是增加的,延迟加载可以简单理解为是级联查询的一种优化策略

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值