MyBatis使用手册

本文全文PDF版本下载地址
https://download.csdn.net/download/qq_37561309/10973794

运行环境

IDEA 2018 + Maven + Mysql 5.1.44 + MyBatis 3.4.6

POM文件
	<dependencies>
        <!--MyBatis JAR-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!--MySQL JAR-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>
    </dependencies>
从JDBC谈起

首先我们先去准备以下数据,做一个User表,以下我们就根据user表来表述整个MyBatis的入门篇

跟着搭建环境的小伙伴可以直接复制这段sql执行

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `user`;

CREATE TABLE `user`  (
  `id` int(8) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(4) NULL DEFAULT NULL,
  `phone` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

INSERT INTO `user` VALUES (1, '张三', '男', 18, '189----5487');
INSERT INTO `user` VALUES (2, '皇甫', '男', 24, '123----7536');

SET FOREIGN_KEY_CHECKS = 1;

对于传统的jdbc操作而言 我们通常都是怎么去连接数据库呢?先总结一下步骤

  1. 加载驱动
  2. 获取连接
  3. 准备sql
  4. 发送sql
  5. 填充参数
  6. 执行sql
  7. 执行查询的情况下获取结果集
  8. 关闭资源

那我们就根据传统JDBC去编写代码吧,进而引申出它的缺点以及为什么会有MyBatis的出现

package com.huangfu;
import java.sql.*;
/**
 * 传统jdbc的测试方案
 * 注意,再跟着练习的时候 请看清楚导包的名字 mysql jar包有重复包名,新手容易混淆
 * @author 皇甫
 */
public class TestJdbc {
    /**
     * 测试查询语句
     * @param args
     */
    public static void main(String[] args) throws SQLException {
        //数据库连接引用
        Connection connection = null;
        //sql执行器的引用
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            //驱动加载
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接
            String url = "jdbc:mysql://localhost:3306/testmybatis";
            String userName = "root";
            String password = "hr";
            connection = DriverManager.getConnection(url, userName, password);
            //准备sql
            final String SQL = "select * from user where id=?";
            //发送sql
            preparedStatement = connection.prepareStatement(SQL);
            //填充参数
            preparedStatement.setInt(1, 1);
            //执行sql
            resultSet = preparedStatement.executeQuery();
            //处理结果集
            while (resultSet.next()){
                System.out.println("id:"+resultSet.getInt("id"));
                System.out.println("name:"+resultSet.getString("name"));
                System.out.println("sex:"+resultSet.getString("sex"));
                System.out.println("age:"+resultSet.getInt("age"));
                System.out.println("phone:"+resultSet.getString("phone"));
                System.out.println("------------------------------------------");
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            // 关闭连接,释放资源
            if (resultSet != null) {
                resultSet.close();
            }
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }
}
以上的jdbc缺点是什么呢,看一下下面图解分析?

在这里插入图片描述

思考一个问题,我们能不能将驱动加载和sql,从java代码中分离出来,写进配置文件里面呢,加载驱动和获取连接利用工厂模式获取呢?我们可以将驱动加载获取连接发送sql执行sql关闭资源;封装成一个JDBCUtil工具类;当然,这里有兴趣的同学可以自己封装一下,成功了别忘了告诉我这个好消息。

MyBatis入门篇

​ 此时,我们上面所说的封装成工具类什么的,一个优秀的开源框架MyBatis全部都帮我们做到了,它将连接信息,以及SQL等全部都放入了配置文件,再配置文件中我们只需要关注SQL语句就OK了;

​ MyBatis 是一款优秀的持久层框架,它支持定制化 SQL存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。

MyBatis架构图

在这里插入图片描述

以上我们可以看出,其实MyBatis很简单

  • MyBatisConfig.xml是一个全局的配置文件,一个项目内只能有一个。
  • Mapper.xml是映射文件,可以有多个
  • SqlSessionFactory见名知意,是一个SqlSession工厂,用于获取sqlSession;获取方式为new SqlSessionFactoryBuilder.build(inputStream)
  • SqlSession 用于操作SQL的CRUD(增删改查 )
  • Executor执行器 执行sql
  • Mapped Statement 在配置文件中配置
初识MyBatis

在Resources文件夹的根目录建立MyBatisConfig.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>
    <!--配置默认使用那个环境变量  以id作为标志-->
    <environments default="test">
        <!--配置连接环境-->
        <environment id="test">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/testmybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="hr"/>
            </dataSource>
        </environment>
        <!--配置多个连接环境,至于使用谁在environments指定-->
        <environment id="development">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/testmybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="hr"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

为了方便,建立映射数据库的实体类

package com.huangfu.entity;

/**
 * 构建实体类
 * @author 皇甫
 */
public class User {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private String phone;

    public User(Integer id, String name, String sex, Integer age, String phone) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.phone = phone;
    }

    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
}

在Resources文件夹建立Mapper文件夹,在创建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" >
<!--namespace 暂时可以随意写 保证命名空间唯一-->
<mapper namespace="myUser">
    <!--ID保证唯一 resultType指定返回值类型-->
    <select id="selectUser" resultType="com.huangfu.entity.User">
        select * from user where id=#{id}
    </select>
</mapper>

将Mapper文件注入到全局配置文件 ,此时查看完整的 mybatis-config.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>
    <!--配置默认使用那个环境变量  以id作为标志-->
    <environments default="test">
        <!--配置连接环境-->
        <environment id="test">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/testmybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="hr"/>
            </dataSource>
        </environment>
        <!--配置多个连接环境,至于使用谁在environments指定-->
        <environment id="development">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/testmybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="hr"/>
            </dataSource>
        </environment>
    </environments>
    <!--将Mapper文件在这里注册-->
    <mappers>
        <!--resource书写mapper所在文件的路径-->
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

准备工作完成,开始测试我们的使用是否正确

public class TestMyBatis {
    /**
     * 先将全局配置文件读入到内存
     * 获取SqlSessionFactory
     * 获取Session
     */
    private InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    private SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    private SqlSession sqlSession = sqlSessionFactory.openSession();

    public TestMyBatis() throws IOException {
    }

    /**
     * 测试查询
     * @throws IOException
     */
    @Test
    public void selectUser() throws IOException {
        try{
            //指定哪一个Mapper文件下的哪一个语句
            //myUser对应Mapper文件中namespace的id名字  selectUser对应select id的名字
            //第二个参数 1 代表 #{id}   即要给#{id} 传值为1
            User user = sqlSession.selectOne("myUser.selectUser", 1);
            System.out.println(user);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(sqlSession !=null){
                sqlSession.close();
            }
            if(is != null){
                is.close();
            }
        }
    }
}

运行测试

User{id=1, name=‘张三’, sex=‘男’, age=18, phone=‘189----5487’}

查看何时执行的sql

导入日志jar包

		<!--导入日志JAR-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>

在Resources根目录创建文件 log4j.properties 并将下列日志文件复制进去

log4j.rootLogger=DEBUG,A1
log4j.logger.org.apache=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

再次运行查询,我们会看到控制台打印日志文件如下

在这里插入图片描述

目录结构

在这里插入图片描述

完整的演示一下MyBatis的增删改查
1.书写实体类
package com.huangfu.entity;

/**
 * 构建实体类
 * @author 皇甫
 */
public class User {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private String phone;

    public User(Integer id, String name, String sex, Integer age, String phone) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.phone = phone;
    }

    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
}

