MyBatis

MyBatis

MyBatis简介

MaBatis特性

1)是支持定制化SQL,存储过程以及高级映射的优秀的持久层框架

2)避免了几乎所有的JDBC代码和手动设置参数以及获取结果集

3)可以使用简单的xml或注解用于配置和原始映射,将接口和java的POJO映射成数据库中的记录

4)是一个半自动的ORM框架

MaBatis下载

https://github.com/mybatis/mybatis-3

下载导入maven依赖即可,这里主要下载官方文档

和其他持久化层技术对比

JDBC

  • SQL夹杂在Java代码中耦合度高,导致硬编码内伤
  • 维护不易且SQL有变化,需要频繁修改
  • 代码冗长,开发效率低

Hibernate和JPA

  • 操作简便,开发效率高
  • 程序中长难复杂SQL需要绕过框架
  • 内部自动生产的SQL,不容易做特殊优化
  • 基于全映射的全自动框架,大量字段POJO进行映射时比较困难
  • 反射操作太多,导致数据库性能下降

MyBatis

  • 轻量级,性能出色
  • SQL和java编码分开,功能边界清晰了,java代码专注业务,SQL语句专注数据
  • 开发效率稍逊于HIbernate,但是完全能够接受

搭建MyBatis

开发环境

maven3.8.8

mysql8.0

mybatis3.5.14

创建maven工程

打包方式:jar

引入依赖

 <!-- Mybatis核心 -->
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.14</version>
      </dependency>
      <!-- junit测试 -->
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
      </dependency>
      <!-- MySQL驱动 -->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
      </dependency>

创建MyBatis核心配置文件

主要用于配置连接数据库的环境以及Mybatis的全局配置信息,在src/main/resources目录下,习惯将其命名为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>
    
    <!--    引入文件-->
        <!--    引入文件后下面数据库相关设置可以这样写 <property name="driver" value="${jdbc.driver}"/>-->       
    <properties resource="jdbc.properties"></properties>
    <!--   标签顺序:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?-->
    <!--给全类名设置别名-->
    <typeAliases>
<!-- 给type设置别名为alias的值-->
        <typeAlias type="com.zhe.pojo.User" alias="User"></typeAlias>
    </typeAliases>
<!--    配置连接数据库的环境-->
    <!--  environments:配置多个数据库的环境
          属性:default:设置默认使用的环境id
     -->
    <environments default="development">
       <!-- environment:配置某个具体的环境
               属性:id:表示连接数据库的环境的唯一标识,不能重复
        -->
        <environment id="development">

         <!-- 
                transactionManager:设置事务管理方式
                属性:
                type:设置事务管理方式,type="JDBC|MANAGED"
                type="JDBC":设置当前环境的事务管理都必须手动处理
                type="MANAGED":设置事务被管理,例如spring中的AOP
           -->
            <transactionManager type="JDBC"/>
            <!--
               dataSource:设置数据源
                属性:
                type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
                type="POOLED":使用数据库连接池缓存数据库连接,下次使用可以从缓存中直接获取,不需要重新创建
                type="UNPOOLED":不使用数据库连接池,即每次使用连接都需要重新创建
                type="JNDI":调用上下文中的数据源
            -->
            <dataSource type="POOLED">
                 <!-- 设置连接数据库的驱动-->
                <!--  mysql8需要加上cj-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                 <!-- 设置连接数据库的连接地址-->
                <!--   mysql8需要在最后配置时区-->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
                <!-- 设置连接数据库的连接用户名-->
                <property name="username" value="root"/>
                <!-- 设置连接数据库的连接密码-->             
                <property name="password" value="dyz200472"/>
            </dataSource>
        </environment>
    </environments>
<!--    引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>

<typeAliases>标签,设置类型的别名

标签顺序

properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

两个子标签

1)<typeAlias>,type属性写全类名,alias属性如果设置别名,不设置alias属性,该类型拥有默认别名,即类名不区分大小写

2)<package>标签,name属性写包的路径,表示以包为单位,将包下所有的类型设置类型别名,即类名不区分大小写

设置数据库环境第二种方式

1)将这些驱动等写到jdbc.properties文件中,文件放在resources目录下

文件内容,写jdbc.driver是为了以后区分文件方便

在这里插入图片描述

2)在配置文件中引入jdbc.properties

 <!--    引入文件-->
    <properties resource="jdbc.properties"></properties>