2.书写mybatis-config.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>
    <!--配置默认使用那个环境变量  以id作为标志-->
    <environments default="test">
        <!--配置连接环境-->
        <environment id="test">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/testmybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="hr"/>
            </dataSource>
        </environment>
        <!--配置多个连接环境,至于使用谁在environments指定-->
        <environment id="development">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/testmybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="hr"/>
            </dataSource>
        </environment>
    </environments>
    <!--将Mapper文件在这里注册-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

3.书写Dao接口,映射到指定mapper

package com.complete;

import com.huangfu.entity.User;

/**
 * 用户的增删改查
 * @author 皇甫
 */
public interface UserDao {
    /**
     * 查询用户 根据ID
     * @param id
     * @return
     */
    public User findOneUserById(Integer id);

    /**
     * 修改用户
     * @param user
     */
    public void updateUser(User user);

    /**
     * 增加一个用户
     * @param user
     */
    public void addUser(User user);

    /**
     * 删除一个用户
     * @param id
     */
    public void deleteUserById(Integer id);
}

书写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" >
<!--namespace 暂时可以随意写 保证命名空间唯一-->
<mapper namespace="myUser">
    <!--ID保证唯一 resultType指定返回值类型-->
    <!--查询-->
    <select id="selectUser" resultType="com.huangfu.entity.User">
        select * from user where id=#{id}
    </select>
    <!--插入-->
    <insert id="insertUser" parameterType="com.huangfu.entity.User">
        insert into
          user(id,name,sex,age,phone)
        values
          (#{id},#{name},#{sex},#{age},#{phone})
    </insert>
    <!--修改-->
    <update id="updateUser" parameterType="com.huangfu.entity.User">
        update user set
          name=#{name},sex=#{sex},age=#{age},phone=#{phone}
        where
          id=#{id}
    </update>
    <!--删除-->
    <delete id="deleteUser">
        delete from user where id=#{id}
    </delete>
</mapper>

我为了简单,简易封装一个工具类,你们可以按照上面写的,不封装,一个一个写也行

package com.complete.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * MyBatis简易工具类
 * @author 皇甫
 */
public class MyBatisUtil {
    private static InputStream is = null;
    private static  SqlSessionFactory sqlSessionFactory = null;
    static {
        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private static SqlSession sqlSession = null;

    public static SqlSession openSqlSession(){
        return sqlSessionFactory.openSession();
    }
    public static void closeSqlSession(){
        sqlSession.close();
    }

书写Dao实现类

package com.complete.dao.impl;

import com.complete.dao.UserDao;
import com.complete.util.MyBatisUtil;
import com.huangfu.entity.User;
import org.apache.ibatis.session.SqlSession;

/**
 *
 * @author 皇甫
 */
public class UserDaoImpl implements UserDao {
    private  SqlSession sqlSession = null;

    public UserDaoImpl() {
        sqlSession = MyBatisUtil.openSqlSession();
    }

    public User findOneUserById(Integer id) {
        User user = sqlSession.selectOne("myUser.selectUser", id);
        MyBatisUtil.closeSqlSession();
        return user;
    }

    public void updateUser(User user) {
        try{
            sqlSession.update("myUser.updateUser", user);
            //事务控制
            sqlSession.commit();
            System.out.println("修改成功");
        }catch (Exception e){
            sqlSession.rollback();
            e.printStackTrace();
        }finally {
            MyBatisUtil.closeSqlSession();
        }
    }

    public void addUser(User user) {
        try{
            sqlSession.insert("myUser.insertUser",user);
            //事务控制
            sqlSession.commit();
            System.out.println("插入成功");
        }catch (Exception e){
            sqlSession.rollback();
            e.printStackTrace();
        }finally {
            MyBatisUtil.closeSqlSession();
        }

    }

    public void deleteUserById(Integer id) {
        try{
            sqlSession.insert("myUser.deleteUser",id);
            //事务控制
            sqlSession.commit();
            System.out.println("删除成功");
        }catch (Exception e){
            sqlSession.rollback();
            e.printStackTrace();
        }finally {
            MyBatisUtil.closeSqlSession();
        }
    }
}

测试运行

package com.huangfu.testmybatis;

import com.complete.dao.UserDao;
import com.complete.dao.impl.UserDaoImpl;
import com.huangfu.entity.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

/**
 * 测试MyBatis
 * @author 皇甫
 */
public class MyBatisTest {

    private UserDao ud = new UserDaoImpl();
    @Test
    public void testSelectUser(){
        User user = ud.findOneUserById(1);
        System.out.println(user);
    }
    @Test
    public void testInsertUser(){
        User user = new User(4, "赵六", "女", 20, "165----9574");
        ud.addUser(user);
    }
    @Test
    public void testUpdateUser(){
        User user = new User(2, "皇甫", "男", 20, "165----9574");
        ud.updateUser(user);
    }
    @Test
    public void testDeleteUser(){
        ud.deleteUserById(1);
    }
}
MyBatis中级篇

​ 如约而至,MyBatis的中级篇;小伙伴们对昨天的中级篇的代码掌握了嘛!在程序猿这里,程序这条路没有捷径,只有怀着对技术的热枕砥砺前行,多写多练,才能使自己的技术更进一步!如果你真的想掌握MyBatis的使用,就把这几篇所有讲到的代码吃透,保证无论是你自己写demo,还是企业中的实际开发,在使用MyBatis的时候都没有任何问题!如果有问题的话可以加我微信号,文末我会贴上自己的微信号,有问题的话我会第一时间回复你。

本章节我将继续讲述MyBatis,会对上一章节的一些代码进行简化和重构,使整个程序看起来更加优雅,易扩展。

提取数据连接配置

思考一个问题 ,上节课讲到,我们将关于数据库的一些配置全部都配置在了一个全局配置文件上,我们能否将配置信息拿出来,单独放置在一个文件里?`

将数据库连接信息提取到外部.properties文件,Resources文件夹下建立properties文件夹,并创建db.properties文件

mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://127.0.0.1:3306/testmybatis
mysql.username=root
mysql.password=hr

完整的配置文件信息如下

<?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="properties/db.properties"></properties>
    <!--配置默认使用那个环境变量  以id作为标志-->
    <environments default="test">
        <!--配置连接环境-->
        <environment id="test">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver}"/>
                <property name="url" value="${mysql.url}"/>
                <property name="username" value="${mysql.username}"/>
                <property name="password" value="${mysql.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--将Mapper文件在这里注册-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration> 

将配置信息文件导入全局配置文件mybatis-config.xml;在mybatis-config.xml中配置一下配置。

<properties resource="properties/db.properties"></properties>

思考:如果配置文件缺失,能否再缺失配置文件的情况下保证程序正常运行呢?引申出下面的概念,给配置信息增加默认值。

首先我们需要开启MyBatis对配置文件默认值的支持

<properties resource="properties/db.properties">
        <!--开启配置文件配置默认值-->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>

其次,在进行注入数据时加上 :分割,具体注入操作如下;

 <!--数据源   连接池使用该POOLED-->
<dataSource type="POOLED">
     <property name="driver" value="${mysql.driver:com.mysql.jdbc.Driver}"/>
     <property name="url" value="${mysql.url:jdbc:mysql://127.0.0.1:3306/testmybatis}"/>
     <property name="username" value="${mysql.username:root}"/>
     <property name="password" value="${mysql.password:hr}"/>
</dataSource>

当你注入配置文件时,就使用配置文件注入,当不使用配置文件时就是用默认数据库。

MyBatis自带动态代理执行sql语句

首先大概介绍一下什么叫做动态代理类动态代理类必须实现InvocationHandler,覆盖invoke方法!通过new ProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler invocationHandler )返回一个实例,这个实例就是代理对象,使用ClassLoader来动态的生成代理对象,通过反射拿到目标类的构造函数,从而调用目标类的方法,其唯一参数类型是调用处理器接口类型! 想详细了解动态代理类详情请移步我以前的一篇博客设计模式之代理模式

根据上面的说法,我们如果想要使用动态代理类就必须提供类对象以及方法名字和参数;相同MyBatis想要使用动态代理类也必须知道这几个先决条件!

下面,我们去看如何去使用动态代理吧。为了区分上一节课,我们重新建立一个数据库表(依旧可以直接复制)

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` int(7) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `clazzname` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

INSERT INTO `student` VALUES (1, '张三', '计算机网络技术01班');

SET FOREIGN_KEY_CHECKS = 1;

老规矩还是先建立实体类

package com.recommend.entity;

/**
 * @author 皇甫
 */
public class Student {
    private Integer id;
    private String name;
    private String clazzName;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", clazzName='" + clazzName + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getClazzName() {
        return clazzName;
    }

    public void setClazzName(String clazzName) {
        this.clazzName = clazzName;
    }

    public Student(Integer id, String name, String clazzName) {
        this.id = id;
        this.name = name;
        this.clazzName = clazzName;
    }

    public Student() {
    }
}

建立DAO接口

package com.recommend.dao;

import com.recommend.entity.Student;

import java.util.List;

/**
 * @author 皇甫
 */
public interface StudentDao {
    /**
     * 查询所有学生信息
     * @return
     */
    public List<Student> findAllStudent();
}

配置Mapper文件;此时,请注意,使用动态代理类最关键的一点是 必须提供类对象 方法名 ,类对象可以通过全限定名获取,故在配置Mapper的namespace时,所使用的命名必须时要映射的Dao接口的全限定名;在设置增删改查的id时必须时方法名称;请关注我划重点的地方,这是整个MyBatis使用动态代理的核心点。具体实现如下:

<?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.recommend.dao.StudentDao">
    <select id="findAllStudent" resultType="com.recommend.entity.Student">
        select * from student
    </select>
</mapper>

下面我将重点圈起来

在这里插入图片描述

最重要的一步,别忘了将Mapper注入到全局配置文件

在这里插入图片描述

此时就完成了动态代理类的使用,我们此时可将Dao实现类省略,有动态代理来隐式生成实现类;省略实现类,直接测试如下:

package com.recommend.testmybatis;

import com.complete.util.MyBatisUtil;
import com.recommend.dao.StudentDao;
import com.recommend.entity.Student;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
 * @author 皇甫
 */
public class MybatisTest {
    private SqlSession sqlSession= null;
    public MybatisTest() {
        //使用工具类获取SqlSession
        sqlSession = MyBatisUtil.openSqlSession();
    }

    @Test
    public void findAll(){
        //将Dao接口注入到sqlSession,动态生成DaoImpl对象
        StudentDao mapper = sqlSession.getMapper(StudentDao.class);
        //执行查询
        List<Student> students = mapper.findAllStudent();
        for (Student student : students) {
            System.out.println(student.getName());
        }
    }
}
动态代理总结
>1. namespace的名字必须同映射Dao接口的全限定名相同
>2. sql所定义的id必须同方法名字相同
>3. sqlSession.getMapper(Clazz<T> clazz)  参数必须为所要映射的Dao的类对象
MyBatis中的多参数查询

如果你想使用班级和名字去查询一个人的信息,此时你应该怎么去设计sql以及Dao接口

 /**
     * 根据名字和班级查询
     * @param name
     * @param clazz
     * @return
     */
    public List<Student> findStudentByNameAndClazz(String name,String clazz);

Mapper文件为

	<select id="findStudentByNameAndClazz" resultType="com.recommend.entity.Student">
        select * from student where name=#{name} and clazzname = #{clazzName}
    </select>

测试执行报错,报错信息如下

在这里插入图片描述

报错信息说找不到name,可是我明明有啊!原来是因为在多个参数的情况下,系统并不知道你要把值注入给谁,需要你手动去指定!

使用注解@Param(“对应sql注入的名字”)
 /**
     * 根据名字和班级查询
     * @Param内部参数对应着你sql语句中#{name},#{clazzName} 名字必须一致
     * @param name
     * @param clazz
     * @return
     */
		public List<Student> findStudentByNameAndClazz(@Param("name") String name, @Param("clazzName") String clazz);

再次运行就OK了!

Mapper文件中 ${id} #{id}的区别

在我们前面的使用中一直使用的都是#{id}取值,还有另外一种取值方式KaTeX parse error: Expected 'EOF', got '#' at position 15: {id},他们两个的区别为,#̲{id}为注入参数,{id}为拼接参数!#{id}不可进行运算; i d 可 以 进 行 运 算 ! 当 我 们 使 用 {id}可以进行运算!当我们使用 id使{id}进行注入时,此时会报错,但是我们可以观看它的日志详情

<select id="findStudentByNameAndClazz" resultType="com.recommend.entity.Student">
        select * from student where name=${name} and clazzname = ${clazzName}
 </select>

在这里插入图片描述

那么 i d 报 错 的 情 况 也 就 很 容 易 解 释 了 , 因 为 字 符 串 条 件 是 需 要 加 ‘ ′ ′ ‘ 的 , 直 接 拼 接 的 没 有 所 以 报 错 , 它 一 般 应 用 在 什 么 情 况 下 呢 ! 上 面 说 到 了 , {id}报错的情况也就很容易解释了,因为字符串条件是需要加`&#x27;&#x27;`的,直接拼接的没有所以报错,它一般应用在什么情况下呢!上面说到了, id{}支持运算,当你想分页时可以使用!

<select id="findAllStudent" resultType="com.recommend.entity.Student">
        select * from student limit 0,${1*2}
    </select>

当你想根据某些条件查询不同的表时也可以使用

<select id="findAllStudent" resultType="com.recommend.entity.Student">
        select * from ${table_name}
    </select>

以上就是中级篇的内容,下期高级篇主要讲述知识点为:

  1. MyBatis的动态SQL
  2. MyBatis的缓存策略
  3. MyBatis的多表连接查询映射即MySql的高级查询
  4. Spring整合MyBatis如何整合
MyBatis高级篇

hello,大家好,咱们又见面了,如约而至,相信观看前面的两篇之后对MyBatis有了一个比较清除的认识,并且大家应该能够基本使用的MyBatis框架解决一些实际问题!但是如果大家经过认真思考之后,一定会发现很多问题:

例如JDBC中的一些查询条件可以根据业务需求而改变,使得一个查询语句完成多种查询,而在MyBatis中,似乎一个查询语句只能完成一个功能;例如多个条件的模糊查询根据不同查询条件查询对应的i结果等等!

咱们之前所有的查询语句全部都是单表查询,而且细心的小伙伴可能已经发现了,咱们所建的实体类全部都是和表中字段名字一样的,难道只能这样嘛?MyBatis能够做多表连接查询嘛?下面咱们就带着这些疑问往下继续学习吧!

初识MyBatis的动态SQL语句

建立数据表book

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `id` int(7) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `author` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `createdate` datetime NULL DEFAULT NULL,
  `status` int(7) NULL DEFAULT NULL,
  `clazzid` int(7) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of book
-- ----------------------------
INSERT INTO `book` VALUES (1, '鲁宾孙漂流记', '丹尼尔.迪福', '2019-02-24 14:38:58', 0, 1);
INSERT INTO `book` VALUES (2, '红楼梦', '曹雪芹', '2019-02-12 14:39:27', 0, 2);
INSERT INTO `book` VALUES (3, '水浒传', '施耐庵', '2019-02-15 14:39:44', 0, 2);
INSERT INTO `book` VALUES (4, '三国演义', '罗贯中', '2019-02-24 14:40:13', 0, 2);
INSERT INTO `book` VALUES (5, '西游记', '吴承恩', '2019-01-30 14:40:39', 0, 2);
INSERT INTO `book` VALUES (6, '聊斋志异', '蒲松龄', '2019-01-29 14:40:56', 0, 2);
INSERT INTO `book` VALUES (7, '简爱', '夏洛蒂.勃朗特', '2019-02-14 14:41:11', 0, 1);
INSERT INTO `book` VALUES (8, 'MyBatis入门篇章', '皇甫嗷嗷叫', '2019-02-24 14:54:59', 1, 3);
INSERT INTO `book` VALUES (9, 'MyBatis中级篇章', '皇甫嗷嗷叫', '2019-02-24 14:55:34', 1, 3);
INSERT INTO `book` VALUES (10, 'MyBatis高级篇章', '皇甫嗷嗷叫', '2019-02-24 14:56:01', 1, 3);

SET FOREIGN_KEY_CHECKS = 1;

需求:根据文章姓名和作者姓名查询具体数据,当用户只输入作者姓名时,只按照作者姓名查询;当用户只输入文章名称时只按照文章姓名查找;当作者数据文章标题和作者名称时,按照两个文章进行查询!

在看到这个需求时,如果你不了解MyBatis的动态sql的话,恐怕第一想到的就是,后台进行一系列的判断,判断执行那个方法和对应的具体sql吧!但是MyBatis为我们提供了强大的动态SQL后,一切都变的简单起来。

MyBatis提供了很强大的动态sql功能,他可以在sql语句内部进行判断,从而来完成一个动态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" >
<mapper namespace="com.senior.dao.BookDao">
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select * from book where
        <if test="author!=null">
            author = #{author}
        </if>
        <if test="name!=null">
          and name = #{name}
        </if>

    </select>
</mapper>

具体代码

1.创建实体类

package com.senior.entity;


import java.util.Date;

/**
 * @author 皇甫
 */
public class Book {
    private Integer id;
    private String name;
    private String author;
    private Date createDate;
    private Integer status;
    private Integer clazzId;

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", createDate=" + createDate +
                ", status=" + status +
                ", clazzId=" + clazzId +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Integer getClazzId() {
        return clazzId;
    }

    public void setClazzId(Integer clazzId) {
        this.clazzId = clazzId;
    }

    public Book() {
    }

    public Book(Integer id, String name, String author, Date createDate, Integer status, Integer clazzId) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.createDate = createDate;
        this.status = status;
        this.clazzId = clazzId;
    }
}

2.创建Dao接口

package com.senior.dao;

import com.senior.entity.Book;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author 皇甫
 */
public interface BookDao {
    /**
     * 需求功能
     * 根据作者或者书籍名字查询
     * @param author
     * @param name
     * @return
     */
    public List<Book> queryBookByAuthorOrName(@Param("author") String author, @Param("name") String name);
}

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="com.senior.dao.BookDao">
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select * from book where
        <if test="author!=null">
            author = #{author}
        </if>
        <if test="name!=null">
          and name = #{name}
        </if>

    </select>
</mapper>

注册进MyBatis-config.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="properties/db.properties">
        <!--开启配置文件配置默认值-->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    </properties>
    <!--配置默认使用那个环境变量  以id作为标志-->
    <environments default="test">
        <!--配置连接环境-->
        <environment id="test">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver:com.mysql.jdbc.Driver}"/>
                <property name="url" value="${mysql.url:jdbc:mysql://127.0.0.1:3306/testmybatis}"/>
                <property name="username" value="${mysql.username:root}"/>
                <property name="password" value="${mysql.password:hr}"/>
            </dataSource>
        </environment>
    </environments>
    <!--将Mapper文件在这里注册-->
    <mappers>
        <mapper resource="mapper/BookMapper.xml"/>
    </mappers>
</configuration>

运行测试

1.只存在作者姓名时

package com.senior.test;

import com.complete.util.MyBatisUtil;
import com.senior.dao.BookDao;
import com.senior.entity.Book;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
 * @author 皇甫
 */
public class MyBatisTest {
    private SqlSession sqlSession = null;

    public MyBatisTest() {
        sqlSession = MyBatisUtil.openSqlSession();
    }

    @Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        for (Book book : books) {
            System.out.println(book);
        }
    }
}