3)修改配置代码

    <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>

<mappers>标签引入映射文件

方式一:一个一个进行引入

    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>

方式二:以包为单位

    <mappers>
<!--
        以包为单位引入映射文件
        注意:在resources下创建包要用/分隔而不是.分隔
        要求
        1.mapper接口所在的包要和映射文件所在的包一致
        2.mapper接口要和映射文件名字一致
-->
        <package name="com.zhe.mapper"/>
    </mappers>

创建Mapper接口

//有几张表就有几个接口
public interface UserMapper {

    int insertUser();
}

创建MyBatis映射文件

ORM:对象关系映射

  • 对象:Java的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的关系

MaBatis映射文件

1)每一张表对应一个实体类对应一个映射文件,映射文件命名规则:表所对应的实体类的类名+Mapper.xml

2)用于编写sql

3)存放位置:src/main/resources/mappers目录

<?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和mapper接口全类名保持一致-->
<mapper namespace="com.zhe.mapper.UserMapper">

<!--映射文件中sql语句的id与mapper接口中方法名保持一致-->
    <insert id="insertUser">
        insert into t_user values(null,'admin','12345',23,'男','12345@qq.com')
    </insert>
</mapper>

编写测试代码

public class MaBatisTest {

 @Test
 public void testMabatis() throws IOException {
  //加载核心配置文件,以字节输入流方式获取
  InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
  //获取SqlSessionFactoryBuilder
  SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
  //获取SqlSessionFactory
  SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
  //获取sqlSession
  SqlSession sqlSession = build.openSession(true);//默认不自动提交,若需要自动提交事务,增加参数true
  //获取mapper接口对象 获取mapper接口底层是代理模式
  UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  //测试,返回值为受影响的行数
  int i = mapper.insertUser();
  //提交事务
  //sqlSession.commit();
  System.out.println(i);
 }
}

SqlSession:代表java程序和数据库之间的会话

SqlSessionFactory:是生产SqlSession的工厂

加入log4j日志功能

加入依赖

<!-- log4j日志 -->
 <dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.2.17</version>
 </dependency>

加入log4j配置文件log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--信息爆红没关系hh-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}
%m  (%F:%L) \n" />
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug" />
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    </logger>
    <root>
        <level value="debug" />
        <appender-ref ref="STDOUT" />
    </root>
</log4j:configuration>

日志的级别

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)

级别由高到低,打印内容越来越详细

项目结构

在这里插入图片描述

MyBatis增删改查

实现增删改查的基本步骤

mapper接口中写抽象方法->MaBatis映射文件中编写sql->测试类中进行测试

增删改查,重点在于MaBatis映射文件,下面都是映射文件中的代码

<!--int insertUser();-->
 <insert id="insertUser">
    insert into t_user values(null,'admin','123456',23,'男')
 </insert>

 <!--int deleteUser();-->
 <delete id="deleteUser">
    delete from t_user where id = 7
 </delete>

<!--int updateUser();-->
 <update id="updateUser">
 update t_user set username='ybc',password='123' where id = 6
 </update>

查询实体类对象

<!--    User getUserById();  -->
    <select id="getUserById" resultType="com.zhe.pojo.User">
        select * from t_user where id = 3
    </select>

查询集合

<!--        List<User> getAllUser(); 注释里都是对应的mapper接口的抽象方法-->
    <select id="getAllUser" resultType="User">
        select * from t_user
    </select>

注意

1)查询功能必须设置resultType或resultType,表示查询结果返回值类型
resultType 自动映射 字段名和属性名一致的情况使用
resultType 自定义映射 字段名和属性名不一致或者多对一,一对多的情况

(也可以不设置,我没有设置没报错,应该是版本更新之后不需要设置,默认为自动映射)

2)查询数据为多条时,方法不能使用实体类作为返回值,只能使用集合

MyBatis获取参数的两种方式(重点)

${}

本质是字符串拼接,使用时需要手动添加单引号

elect * from t_user where username = '${username}'

#{}

本质是占位符赋值,不需要添加单引号

  select * from t_user where username = #{username}

MyBatis获取参数值的各种情况(重点)

单个字面量类型的参数

若mapper接口中的方法参数为单个的字面量类型,此时可以使用${}和#{}以任意的名称获取参数的值