日志如下

在这里插入图片描述

两个条件都存在时:

package com.senior.test;

import com.complete.util.MyBatisUtil;
import com.senior.dao.BookDao;
import com.senior.entity.Book;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
 * @author 皇甫
 */
public class MyBatisTest {
    private SqlSession sqlSession = null;

    public MyBatisTest() {
        sqlSession = MyBatisUtil.openSqlSession();
    }

    @Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", "MyBatis入门篇章");
        for (Book book : books) {
            System.out.println(book);
        }
    }
}

日志如下:

在这里插入图片描述

以上就是动态sql的常规用法,下面我们将介绍几个几个常用的动态SQL语句

1.动态SQL之 if
<select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
    select * from book where
    <if test="author!=null">
        author = #{author}
    </if>
    <if test="name!=null">
        and name = #{name}
    </if>
</select>
2.动态SQL之 choose when otherwise

相当于java语言里面的if..else if ...else,一旦有前面的when 符合条件,则后面的不会再执行,如果前面的when 都没有匹配条件,则执行otherwise

<?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.senior.dao.BookDao">
    <!--1=1的作用是  当第一个条件不成立,第二个条件执行时,前面会多个and 导致报错,故加上一个无关紧要的条件来取消掉这个错误-->
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select * from book where 1=1
        <choose>
            <when test="author!=null">
                and author = #{author}
            </when>
            <when test="name!=null">
                and name = #{name}
            </when>
            <otherwise>
                author = "皇甫嗷嗷叫"
            </otherwise>
        </choose>
    </select>
</mapper>
3.动态SQL之 where

这里没有加1=1的原因是 自动过滤了 最前面的 and

<?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.senior.dao.BookDao">
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select * from book
        <where>
            <if test="author!=null">
                author = #{author}
            </if>
            <if test="name!=null">
              and name = #{name}
            </if>
        </where>
    </select>
</mapper>
4.动态SQL之 set

书写DAO接口方法

	/**
     * 修改书籍信息
     * @param book
     */
    public void updateBook(Book book);

书写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="com.senior.dao.BookDao">
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select * from book
        <where>
            <if test="author!=null">
                author = #{author}
            </if>
            <if test="name!=null">
              and name = #{name}
            </if>
        </where>
    </select>
    <!--只修改要修改的值,不必去构建一个完整的Book-->
    <update id="updateBook" parameterType="com.senior.entity.Book">
        update book
        <set>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="author!=null">
                author = #{author},
            </if>
            <if test="createDate!=null">
                createdate = #{createDate},
            </if>
            <if test="status !=null">
                status = #{status},
            </if>
            <if test="clazzId !=null">
                clazzid = #{clazzId}
            </if>
        </set>
        where id=#{id}
    </update>
</mapper>

测试

 	@Test
    public void testUpDateBook(){
        Book book = new Book();
        book.setId(10);
        book.setStatus(0);
        BookDao bd = sqlSession.getMapper(BookDao.class);
        bd.updateBook(book);
        sqlSession.commit();
    }