多个字面量类型的参数

此时MaBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值;以param1,param2…为键,以参数为值;因此只需通过${}和#{}访问map集合的键就可以获取对应的值

map集合类型的参数

手动创建map集合,只需要通过${}和#{}直接访问map集合的键就可以获取对应的值

实体类型的参数

参数为实体类对象,通过${}和#{}访问实体类对象中的属性名获取属性值

使用@Param标识参数

使用@Param注解标识mapper接口中的方法参数

 //命名参数
    User checkLoginByParam(@Param("username") String username,@Param("password") String password);

此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以param1,param2为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取对应的值

总结

整合为实体类对象类型map集合参数和**@Param**两种情况即可

前两种均可以使用@Param方式获取参数对应的值

MyBatis的各种查询功能

查询一个实体类对象

mapper接口中的方法

 User getUserById(@Param("id") int id);

映射文件代码

 <!--User getUserById(@Param("id") int id);-->
 <select id="getUserById" resultType="User">
 select * from t_user where id = #{id}
 </select>

查询一个list集合

mapper接口中的方法

 List<User> getUserList();

映射文件代码

 <!--List<User> getUserList();-->
 <select id="getUserList" resultType="User">
 select * from t_user
 </select>

查询单个数据

mapper接口中的方法

 int getCount();

映射文件代码

 <!--int getCount();-->
 <select id="getCount" resultType="_integer">
 select count(id) from t_user
 </select>

在MaBatis中,对于java常用的类型都设计了类型别名

例如:java.lang.Integer–>int,integer

int–>_int,_integer

Map–>map

List–>list

查询一条数据为map集合

mapper接口中的方法

 Map<String, Object> getUserToMap(@Param("id") int id);

映射文件代码

 <!--Map<String, Object> getUserToMap(@Param("id") int id);-->
 <select id="getUserToMap" resultType="map">
 select * from t_user where id = #{id}
 </select>
 <!--结果:{password=123456, sex=男, id=1, age=23, username=admin}-->

查询多条数据为map集合

方式一:将表中数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取

mapper接口中的方法

List<Map<String, Object>> getAllUserToMap()

映射文件代码

 <!--List<Map<String, Object>> getAllUserToMap()-->
 <select id="getAllUserToMap" resultType="map">
 select * from t_user
 </select>

方式二:将表中数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的map集合

mapper接口中的方法

 @MapKey("id")
 Map<String, Object> getAllUserToMap();

映射文件代码

 <!--Map<String, Object> getAllUserToMap();-->
 <select id="getAllUserToMap" resultType="map">
 select * from t_user
 </select>
结果:
<!--
 {
 1={password=123456, sex=男, id=1, age=23, username=admin}, 
2={password=123456, sex=男, id=2, age=23, username=张三}, 
3={password=123456, sex=男, id=3, age=23, username=张三}
 }-->

特殊SQL的执行

模糊查询

 <!--List<User> testMohu(@Param("mohu") String mohu);-->
 <select id="testMohu" resultType="User">
 方式一:select * from t_user where username like '%${mohu}%'
 方式二:select * from t_user where username like concat('%',#{mohu},'%')
 方式三:select * from t_user where username like "%"#{mohu}"%"
 </select>

批量删除

<!--int deleteMore(@Param("ids") String ids);-->
 <delete id="deleteMore">
 delete from t_user where id in (${ids})
 </delete>

动态设置表名

<!--List<User> getAllUser(@Param("tableName") String tableName);-->
 <select id="getAllUser" resultType="User">
 select * from ${tableName}
 </select>

添加功能获取自增的主键

useGeneratedKeys:设置当前Sql使用自增的主键

keyProperty:将自增的主键的值赋值给传输到映射文件中参数的某个属性,这里即赋值给id

 <!--int insertUser(User user);-->
 <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
 insert into t_user values(null,#{username},#{password},#{age},#{sex})
 </insert>

自定义映射resultMap

resultMap处理字段和属性的映射关系

查询操作时,数据库中字段名与实体类中属性名不一致,查询结果为null

解决方案

1.给字段设置别名

 select eid,emp_name empName,age,sex,email from t_emp

2.设置全局配置

在MaBatis核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase,可以在查询表中数据时,自动将_类型的字段名转换为驼峰

例如:字段名user_name设置了mapUnderscoreToCamelCase,此时字段名就会转换为userName

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

3.通过restultMap设置自定义映射

 <!--
resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一的映射关系
collection:设置一对多的映射关系
属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中表中的字段名
-->  
     <resultMap id="empResultMap" type="Emp">
         <id property="eid" column="eid"></id>
         <result property="empName" column="emp_name"></result>
         <result property="age" column="age"></result>
         <result property="sex" column="sex"></result>
         <result property="email" column="email"></result>
     </resultMap>
    <select id="getAllEmp" resultMap="empResultMap">
        select * from t_emp
    </select>

多对一映射处理

查询员工信息以及员工所对应的部门信息

多个员工对应一个部门

级联方式处理映射关系

<resultMap id="empDeptMap" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <!--表示将查询结果中的 "did" 列的值映射到 Emp 对象中的 dept 对象的 did 属性。-->
        <result property="dept.did" column="did"></result>
        <result property="dept.deptName" column="dept_name"></result>
 </resultMap>
 <!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
 <select id="getEmpAndDeptByEid" resultMap="empDeptMap">
 select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
 </select>

使用association处理映射关系

    <resultMap id="empDeptMap" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
<!--
           association:处理多对一的映射关系
           property:需要处理多对的映射关系的实体类中的属性属性名
           javaType:该属性的类型
-->
        <association property="dept" javaType="Dept">
            <id property="id" column="id"></id>
            <result property="deptName" column="dept_name"></result>
        </association>
    </resultMap>

 <!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
 <select id="getEmpAndDeptByEid" resultMap="empDeptMap">
 select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}
 </select>

分步查询

1)查询员工信息

/**
 * 通过分步查询查询员工信息
 * 
 * EmpMapper接口
 */
 Emp getEmpByStep(@Param("eid") int eid);
<!-- EmpMapper.xml配置文件 -->
<resultMap id="empDeptStepMap" type="Emp">
 <id column="eid" property="eid"></id>
 <result column="ename" property="ename"></result>
 <result column="age" property="age"></result>
 <result column="sex" property="sex"></result>
 <!--
 property:将查询结果映射到dept属性
 select:设置分步查询,查询某个属性的值的sql的标识(namespace.sqlId)  就是全类名.查询部门id的方法
column:将sql以及查询结果中的某个字段设置为分步查询的条件-->
<association property="dept" 
             select="com.zhe.mapper.DeptMapper.getEmpDeptByStep" 
             column="did">
 </association>
 </resultMap>
 <!--Emp getEmpByStep(@Param("eid") int eid);-->
 <select id="getEmpByStep" resultMap="empDeptStepMap">
 select * from t_emp where eid = #{eid}
 </select>

2)根据员工所对应的部门id查询部门信息

/**
 * 分步查询的第二步:根据员工所对应的did查询部门信息
 * DeptMapper接口
 * 
 */
 Dept getEmpDeptByStep(@Param("did") int did);
<!-- DeptMapper.xml配置文件 -->
 <!--Dept getEmpDeptByStep(@Param("did") int did);-->
 <select id="getEmpDeptByStep" resultType="Dept">
 select * from t_dept where did = #{did}
 </select>

一对多映射处理

根据部门id查询部门以及部门中的员工信息

collection

/**
 * 根据部门id查询部门以及部门中的员工信息
 * 
 * 
 */
 Dept getDeptEmpByDid(@Param("did") int did);
 <resultMap id="deptEmpMap" type="Dept">
 <id property="did" column="did"></id>
 <result property="dname" column="dname"></result>
 <!--
 ofType:设置collection标签所处理的集合属性中存储数据的类型-->
 <collection property="emps" ofType="Emp">
 <id property="eid" column="eid"></id>
 <result property="ename" column="ename"></result>
 <result property="age" column="age"></result>
 <result property="sex" column="sex"></result>
 </collection>
 </resultMap>
 <!--Dept getDeptEmpByDid(@Param("did") int did);-->
 <select id="getDeptEmpByDid" resultMap="deptEmpMap">
 select * from t_dept left join t_emp on t_dept.did = 
t_emp.did where t_dept.did = #{did}
 </select>

分步查询

1)查询部门信息

/**
 * 分步查询部门和部门中的员工
 * DeptMapper接口
 * 
 */
 Dept getDeptByStep(@Param("did") int did);
 <resultMap id="deptEmpStep" type="Dept">
 <id property="did" column="did"></id>
 <result property="dname" column="dname"></result>
 <collection property="emps" fetchType="eager" 