5.动态SQL之 foreach

用于解决循环数组的问题

    /**
     * 根据给定id集合 查询所有符合条件的数据
     * @param ids
     * @return
     */
    public List<Book> findBookByIds(@Param("ids") Integer[] ids);

Mapper

    <select id="findBookByIds" resultType="com.senior.entity.Book">
        select * from book where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

因为参数比较多,重点解释一下

----正常这个SQL语句为:
select * from book where id in(1,2,3,4,5);

1.collection:没有@Param(xxx)指定的时候,默认为数组的类型,即-数组为array 集合为list,@Param(xxx)指定的时候为@Param的值。

2.item:为遍历的单个名字,命名随意,使用时直接使用即可,详情见Mapper的编写内容#{id}

3.open:in后面以(开头

4.close:顾名思义,以结尾

5.separator:作为分割

MyBatis缓存
一级缓存

首先了解什么叫缓存,缓存可以看作是一种程序的优化机制,拿查询来说,第一次查询,会访问数据库,查询到结果之后,先将结果保存在内存中,然后在返回结果;第二次执行相同的sql时,先判断内存中是否有此查询的结果,有就直接返回不访问数据库;没有再去访问数据库;这样就保证了程序访问数据库的次数减少,防止了数据库压力过大导致崩溃。

MyBatis中的缓存是默认开启的无法关闭,当然只是单指MyBatis单独存在的情况下,后期涉及到MyBatis整合Spring,Spring会将一级缓存关闭掉,当然这是后话!但只有MyBatis的情况下,一级缓存是无法关闭的!

一级缓存的作用范围

一级缓存的作用范围是同一个session同一个sql相同的参数

当我们执行一个查询语句两次时,我们看日志会是什么情况

	@Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        List<Book> books1 = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        for (Book book : books) {
            System.out.println(book);
        }
    }