select="com.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid" column="did">
 </collection>
 </resultMap>
 <!--Dept getDeptByStep(@Param("did") int did);-->
 <select id="getDeptByStep" resultMap="deptEmpStep">
 select * from t_dept where did = #{did}
 </select>

2)根据部门id查询部门中所有员工

/**
 * 根据部门id查询员工信息
* 
 * EmpMapper接口
 */
 List<Emp> getEmpListByDid(@Param("did") int did);
 
<!--List<Emp> getEmpListByDid(@Param("did") int did);-->
 <select id="getEmpListByDid" resultType="Emp">
 select * from t_emp where did = #{did}
 </select>

延迟加载

分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息

    <settings>
<!--        开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

lazyLoadingEnabled:延迟加载的开关,当开启时,所有的关联对象都会延迟加载

aggressiveLazyLoading:默认为false关闭,当开启时,任何方法的调用都会加载该对象的所有属性,否则每个属性会按需加载

默认属性就可以实现按需加载,此时可通过可通过association和 collection中的fetchType属性,,fetchType=“lazy(延迟加 载)|eager(立即加载)”

动态SQL

是一种根据特定条件动态拼装SQL语句的功能,它的存在是为了解决拼接SQL语句字符串时的痛点问题

if

test属性内表达式的结果为true,则标签中的内容会执行,反之标签中的内容不会执行

   <select id="getEmpByConditionOne" resultType="Emp">
        select * from t_emp where 1=1 and
        <if test="empName != null and empName != '' ">
            emp_name = #{empName}
        </if>
        <if test="age != null and age != '' ">
            and age = #{age}
        </if>
    </select>

where

where标签一般和if结合使用

1)若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字

2)若where标签中if条件满足,则where标签自动添加where关键字,并将条件前面多余的and或者or去掉

注意:无法去掉后面多余的and或者or

    <select id="getEmpByConditionTwo" resultType="Emp">
        select * from t_emp
        <where>
<!--
    where
    有内容时,自动生成where关键字,并且将内容前多余的and或or去掉
    当where标签中没有内容时,此时where标签没有任何效果

-->
            <if test="empName != null and empName != '' ">
                emp_name = #{empName}
            </if>
            <if test="age != null and age != '' ">
                and age = #{age}
            </if>
        </where>
    </select>

trim

trim标签用于去掉或者添加标签中的内容

常用属性

prefix:在trim标签的内容的前面添加某些内容

prefixOverrides:在前面去掉某些内容

suffix:在trim标签的内容的后面添加某些内容

suffixOverrides:在后面去掉某些内容

    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <trim prefix="where" suffixOverrides="and|or">
            <if test="empName != null and empName != '' ">
                emp_name = #{empName} and
            </if>
            <if test="age != null and age != '' ">
                 age = #{age}
            </if>
        </trim>
    </select>

choose,when,otherwise

1)相当于if…else,有一个when执行了其他就都不执行了
2)when至少有一个,otherwise最多只能有一个

 <select id="getEmpListByChoose" resultType="Emp">
    select <include refid="empColumns"></include> from t_emp
    <where>
        <choose>
            <when test="ename != '' and ename != null">
                ename = #{ename}
            </when>
            <when test="age != '' and age != null">
                age = #{age}
            </when>
            <when test="sex != '' and sex != null">
                sex = #{sex}
            </when>
            <when test="email != '' and email != null">
                email = #{email}
            </when>
            <!--相当于else-->
            <otherwise>
                did = 2
            </otherwise>
        </choose>
    </where>
 </select>

foreach

就是for循环了啦

属性

collection:设置要循环的数组或集合

item:表示数组或集合中的每一个数据

separator:设置循环体之间的分隔符

open:设置foreach标签中内容的开始符