此时我们查看日志

在这里插入图片描述

发现,我们执行了两次查询,但是只执行了一次sql语句,为了证明缓存的存在,我们在执行一遍查询之后将缓存清除掉,在去查看日志!sqlSession.clearCache();是清除缓存

    @Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        //清除缓存
        sqlSession.clearCache();
        List<Book> books1 = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        for (Book book : books) {
            System.out.println(book);
        }
    }

日志如下

在这里插入图片描述

我们可以很清楚的看到,缓存清除之后,sql语句执行了两边;所以能够证明一级缓存默认开启,且无法关闭,能够优化查询效率。但是注意一点哦执行修改操作会清空缓存哦!例如 :update insert delete等

二级缓存

二级缓存的作用范围是:

一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。

开启二级缓存的方法是在Mapper文件里面加一个标签 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="com.senior.dao.BookDao">
    <cache/>
</mapper>

完整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="com.senior.dao.BookDao">
    <cache/>
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select * from book
        <where>
            <if test="author!=null">
                author = #{author}
            </if>
            <if test="name!=null">
              and name = #{name}
            </if>
        </where>
    </select>

    <update id="updateBook" parameterType="com.senior.entity.Book">
        update book
        <set>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="author!=null">
                author = #{author},
            </if>
            <if test="createDate!=null">
                createdate = #{createDate},
            </if>
            <if test="status !=null">
                status = #{status},
            </if>
            <if test="clazzId !=null">
                clazzid = #{clazzId}
            </if>
        </set>
        where id=#{id}
    </update>
    <select id="findBookByIds" resultType="com.senior.entity.Book">
        select * from book where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>
</mapper>

测试观看日志;为了验证二级缓存的存在,咱们查询一次之后把Session给关闭掉,在创建一个新的SqlSession看它的日志输出情况!

    @Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        //关闭sqlSession
        sqlSession.close();
        //创建一个新的SqlSession
        SqlSession newSqlSession = MyBatisUtil.openSqlSession();
        BookDao newDd = newSqlSession.getMapper(BookDao.class);
        List<Book> books1 = newDd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);

        for (Book book : books) {
            System.out.println(book);
        }
    }

查看日志

在这里插入图片描述

可以看出Sql只执行了一次,由第二个圈着的地方:是计算的缓存命中率,命中率为0.5,执行了两次sql,在缓存中只查了一次,所以它的命中率为0.5,也很直接的证明了二级缓存在起作用。

关闭二级缓存

1.不开启 二级缓存默认关闭

2.在全局配置文件中配置,全局配置文件一旦禁用二级缓存,Mapper开启二级缓存也没用,Mapper配置文件的配置如下:

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

完整配置文件 注意顺序

<?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="properties/db.properties">
        <!--开启配置文件配置默认值-->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    </properties>

    <settings>
        <setting name="cacheEnabled" value="false"/>
    </settings>
    <!--配置默认使用那个环境变量  以id作为标志-->
    <environments default="test">
        <!--配置连接环境-->
        <environment id="test">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver:com.mysql.jdbc.Driver}"/>
                <property name="url" value="${mysql.url:jdbc:mysql://127.0.0.1:3306/testmybatis}"/>
                <property name="username" value="${mysql.username:root}"/>
                <property name="password" value="${mysql.password:hr}"/>
            </dataSource>
        </environment>
    </environments>
    <!--将Mapper文件在这里注册-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
        <mapper resource="mapper/StudentMapper.xml"/>
        <mapper resource="mapper/BookMapper.xml"/>
    </mappers>
</configuration>

其中可以配置缓存策略

在这里插入图片描述

多表查询 多表映射

在我们使用Sql进行查询时,难免有时候会遇到表字段与实体类属性不相同的情况,我们可以这样解决!

select id as 你的属性名

	<select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select id as `你的属性名`,name,author from book
        <where>
            <if test="author!=null">
                author = #{author}
            </if>
            <if test="name!=null">
              and name = #{name}
            </if>
        </where>
    </select>

这样做只适用于单表查询,如果时多表查询你应该怎么搞?此时就需要用到resultMap属性了