close:设置foreach标签中的内容的结束符

 <!--int insertMoreEmp(List<Emp> emps);-->
 <insert id="insertMoreEmp">
    insert into t_emp values
    <foreach collection="emps" item="emp" separator=",">
        (null,#{emp.ename},#{emp.age},#{emp.sex},#{emp.email},null)
    </foreach>
 </insert>
 <!--int deleteMoreByArray(int[] eids);-->
 <delete id="deleteMoreByArray">
    delete from t_emp where
    <foreach collection="eids" item="eid" separator="or">
        eid = #{eid}
    </foreach>
     </delete>
 <!--int deleteMoreByArray(int[] eids);-->
 <delete id="deleteMoreByArray">
 delete from t_emp where eid in
 <foreach collection="eids" item="eid" separator="," open="(" close=")">
   #{eid}
 </foreach>
 </delete>

SQL片段

sql片段标签,可以记录一段公共sql片段,在使用的地方通过include标签进行引入

<sql id="empColumns">
 eid,ename,age,sex,did
 </sql>
 select <include refid="empColumns"></include> from t_emp

MyBatis的缓存

MyBatis的一级缓存

一级缓存的级别

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

一级缓存的失效情况

使一级缓存失效的四种情况:

1)不同的SqlSession对应不同的一级缓存

2)同一个SqlSession但是查询条件不同

3)同一个SqlSession两次查询期间执行了任何一次增删改操作

4)同一个SqlSession两次查询期间调用sqlSession.clearCache()手动清空了缓存

MyBatis的二级缓存

二级缓存的级别

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,下次若再执行相同的查询语句,结果就会从缓存中获取

二级缓存开启的条件

1)核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认就为true,故不需要设置

2)在映射文件中设置标签<cache/>

3)查询的数据所转换的实体类类型必须实现序列化的接口即implements Serializable

4)二级缓存必须在SqlSession关闭或提交之后有效

二级缓存失效的情况

两次查询之间进行任意的增删改,会使一级和二级缓存同时失效

二级缓存相关配置

在mapper映射文件中添加的cache标签的属性

1)eviction:缓存回收策略

LRU(默认)
FIFO

SOFT

WEAK

2)flushInterval:刷新间隔,单位毫秒

3)size:缓存最多可以存储多少个对象

4)readOnly:是否只读

true:只读缓存,效率高

false:读写缓存,效率低但是安全,默认为false

缓存查询的顺序

二级缓存->一级缓存->数据库

注意:SqlSession关闭之后会将一级缓存中的数据存到二级缓存中

整合第三方缓存EHCache(了解)

添加依赖

<!-- Mybatis EHCache整合包 -->
 <dependency>
 <groupId>org.mybatis.caches</groupId>
 <artifactId>mybatis-ehcache</artifactId>
 <version>1.2.1</version>
 </dependency>
 <!-- slf4j日志门面的一个具体实现 -->
 <dependency>
 <groupId>ch.qos.logback</groupId>
 <artifactId>logback-classic</artifactId>
 <version>1.2.3</version>
 </dependency>

各jar包功能

在这里插入图片描述

创建EHCache配置文件ehcache.xml

<?xml version="1.0" encoding="utf-8" ?>
 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
 <!-- 磁盘保存路径 -->
 <diskStore path="D:\atguigu\ehcache"/>
 <defaultCache
 maxElementsInMemory="1000"
 maxElementsOnDisk="10000000"
 eternal="false"
 overflowToDisk="true"
 timeToIdleSeconds="120"
 timeToLiveSeconds="120"
 diskExpiryThreadIntervalSeconds="120"
 memoryStoreEvictionPolicy="LRU">
 </defaultCache>
 </ehcache>

配置文件说明(了解)

在这里插入图片描述

设置二级缓存的类型

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

加入logback日志

创建logback配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
 <configuration debug="true">
 <!-- 指定日志输出的位置 -->
 <appender name="STDOUT"
 class="ch.qos.logback.core.ConsoleAppender">
 <encoder>
 <!-- 日志输出的格式 -->
 <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
 <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] 
[%msg]%n</pattern>
 </encoder>
 </appender>
 <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
 <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
 <root level="DEBUG">
 <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
 <appender-ref ref="STDOUT" />
 </root>
 <!-- 根据特殊需求指定局部日志级别 -->
 <logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
 </configuration>

MyBatis逆向工程

神魔是逆向工程

正向工程:先创建java实体类,由框架负责根据实体类生成数据库表

逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成java实体类,mapper接口,mapper映射文件

创建逆向工程的步骤

添加依赖和插件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.zhe</groupId>
  <artifactId>MaBatis04_MBG</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>MaBatis04_MBG</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>


  <dependencies>
    <!-- log4j日志 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
        <!-- 依赖MyBatis核心包 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.14</version>
    </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
      </dependency>
    <!-- 逆向工程的核心依赖 -->
              <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.3.2</version>
              </dependency>

    <!-- 数据库连接池 -->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.2</version>
    </dependency>

    <!-- MySQL驱动 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>

  </dependencies>

  <!-- 控制Maven在构建过程中相关配置 -->
  <build>
    <!-- 构建过程中用到的插件 -->
    <plugins>
      <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
      <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.0</version>

      </plugin>
    </plugins>
  </build>

</project>

创建MyBatis核心配置文件mybatis-config.xml

创建逆向工程的配置文件generatorConfig.xml(文件名不能更改)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!--爆红不用管-->
<generatorConfiguration>
    <!--
            targetRuntime: 执行生成的逆向工程的版本
                    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
                    MyBatis3: 生成带条件的CRUD(奢华尊享版)
     -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"
                        userId="root"
                        password="dyz200472">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.zhe.pojo"
                            targetProject=".\src\main\java">
<!--是否能使用子包-->
            <property name="enableSubPackages" value="true" />
<!--字段名前面有空格的话,可以自动去掉-->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.zhe.mapper"
                         targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.zhe.mapper"  targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>

执行插件generate目标

在这里插入图片描述

QBC风格的增删改查

public class MBGTest {
    @Test
    public void testMBG() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        //查询所有数据
//        List<Emp> list = mapper.selectByExample(null);
//        list.forEach(emp -> System.out.println(emp));
        //根据条件查询
        EmpExample example = new EmpExample();
        //名字为彭于晏,年龄大于等于5
        example.createCriteria().andEmpNameEqualTo("彭于晏").andAgeGreaterThanOrEqualTo(5);
        //前面的条件加上或者名字为段元哲
        example.or().andEmpNameEqualTo("段元哲");
        List<Emp> list = mapper.selectByExample(example);
        System.out.println(list);
        //普通修改,如果有的设置为null,则修改为null
        mapper.updateByPrimaryKey(new Emp(3,"admin",23,null,"123",2));
        //选择修改,如果有的设置为null,则不会修改,会维持原状
        mapper.updateByPrimaryKeySelective(new Emp(3,"admin",23,null,"123",2));
    }
}

小插曲:报错,maven死活导入不进去依赖

在复制老师配置的时候总是有依赖死活导入不进去,原来是整了两个dependencies标签,导致只有一个生效,因此以后依赖都要放在一个<dependencies>里面哦

分页插件

分页插件使用步骤

添加依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
 <dependency>
 <groupId>com.github.pagehelper</groupId>
 <artifactId>pagehelper</artifactId>
 <version>5.2.0</version>
 </dependency>

配置分页插件

在MaBatis的核心配置文件中配置插件

<plugins>
 <!--设置分页插件-->
 <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
 </plugins>

分页插件的使用

1)在查询之前开启分页

PageHelper.startPage(1,2);

pageNum:当前页的页码

pageSize:每页显示的条数

2)在查询之后获取分页相关数据

PageInfo<Emp> page = new PageInfo<>(list, 3);

list:分页之后的数据

navigatePages:导航分页的页码数

代码实现

public class PageHelperTest {
    /**
     * mysql分页操作
     * limit index,pageSize
     * index:当前页的起始索引
     * pageSize:每页显示的条数
     * pageNum:当前页的页码
     * index=(pageNum-1)* pageSize
     */

    @Test
    public void testPageHelper() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        //使用分页插件实现分页功能
        //1.在查询功能之前开启分页
        PageHelper.startPage(1,2);
        List<Emp> list = mapper.selectByExample(null);
        list.forEach(emp -> System.out.println(emp));
        //2.在查询之后获取相关的数据
        //list表示分页数据,3表示当前导航分页的数量
        PageInfo<Emp> page = new PageInfo<>(list, 3);
        System.out.println(page);
    }
}

分页相关常用数据

pageNum:当前页的页码

pageSize:每页显示的条数

size:当前页显示的真实条数

total:总记录数

pages:总页数

prePage:上一页的页码

nextPage:下一页的页码

isFirstPage/isLastPage:是否为第一页/最后一页

hasPreviousPage/hasNextPage:是否存在上一页/下一页

navigatePages:导航分页的页码数

navigatepageNums:导航分页的页码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值