我们先创建一个类别表,以便下面进行测试


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `bookclazz`;
CREATE TABLE `bookclazz`  (
  `id` int(7) NOT NULL,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;


INSERT INTO `bookclazz` VALUES (1, '国外名著');
INSERT INTO `bookclazz` VALUES (2, '四大名著');
INSERT INTO `bookclazz` VALUES (3, 'MyBatsi');

SET FOREIGN_KEY_CHECKS = 1;
此时如果有个需求:查询一个类别下的所有书籍,此时就应该会用到表连接,其sql应该这样写!
SELECT c.`name`,b.`name`,b.createdate,b.author FROM book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id 

查询结果是这样

在这里插入图片描述

哪映射到Mapper文件上应该怎么写呢?此时我们应该去回想一下JAVA面向对象的思想,试想,一个类别下应该有多个书籍,所以类别实体类下应该有个Book类型的集合!一本书应该只能属于一个类别,所以书籍实体类下应该会有一个类别实体类对象,具体实体类设计应该这样搞!

package com.senior.entity;

import java.util.Date;

/**
 * 书籍实体类
 * @author 皇甫
 */
public class NewBook {
    private Integer id;
    private String name;
    private String author;
    private Date createDate;
    private Integer status;
    /**
     * 一本书一定属于一个类别,所以应该有个类别对象
     */
    private BookClazz bookClazz;

    @Override
    public String toString() {
        return "NewBook{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", createDate=" + createDate +
                ", status=" + status +
                ", bookClazz=" + bookClazz +
                '}';
    }

    public Integer getId() {

        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public BookClazz getBookClazz() {
        return bookClazz;
    }

    public void setBookClazz(BookClazz bookClazz) {
        this.bookClazz = bookClazz;
    }

    public NewBook(Integer id, String name, String author, Date createDate, Integer status, BookClazz bookClazz) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.createDate = createDate;
        this.status = status;
        this.bookClazz = bookClazz;
    }

    public NewBook() {
    }
}
package com.senior.entity;

import java.util.ArrayList;
import java.util.List;

/**
 * 类别实体类
 * @author 皇甫
 */
public class BookClazz {
    private Integer id;
    private String name;
    /**
     * 每一个类别下都有很多本书籍,故而使用集合存放
     */
    private List<Book> books = new ArrayList<Book>();

    @Override
    public String toString() {
        return "BookClazz{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", books=" + books +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }

    public BookClazz(Integer id, String name, List<Book> books) {
        this.id = id;
        this.name = name;
        this.books = books;
    }

    public BookClazz() {
    }
}

编写DAO接口类

package com.senior.dao;

import com.senior.entity.BookClazz;

import java.util.List;

/**
 * @author 皇甫
 */
public interface BookClazzDao {
    /**
     * 查询所有
     * @return
     */
    public List<BookClazz> queryAllBookClazz();
}

编写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="com.senior.dao.BookClazzDao">
    <!--id 随便起 type你要返回的类型  这里返回的是书籍类别-->
    <resultMap id="queryAllBookClazzMap" type="com.senior.entity.BookClazz">
        <!--column 为你数据库字段的名字 或as重名后的名字 property为对应的实体类属性的名字-->
        <!--id 为主键映射  result为普通字段映射-->
        <id property="id" column="c_id"/>
        <result property="name" column="c_name"/>
        <!--collection 代表你要映射的是实体类里面的集合属性-->
        <!--property 实体类里面集合类的名字-->
        <!--javaType 集合类的泛型种类-->
        <collection property="books" javaType="com.senior.entity.Book">
            <id property="id" column="b_id"/>
            <result property="name" column="b_name"/>
            <result property="createDate" column="b_date"/>
            <result property="author" column="b_author"/>
        </collection>
    </resultMap>
    <!--id 对应方法的名字  resultMap对应resultMap定义的id值-->
    <select id="queryAllBookClazz" resultMap="queryAllBookClazzMap">
        SELECT
          c.id as c_id,
          c.`name` as c_name,
          b.id as b_id,
          b.`name` as b_name,
          b.createdate b_date,
          b.author  b_author
        FROM
          book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id
    </select>
</mapper>

测试

    @Test
    public void testQueryAllBookClazz(){
        BookClazzDao bc = sqlSession.getMapper(BookClazzDao.class);
        List<BookClazz> bookClazzes = bc.queryAllBookClazz();
        for (BookClazz bookClazz : bookClazzes) {
            System.out.println("------------------"+bookClazz.getName()+"------------------");
            for (Book book : bookClazz.getBooks()) {
                System.out.println(book);
            }
        }
    }

结果

------------------国外名著------------------
Book{id=1, name='鲁宾孙漂流记', author='丹尼尔.迪福', createDate=Sun Feb 24 14:38:58 CST 2019, status=null, clazzId=null}
Book{id=7, name='简爱', author='夏洛蒂.勃朗特', createDate=Thu Feb 14 14:41:11 CST 2019, status=null, clazzId=null}
------------------四大名著------------------
Book{id=3, name='水浒传', author='施耐庵', createDate=Fri Feb 15 14:39:44 CST 2019, status=null, clazzId=null}
Book{id=6, name='聊斋志异', author='蒲松龄', createDate=Tue Jan 29 14:40:56 CST 2019, status=null, clazzId=null}
Book{id=2, name='红楼梦', author='曹雪芹', createDate=Tue Feb 12 14:39:27 CST 2019, status=null, clazzId=null}
Book{id=5, name='西游记', author='吴承恩', createDate=Wed Jan 30 14:40:39 CST 2019, status=null, clazzId=null}
Book{id=4, name='三国演义', author='罗贯中', createDate=Sun Feb 24 14:40:13 CST 2019, status=null, clazzId=null}
------------------MyBatsi------------------
Book{id=9, name='MyBatis中级篇章', author='皇甫嗷嗷叫', createDate=Sun Feb 24 14:55:34 CST 2019, status=null, clazzId=null}
Book{id=8, name='MyBatis入门篇章', author='皇甫嗷嗷叫', createDate=Sun Feb 24 14:54:59 CST 2019, status=null, clazzId=null}
Book{id=10, name='MyBatis高级篇章', author='皇甫嗷嗷叫', createDate=Sun Feb 24 14:56:01 CST 2019, status=null, clazzId=null}

那么当我想查询一个书属于那个类别应该如何去查呢?

dao接口

	/**
     * 查询所有书籍以及类别
     * @return
     */
    public List<NewBook> findBookAndBookClazz();

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="com.senior.dao.BookDao">
    <cache/>
    <resultMap id="findBookAndBookClazzMap" type="com.senior.entity.NewBook">
        <id property="id" column="b_id"/>
        <result property="name" column="b_name"/>
        <result property="createDate" column="b_date"/>
        <result property="author" column="b_author"/>
        <!--association 为对象类型不是集合类型-->
        <association property="bookClazz" javaType="com.senior.entity.BookClazz">
            <id property="id" column="c_id"/>
            <result property="name" column="c_name"/>
        </association>
    </resultMap>


    <select id="findBookAndBookClazz" resultMap="findBookAndBookClazzMap">
        SELECT
          c.id as c_id,
          c.`name` as c_name,
          b.id as b_id,
          b.`name` as b_name,
          b.createdate b_date,
          b.author  b_author
        FROM
          book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id
    </select>
</mapper>

测试

	@Test
    public void testFindBookAndBookClazz(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<NewBook> books = bd.findBookAndBookClazz();
        for (NewBook book : books) {
            System.out.println(book.getName()+"--类别:"+book.getBookClazz().getName());
        }
    }

结果

鲁宾孙漂流记--类别:国外名著
简爱--类别:国外名著
水浒传--类别:四大名著
聊斋志异--类别:四大名著
红楼梦--类别:四大名著
西游记--类别:四大名著
三国演义--类别:四大名著
MyBatis中级篇章--类别:MyBatsi
MyBatis入门篇章--类别:MyBatsi
MyBatis高级篇章--类别:MyBatsi

我们发现两次查询的SQL是一样的,但是结果却截然不同,这就是Map映射的魅力!

SQL片段

但是,我们能否更简化一点呢?既然一个SQL两个配置文件都用到了 我们能否将SQL提取出来呢

Mapper内的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" >
<mapper namespace="com.senior.dao.BookClazzDao">
    <!--id 随便起 type你要返回的类型  这里返回的是书籍类别-->
    <resultMap id="queryAllBookClazzMap" type="com.senior.entity.BookClazz">
        <!--column 为你数据库字段的名字 或as重名后的名字 property为对应的实体类属性的名字-->
        <!--id 为主键映射  result为普通字段映射-->
        <id property="id" column="c_id"/>
        <result property="name" column="c_name"/>
        <!--collection 代表你要映射的是实体类里面的集合属性-->
        <!--property 实体类里面集合类的名字-->
        <!--javaType 集合类的泛型种类-->
        <collection property="books" javaType="com.senior.entity.Book">
            <id property="id" column="b_id"/>
            <result property="name" column="b_name"/>
            <result property="createDate" column="b_date"/>
            <result property="author" column="b_author"/>
        </collection>
    </resultMap>
    
    <!--将SQL片段提提取出来-->
    <sql id="NEWBOOKANDBOOKCLAZZ">
        c.id as c_id,
          c.`name` as c_name,
          b.id as b_id,
          b.`name` as b_name,
          b.createdate b_date,
          b.author  b_author
    </sql>
    <!--id 对应方法的名字  resultMap对应resultMap定义的id值-->
    <select id="queryAllBookClazz" resultMap="queryAllBookClazzMap">
        /*SQL语句内引入SQL片段*/
        SELECT
          <include refid="NEWBOOKANDBOOKCLAZZ"/>
        FROM
          book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id
    </select>
</mapper>
查询语句中出现大于号和小于号的解决方案

查询语句中出现大于号和小于号的解决方案

在这里插入图片描述

业务需求,求书籍id小于2的数据

select * from book where id>2

Mapper

<!--报错   因为不允许使用`<`-->
<select id="queryBook" resultType="com.senior.entity.Book">
        select * from book where id < 2
    </select>
解决方案1
<select id="queryBook" resultType="com.senior.entity.Book">
        select * from book where id &lt; 2
    </select>
解决方案2

<select id="queryBook" resultType="com.senior.entity.Book">
    <![CDATA[
		 select * from book where id < 2
	]]>      
</select>

喜欢这篇文章的小伙伴,请识别下方二维码关注本公众号,小编不定期赠送免费的学习资源哦

在这里插入图片描述

下面是我的个人微信号,欢迎叨扰!

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值