Mybatis

一、原始JDBC开发存在的问题

package com.qf.java2107.test;

import org.junit.Test;

import java.math.BigDecimal;
import java.sql.*;

/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-22
 **/
public class JdbcTest {

    /**
     * 存在的问题:
     *      1. 需要频繁的手动获取连接
     *      2. 需要手动封装查询结果集
     *      3. 需要手动释放资源
     *      4. SQL硬编码
     *   有自己封装的工具类,也有DBUtils等工具类API。但是都没有从根本上解决上面的问题
     */
    @Test
    public void testJdbc(){

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            //1.反射加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            //2.获取数据库连接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2106_hotel", "root", "root");

            //3.编写SQL
            String sql = "SELECT * FROM t_user WHERE user_id = ?";

            //4.获取执行SQL的载体对象,预编译SQL
            preparedStatement = connection.prepareStatement(sql);
            //填充占位符
            preparedStatement.setLong(1, 3);

            //5.执行SQL
            resultSet = preparedStatement.executeQuery();

            //6.处理结果
            if (resultSet.next()) {
                //user_id : 结果集的列名
                long userId = resultSet.getLong("user_id");
                String username = resultSet.getString("username");
                BigDecimal balance = resultSet.getBigDecimal("balance");
                System.out.println(userId + "," + username + "," + balance );
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {

            //7.释放资源
            //先开后关
            try {
                if(null != resultSet) {
                    resultSet.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(null != preparedStatement) {
                    preparedStatement.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(null != connection) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}
  • 存在的问题

      1. 需要频繁的手动获取连接
    •  2. 需要手动封装查询结果集
      
    •  3. 需要手动释放资源
      
    •  4. SQL硬编码
      

二、ORM框架

  • 通过ORM框架解决JDBC存在的问题

1、ORM

  • Object Relation Mapping:对象关系映射
Java对象数据库表
类名表名
属性名列名【字段名】
对象行【记录】

2、框架

  • 概述:就是一个半成品软件
    • 需要使用框架帮我们完成JavaEE应用的开发
  • 作用
    • 能够帮我们快速有效的开发JavaEE应用
    • 有自己对应用场景的完整解决方案

三、Mybatis

1、概述

  • 是一款开源的、优秀的、支持定制SQL的ORM框架

  • 官网:https://mybatis.org/mybatis-3/zh/index.html

    MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

2、快速入门

  • 参考官网案例

3.2.1 实现步骤

  1. 创建数据库表
  2. 导入依赖
  3. 实体类
  4. Mapper接口【Dao接口】
  5. SQL映射文件
  6. 全局配置文件
  7. 测试

3.2.2 具体实现

  • 创建数据库表
CREATE TABLE `t_user` (
  `user_id` bigint(100) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(20) DEFAULT NULL COMMENT '用户名',
  `password` varchar(32) DEFAULT NULL COMMENT '密码',
  `nick_name` varchar(20) DEFAULT NULL COMMENT '昵称',
  `is_admin` tinyint(4) DEFAULT NULL COMMENT '是否管理员 0:否  1:是',
  `phone` varchar(11) DEFAULT NULL COMMENT '手机',
  `gender` tinyint(4) DEFAULT NULL COMMENT '性别 0:保密 1:男 2:女',
  `birth` date DEFAULT NULL COMMENT '生日',
  `user_status` tinyint(4) DEFAULT NULL COMMENT '状态(是否激活) 0:否 1:是',
  `user_create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `user_update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `is_delete` tinyint(4) DEFAULT NULL COMMENT '是否删除 0:否 1:是',
  `is_member` tinyint(4) DEFAULT NULL COMMENT '是否会员 0:否 1:是',
  `balance` decimal(20,2) DEFAULT NULL COMMENT '账户余额',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='这是一个用户表';


insert  into `t_user`(`user_id`,`username`,`password`,`nick_name`,`is_admin`,`phone`,`gender`,`birth`,`user_status`,`user_create_time`,`user_update_time`,`is_delete`,`is_member`,`balance`) values 
(1,'aa','e10adc3949ba59abbe56e057f20f883e','666',1,'13566778899',1,'1990-07-18',0,'2021-10-15 10:56:40','2021-10-22 10:56:43',0,1,10000.00),
(2,'bb','bb','666',1,'111111',1,'1990-07-18',0,'2021-11-02 14:20:25','2021-11-03 09:25:58',0,1,1111.00),
(3,'cc','mark123','666',1,'13512341234',0,'1990-07-18',0,'2021-11-03 10:05:37','2021-11-03 10:05:37',0,1,10000.00),
(5,'dd','BB','666',1,'13512341234',0,'1990-07-18',0,'2021-11-03 10:15:00','2021-11-03 10:15:00',0,1,10000.00),
(6,'ee','CC','666',1,'13512341234',2,'1990-07-18',0,'2021-11-03 10:40:01','2021-11-03 10:40:01',0,1,10000.00),
(7,'ff','mark123','666',0,'13512341234',0,'1990-07-18',0,'2021-11-05 11:23:45','2021-11-05 11:23:45',0,1,10000.00);
  • 导入依赖
<dependencies>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>

    <!-- lombok : 能够快速帮我们生成实体的getter/setter方法 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
        <scope>provided</scope>
    </dependency>

</dependencies>
  • 实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Long userId;
    private String username;   //成员变量
    private String password;
    private String nickName;
    private Integer isAdmin;
    private String phone;
    private Integer gender;
    private Date birth;
    private Integer userStatus;
    private Date userCreateTime;
    private Date userUpdateTime;
    private Integer isDelete;
    private Integer isMember;
    private BigDecimal balance;

}
  • Mapper接口
public interface IUserMapper {

    /**
     * 查询所有
     * @return
     */
    List<User> findAll();

}
  • 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">
<!--
    mybatis代理接口开发的要求:
        1.mapper映射文件的namespace 写 mapper接口的全类名
        2.mapper映射文件的statementId 写 mapper接口的对应方法名
        3.mapper映射文件的resultType 写 mapper接口的对应方法返回值类型。如果是集合,写泛型
        4.mapper映射文件的parameterType 写 mapper接口的对应方法形参类型。高版本mybatis可以不写,但是不推荐
 -->
<mapper namespace="com.qf.java2107.mapper.IUserMapper">
    <select id="findAll" resultType="com.qf.java2107.pojo.User">
        <!-- 原生SQL -->
        SELECT
            user_id userId,
            username,
            password,
            nick_name,
            is_admin,
            phone,
            gender,
            birth,
            user_status,
            user_create_time,
            user_update_time,
            is_delete,
            is_member,
            balance
        FROM
            t_user
    </select>
</mapper>
  • 全局配置文件
<?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="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/java2107"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/qf/java2107/mapper/IUserMapper.xml"/>
    </mappers>
</configuration>
  • 测试代码
/**
 * mapper接口代理测试,掌握
 **/
@Test
public void test02() {
    SqlSession session = null;
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        session = sqlSessionFactory.openSession();
        //获取mapper代理
        IUserMapper userMapper = session.getMapper(IUserMapper.class);
        List<User> users = userMapper.findAll();
        //mybatis能够帮我们自动完成查询结果集和实体间的映射,前提结果集列名和实体属性名相同
        for (User user : users) {
            System.out.println(user);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(null != session) {
            session.close();
        }
    }
}

四、CURD操作

  • 增删改操作涉及事务,务必要提交

1、增加

  • Mapper接口
/**
 * 增加
 * @param user
 */
void save(User user);
  • Mapper映射文件
<!-- void save(User user);-->
<insert id="save" parameterType="com.qf.java2107.pojo.User">
    INSERT INTO t_user (
      username,password,nick_name,is_admin,phone,gender,birth,user_status,
      user_create_time,user_update_time,is_delete,is_member,balance)
    VALUES
      (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, #{userStatus},
      #{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance})
</insert>
  • 测试类
/**
 * 增加
 **/
@Test
public void test03() throws Exception {
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession sqlSession = factory.openSession();
    IUserMapper userMapper = sqlSession.getMapper(IUserMapper.class);
    User user = new User();
    user.setUsername("lucy");
    user.setPassword("lucy123");
    user.setNickName("露西");
    user.setIsAdmin(0);
    user.setPhone("13566778899");
    user.setGender(0);
    user.setBirth(java.sql.Date.valueOf("2005-10-30"));
    user.setUserStatus(1);
    user.setUserCreateTime(new Date());
    user.setUserUpdateTime(new Date());
    user.setIsDelete(0);
    user.setIsMember(1);
    user.setBalance(new BigDecimal(2000));
    userMapper.save(user);

    //提交事务
    sqlSession.commit();

    if(null != sqlSession) {
        sqlSession.close();
    }

}

1.1 主键回填

  • 主键自增

    • 方式一【Mapper映射文件】
    <!--void saveReturnPrimaryKey(User user);-->
    <insert id="saveReturnPrimaryKey" parameterType="com.qf.java2107.pojo.User">
    
        <!--
            selectKey : 是指要执行的相关的SQL
                order: selectKey中SQL的执行顺序,after代表之后,before代表之前
                resultType : selectKey中SQL的返回值类型
                keyProperty  : selectKey中SQL的返回值要赋值给哪个JavaBean的属性
                keyColumn : selectKey中SQL的返回值对应的数据库表的列【这个值可以不写,会自动映射】
        -->
        <selectKey order="AFTER" resultType="java.lang.Long" keyProperty="userId" keyColumn="user_id">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO t_user 
        	(username,password,nick_name,is_admin,phone,gender,birth,user_status,
          user_create_time,user_update_time,is_delete,is_member,balance)
        VALUES
            (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, 
        #{userStatus},#{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance})
    </insert>
    
    • 方式二
    <!-- void saveReturnPrimaryKey2(User user); -->
    <!--
        useGeneratedKeys : 是否使用主键生成策略
            true:是
        这种方式可以获取批量插入的主键值
     -->
    <insert id="saveReturnPrimaryKey2" parameterType="com.qf.java2107.pojo.User"
        useGeneratedKeys="true" keyProperty="userId" keyColumn="user_id">
        INSERT INTO t_user
            (username,password,nick_name,is_admin,phone,gender,birth,user_status,
            user_create_time,user_update_time,is_delete,is_member,balance)
        VALUES
        (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, #{userStatus},
        #{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance})
    </insert>
    

2、修改

<!--int update(User user);-->
<update id="update">
    UPDATE t_user
    SET PASSWORD = #{password}, nick_name = #{nickName}, user_update_time = #{userUpdateTime}
    WHERE user_id = #{userId}
</update>

3、删除

<!--boolean deleteById(Long userId);-->
<delete id="deleteById" parameterType="long">
    DELETE FROM t_user WHERE user_id = #{userId}
</delete>

4、主键是字符串实现主键回填

<img src=“pic/image-20211223144131489.png” alt="image-202112231441在这里插入图片描述
31489" style=“zoom:80%;” />
  • 实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author ghy
 * @version 1.0
 * @date
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Stu {

    private String stuId;
    private String stuName;

}
  • 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.qf.java2107.mapper.IStuMapper">

    <!-- int saveReturnPrimaryKey(Stu stu); -->
    <insert id="saveReturnPrimaryKey" parameterType="com.qf.java2107.pojo.Stu">
        <selectKey order="BEFORE" keyProperty="stuId" resultType="string" keyColumn="stu_id">
            SELECT REPLACE(UUID(), '-', '')
        </selectKey>
        insert into t_stu (stu_id, stu_name) values (#{stuId},#{stuName})
    </insert>

</mapper>

五、日志

1、日志体系

  • Slf4j:接口【门面】
    • Log4j
    • Logback
    • commons-logging

2、日志作用

  • 没日志:很难判断问题来源
  • 有日志:一般都可以判断问题来源。
    • 可以通过日志把问题错误信息给记录下来

3、使用

  • 引入依赖
<!-- 日志 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.7</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  • 日志配置文件【log4j.properties】
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=ssm.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=debug, stdout, file
  • 日志级别
级别描述
ALL LEVEL打开所有日志记录开关;是最低等级的,用于打开所有日志记录。
DEBUG输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。【开发使用】
INFO输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。【线上使用】
WARN输出警告信息;表明会出现潜在错误的情形。
ERROR输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。
OFF LEVEL关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。
  • 直接使用API即可
package com.qf.java2107.test;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-23
 **/
public class LoggerTest {

    Logger logger = LoggerFactory.getLogger(LoggerTest.class);

    /**
     *
     **/
    @Test
    public void logTest() throws Exception {
        logger.debug("debug---->{}", "debug level");
        logger.info("info--->{}---{}", "aa", "bb");
        logger.warn("warn----->");
        logger.error("error---->");
    }
}

4、常用插件

| 常用插件 |

--------------------------------------------在这里插入图片描述

| <img src="pic/image-202112在这里插入图片描述

六、Mapper接口参数绑定

1、单个简单类型参数

基本数据类型及其包装类,String

Mapper映射文件获取参数值时,名称可以随意。但是强烈建议参数名跟形参名一致
在这里插入图片描述

2、实体类型参数

JavaBean

Mapper映射文件获取参数值时,使用#{},{}中写JavaBean的属性名
在这里插入图片描述

3、Map入参

Mapper映射文件获取参数值时,使用#{},{}中写Map中key的名称
在这里插入图片描述

4、多个参数

mybatis会把参数封装成一个Map,第一个参数的key为arg0param1】,第二参数的key为arg1param2】,以此类推。

以上方式不推荐使用,可读性太差。

Mapper映射文件获取参数值时,使用@Param来为Mapper接口方法指定入参的参数名,使用#{}取值
在这里插入图片描述

七、ORM映射

把查询结果集跟JavaBean进行映射绑定

1、映射规则

  • 当查询结果集的列名跟JavaBean属性名相同时,自动映射

  • 当查询结果集的列名跟JavaBean属性名不相同时,需要手动映射

2、不满足映射规则

  • 如果满足驼峰

    • 使用别名进行映射【但是SQL太长】
    <!-- User findByIdMapping1(Long userId); -->
    <select id="findByIdMapping1" parameterType="java.lang.Long" resultType="com.qf.java2107.pojo.User">
        SELECT
            user_id userId,
            username,
            password,
            nick_name nickName,
            is_admin isAdmin,
            phone,
            gender,
            birth,
            user_status userStatus,
            user_create_time userCreateTime,
            user_update_time userUpdateTime,
            is_delete isDelete,
            is_member isMember,
            balance
        FROM
            t_user
        WHERE user_id = #{userId}
    </select>
    
    • 全局配置文件【mybatis-config.xml】开启驼峰映射
    <settings>
        <!-- 开启驼峰映射,编写的SQL就不用使用别名了 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
    <!-- User findByIdMapping2(Long userId); -->
    <select id="findByIdMapping2" parameterType="java.lang.Long" resultType="com.qf.java2107.pojo.User">
        SELECT
            user_id,
            username,
            password,
            nick_name,
            is_admin,
            phone,
            gender,
            birth,
            user_status,
            user_create_time,
            user_update_time,
            is_delete,
            is_member,
            balance
        FROM
            t_user
        WHERE user_id = #{userId}
    </select>
    

3、ResultMap

  • 自定义结果集映射
    • mybatis内部已经集成了结果集映射,就是Mapper映射文件的resultType属性。resultType底层使用其实也是ResultMap
    • resultType和resultMap最好只使用一个
      • 如果满足驼峰并且不使用别名的情况下,可以使用resultType
      • resultMap可以在查询结果集封装中自定义使用。连表查询只能使用resultMap
<!--
    resultMap: 自定义结果集
      id : resultMap的名称,是一个唯一标识,用于被select的resultMap属性所引用
      type : 查询的结果集要映射到的实体类型
 -->
<resultMap id="myResultMap" type="com.qf.java2107.pojo.User">
    <!--
        id : 映射主键列,只是一个标识作用,也可以用result
            column : 查询的结果集的列名
            property : JavaBean的属性名
    -->
    <id column="uid" property="userId"/>
    <!-- result : 映射普通列 -->
    <result column="username" property="username"/>
    <result column="password" property="password"/>
    <result column="name" property="nickName"/>
    <result column="is_admin" property="isAdmin"/>
    <result column="phone" property="phone"/>
    <result column="sex" property="gender"/>
    <result column="birth" property="birth"/>
    <result column="user_status" property="userStatus"/>
    <result column="ctime" property="userCreateTime"/>
    <result column="user_update_time" property="userUpdateTime"/>
    <result column="is_delete" property="isDelete"/>
    <result column="is_member" property="isMember"/>
    <result column="balance" property="balance"/>
</resultMap>

<!-- User findByIdUseResultMap(Long userId); -->
<select id="findByIdUseResultMap" parameterType="java.lang.Long" resultMap="myResultMap">
    SELECT
        user_id uid, username, password, nick_name name, is_admin, phone, gender sex, 
        birth, user_status, user_create_time ctime, user_update_time, is_delete, is_member, balance
    FROM
        t_user
    WHERE user_id = #{userId}
</select>
resultMap的使用
在这里插入图片描述

八、全局配置文件【了解】

  • 现在:会
  • 以后:配置文件不见了,放到Spring中
全局配置文件支持的标签
在这里插入图片描述

1、properties

<!--
    properties : 加载外部properties文件
        resource : properties文件基于classpath的路径
-->
<properties resource="jdbc.properties" />

2、settings

<!-- 全局设置 -->
<settings>
    <!-- 开启驼峰映射 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

3、typeAliases

内置别名【推荐使用】
在这里插入图片描述

| <img src="pic/image-2021122409542759在这里插入图片描述

  • 自定义别名
<!-- 别名设置 -->
<typeAliases>
    <!-- 单个取别名 -->
    <!--<typeAlias type="com.qf.java2107.pojo.User" alias="user"></typeAlias>
    <typeAlias type="com.qf.java2107.pojo.Student" alias="student"></typeAlias>-->
    <!-- 批量取别名,默认别名是类名,别名不区分大小写 -->
    <package name="com.qf.java2107.pojo"/>
</typeAliases>

4、plugins

4.1 分页插件概述

  • 可以屏蔽底层数据库的差异,实现同一API实现不同数据库的分页功能
  • https://gitee.com/free/Mybatis_PageHelper

4.2 使用步骤

  • 导入依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.10</version>
</dependency>
  • 全局配置文件
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 配置方言,不配置,则使用数据库连接来判断 -->
        <property name="helperDialect" value="mysql"/>
        <!-- 合理化参数 -->
        <property name="reasonable" value="true"/>
	</plugin>
</plugins>
  • 使用

    • Mapper映射文件
    <!-- List<User> findAll(); -->
    <select id="findAll" resultType="User">
        select * from t_user
    </select>
    
    • 测试代码
    /**
     * 分页
     **/
    @Test
    public void Test() throws Exception {
        IUserMapper userMapper = sqlSession.getMapper(IUserMapper.class);
    
        //分页设置跟查询之间不要出现其他操作
        //参数一:页码
        //参数二:显示条数
        PageHelper.startPage(3, 5);
        List<User> list = userMapper.findAll();
    
        PageInfo<User> pageInfo = new PageInfo<>(list);
        System.out.println("当前页:" + pageInfo.getPageNum());
        System.out.println("当前页集合:" + pageInfo.getList());
        System.out.println("总页数:" + pageInfo.getPages());
        System.out.println("总条数:" + pageInfo.getTotal());
        System.out.println("显示条数:" + pageInfo.getPageSize());  //显示条数
        System.out.println("实际显示条数:" + pageInfo.getSize());  //实际显示条数
    
    }
    

5、environments

  • 环境配置
<!-- 环境配置 -->
<!--
    default 默认使用哪个环境,这个值是指environments下的某个environment子标签的id属性
 -->
<environments default="mysqldb">
    <!--
        mysql环境
            id :就是当前环境的唯一标识,可能被environments的default引用
    -->
    <environment id="mysqldb">
        <!--
            transactionManager : 事务管理器
                type : JDBC
         -->
        <transactionManager type="JDBC"></transactionManager>
        <!--
            dataSource : 数据源
                type : POOLED 池化

         -->
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
    <!-- oracle环境 -->
    <!--<environment id="oracledb">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>-->
</environments>

6、mappers

  • 用来加载Mapper映射文件
<!-- 加载mapper映射文件 -->
<mappers>
    <!--
        mapper : 加载单个mapper映射文件
            resource :基于classpath路径下的mapper映射文件
            class : 基于mapper接口,mybatis注解开发方式。写mapper接口全类名
    -->
    <!--<mapper resource="com/qf/java2107/mapper/IUserMapper.xml" ></mapper>-->
    <!--
        批量加载mapper映射文件
           要求:mapper映射文件跟mapper接口在编译后必须在同一个路径下
    -->
    <package name="com.qf.java2107.mapper"/>
</mappers>

九、连表查询

1、表与表之间的关系

  • 数据库层面

    • 一对多:部门对员工、公司对部门
    • 多对多:项目跟程序员、学生跟老师
    • 多对一:学生对班级、员工对部门
    • 一对一:人跟身份证、旅客跟护照
  • mybatis层面

    • 一对多【多对多】
    • 一对一【多对一】

2、部门表跟员工表为例

  • 一对多:查询部门关联查询员工
  • 一对一:查询员工关联查询部门

2.1 准备工作

  • 数据库表
CREATE TABLE `t_department` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `dept_name` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

INSERT  INTO `t_department`(`id`,`dept_name`) VALUES 
(1,'研发部'),
(2,'市场部'),
(3,'财务部'),
(4,'测试部');


CREATE TABLE `t_employee` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `emp_name` VARCHAR(20) NOT NULL,
  `gender` INT(1) DEFAULT NULL COMMENT '1:男 0:女',
  `birthday` DATE DEFAULT NULL,
  `hire_date` DATETIME DEFAULT NULL,
  `salary` INT(11) DEFAULT NULL,
  `address` VARCHAR(200) DEFAULT NULL,
  `dept_id` INT(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;


INSERT  INTO `t_employee`(`id`,`emp_name`,`gender`,`birthday`,`hire_date`,`salary`,`address`,`dept_id`) VALUES 
(1,'宝宝',0,'2000-10-31','2021-10-01 09:00:00',8000,'杭州江干',2),
(2,'李四',1,'1985-11-10','2006-12-12 18:10:10',9000,'李家村',1),
(3,'王五',0,'1991-10-02','2009-12-02 00:00:00',1500,'王家村',2),
(5,'测试更新2',1,'2021-10-04','2021-11-11 00:00:00',15000,'杭州',2),
(7,'大CC',1,'2019-05-12','2021-11-07 00:00:00',6000,'杭州',2),
(8,'DD',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(9,'九妹',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',NULL),
(10,'萧炎',1,'1999-12-12','2021-02-03 00:00:00',5000,'牛田村',1),
(11,'萧媚',1,'1985-11-11','2006-12-12 00:00:00',9000,'李家村',1),
(12,'小医仙',0,'1991-10-02','2009-12-02 00:00:00',1500,'王家村',2),
(13,'林动',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(14,'凌青竹',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(15,'纪宁',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',2),
(16,'北冥',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(17,'叶伏天',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',NULL),
(18,'余生',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',2),
(19,'花解语',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',1),
(100,'张三',1,'1999-12-12','2021-02-03 00:00:00',5000,'牛田村',1);
部门表跟员工表
在这里插入图片描述
  • 实体
部门
在这里插入图片描述
员工
在这里插入图片描述

2.2 一对多

  • 根据ID查询部门信息且关联员工信息
2.2.1 修改部门实体
修改部门实体
在这里插入图片描述
2.2.2 Mapper接口和Mapper映射文件
Mapper接口和Mapper映射文件
在这里插入图片描述
  • Mapper接口
public interface IDepartmentMapper {

    Department findByIdAndEmps(Integer id);

}
  • Mapper映射文件
    <resultMap id="DeptAndEmpsResultMap" type="com.qf.java2107.pojo.Department" extends="BaseResultMap">

        <!-- 映射一对多【集合】 -->
        <!--
            collection: 映射一对多【集合】
                property: 集合名
                ofType : 集合中的元素类型
         -->
        <collection property="employees" ofType="com.qf.java2107.pojo.Employee">
            <!-- 映射单个员工 -->
            <id column="emp_id" property="id"/>
            <result column="emp_name" property="empName"/>
            <result column="gender" property="gender"/>
            <result column="birthday" property="birthday"/>
            <result column="hire_date" property="hireDate"/>
            <result column="salary" property="salary"/>
            <result column="address" property="address"/>
            <result column="dept_id" property="deptId"/>
        </collection>
    </resultMap>

    <resultMap id="BaseResultMap" type="com.qf.java2107.pojo.Department">
        <id column="id" property="id"/>
        <result column="dept_name" property="deptName"/>
    </resultMap>

    <!-- Department findByIdAndEmps(Integer id); -->
    <select id="findByIdAndEmps" parameterType="int" resultMap="DeptAndEmpsResultMap">
        SELECT
           d.id,
           d.dept_name,
           e.id emp_id,
           e.emp_name,
           e.gender,
           e.birthday,
           e.hire_date,
           e.salary,
           e.address,
           e.dept_id
        FROM
        t_department d, t_employee e
        WHERE d.id = e.dept_id
        AND d.id = #{id}
    </select>

2.3 一对一

  • 根据ID查询员工信息且关联部门信息
2.3.1 修改员工实体
修改员工实体
在这里插入图片描述
2.3.2 Mapper接口和Mapper映射文件
Mapper接口和Mapper映射文件
在这里插入图片描述
  • Mapper接口
public interface IEmployeeMapper {

    Employee findByIdAndDept(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">

<mapper namespace="com.qf.java2107.mapper.IEmployeeMapper">

    <resultMap id="BaseResultMap" type="com.qf.java2107.pojo.Employee">
        <id column="id" property="id"/>
        <result column="emp_name" property="empName"/>
        <result column="gender" property="gender"/>
        <result column="birthday" property="birthday"/>
        <result column="hire_date" property="hireDate"/>
        <result column="salary" property="salary"/>
        <result column="address" property="address"/>
        <result column="dept_id" property="deptId"/>
    </resultMap>

    <resultMap id="EmpAndDeptResultMap" type="com.qf.java2107.pojo.Employee" extends="BaseResultMap">
        <!--
            association : 映射实体
                property : 实体属性名
                javaType : 实体属性全类名
            -->
        <association property="department" javaType="com.qf.java2107.pojo.Department">
            <id column="did" property="id"/>
            <result column="dept_name" property="deptName"/>
        </association>
    </resultMap>

    <!-- Employee findByIdAndDept(Integer id); -->
    <select id="findByIdAndDept" parameterType="int" resultMap="EmpAndDeptResultMap">
        SELECT
           e.id id,
           e.emp_name,
           e.gender,
           e.birthday,
           e.hire_date,
           e.salary,
           e.address,
           e.dept_id,
           d.id did,
           d.dept_name
        FROM
        t_employee e JOIN t_department d
        ON e.dept_id = d.id
        WHERE e.id = #{id}
    </select>
</mapper>

十、分步查询、延迟加载

1、分步查询

  • 是连表查询的另一种方式
    • 把连表查询的SQL进行拆分出多个单表查询的SQL

2、一对多

  • 查询部门信息关联查询员工信息

2.1 IDeparmentMapper映射文件

<!-- =================分步查询====================== -->
<resultMap id="DeptAndEmpStepQueryResultMap" type="com.qf.java2107.pojo.Department">
    <id column="id" property="id"/>
    <result column="dept_name" property="deptName"/>
    <!-- 映射集合 -->
    <collection property="employees" ofType="com.qf.java2107.pojo.Employee"
        select="com.qf.java2107.mapper.IEmployeeMapper.findByDeptId" column="id">
    </collection>
</resultMap>

<!--Department findByIdUseStepQuery(Integer id);-->
<select id="findByIdUseStepQuery" parameterType="int" resultMap="DeptAndEmpStepQueryResultMap">
    SELECT id, dept_name FROM t_department WHERE id = #{id}
</select>

2.2 IEmployeeMapper映射文件

<!--==================分步查询相关======================== -->
<!--List<Employee> findByDeptId(Integer deptId);-->
<select id="findByDeptId" parameterType="int" resultMap="BaseResultMap">
    SELECT * FROM t_employee WHERE dept_id = #{deptId}
</select>

2.3 执行流程

执行流程
在这里插入图片描述

3、延迟加载【面试题】

也叫懒加载,也叫按需加载

当需要使用到关联的数据时,才去执行查询操作

实现原理:Cglib动态代理【基于继承】

延迟加载只会出现在分步查询中。一般延迟加载的数据都是大数据【如集合】

  • 即时加载:先执行完所有的SQL,再打印数据
  • 延迟加载,先执行需要获取数据的SQL,再打印数据。后面如果还需要获取数据,再执行SQL,再打印数据
3.1 具体实现
  • 全局配置文件开启延迟加载的开头
全局配置文件开启延迟加载的开头
在这里插入图片描述
  • DepartmentMapper接口和映射文件
DepartmentMapper接口和映射文件
在这里插入图片描述
  • 测试
即时加载
在这里插入图片描述
延迟加载
在这里插入图片描述

十一、动态SQL

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

1、if

  • 条件判断
    • 成立,就拼接SQL

2、where

  • 功能相当于数据库的where关键字
<!-- List<Employee> findWithIf(Employee employee); -->
<select id="findWithIf" parameterType="com.qf.java2107.pojo.Employee" resultType="com.qf.java2107.pojo.Employee">
    SELECT * FROM t_employee
    <!--WHERE 1=1-->
    <!-- where 会忽略条件成立的最前面的多余的and或者or -->
    <where>
        <if test="empName != null and empName.trim() != ''">
            AND emp_name LIKE #{empName}
        </if>
        <if test="salary != null and salary > 0">
            AND salary = #{salary}
        </if>
        <if test="gender == 0 || gender == 1">
            AND gender = #{gender}
        </if>
        <if test="deptId != null">
            AND dept_id = #{deptId} 
        </if>
    <!-- 除SQL之外的地方都是JavaBean的属性名 -->
    </where>
</select>

3、trim【理解】

  • 可以自定义的拼接或去掉SQL片段前后缀
    <!-- List<Employee> findWithTrim(Employee employee); -->
    <select id="findWithTrim" parameterType="com.qf.java2107.pojo.Employee" resultType="com.qf.java2107.pojo.Employee">
        SELECT * FROM t_employee
        <!--
            prefix : 要加的前缀
            prefixOverrides : 要去掉的前缀
            suffix  : 要加的后缀
            suffixOverrides : 要去掉的后缀
        -->
        <!--<trim prefix="where" prefixOverrides="and | or" suffix="" suffixOverrides="">
            <if test="empName != null and empName.trim() != ''">
                AND emp_name LIKE #{empName}
            </if>
            <if test="salary != null and salary > 0">
                AND salary = #{salary}
            </if>
            <if test="gender == 0 || gender == 1">
                AND gender = #{gender}
            </if>
            <if test="deptId != null">
                AND dept_id = #{deptId}
            </if>
        </trim>-->

        <trim prefix="where" prefixOverrides="" suffix="" suffixOverrides="AND | OR">
            <if test="empName != null and empName.trim() != ''">
                 emp_name LIKE #{empName} AND
            </if>
            <if test="salary != null and salary > 0">
                 salary = #{salary} AND
            </if>
            <if test="gender == 0 || gender == 1">
                 gender = #{gender} AND
            </if>
            <if test="deptId != null">
                 dept_id = #{deptId} AND
            </if>
        </trim>

    </select>

4、forEach

<!-- List<Employee> findByIds(List<Integer> ids); -->
<select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
    <!--SELECT * FROM t_employee WHERE id IN (1,25,3,62)-->
    SELECT * FROM t_employee
    <where>
        <if test="list != null and list.size() > 0">
            <!--id IN (1,25,3,62)-->
            <!--
                collection : 要遍历的集合,可以使用别名,但是如果使用了@Param指定入参key,那么就指定这个key
                item : 正在迭代的元素名,自己起名
                separator : 元素之间的分隔符
                open : 要遍历的元素开始之前的SQL片段
                close : 要遍历的元素结束之后的SQL片段
            -->
            <foreach collection="list" item="id" separator="," open="id IN (" close=")">
                #{id}
            </foreach>
        </if>
    </where>
</select>

5、choose…when…otherwise【了解】

<!-- List<Employee> findWithChoose(Employee employee); -->
<select id="findWithChoose" parameterType="com.qf.java2107.pojo.Employee" resultType="com.qf.java2107.pojo.Employee">
    SELECT * FROM t_employee
    <where>
        <choose>
            <when test="empName != null and empName.trim() != ''">
                AND emp_name LIKE #{empName}
            </when>
            <when test="salary != null and salary > 0">
                AND salary = #{salary}
            </when>
            <when test="gender == 0 or gender == 1">
                AND gender = #{gender}
            </when>
            <otherwise>
                dept_id = 1
            </otherwise>
        </choose>
    </where>
</select>

6、sql…include

    <!-- List<Employee> findByIds(List<Integer> ids); -->
    <select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
        <include refid="BaseSelect"></include>
        <where>
            <if test="list != null and list.size() > 0">
                <foreach collection="list" item="id" separator="," open="id IN (" close=")">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
    <sql id="BaseSelect">
        SELECT
         <include refid="BaseColumn"></include>
         FROM t_employee
    </sql>

    <sql id="BaseColumn">
        id,
        emp_name,
        gender,
        birthday,
        hire_date,
        salary,
        address,
        dept_id
    </sql>

7、set

  • 仅用于更新,跟if配套使用
<!--  int updateWithSet(Employee employee); -->
<update id="updateWithSet" parameterType="Employee">
    update t_employee
    <set>
        <if test="empName != null and empName.trim() != ''">
            emp_name = #{empName},
        </if>
        <if test="salary != null and salary > 0">
            salary = #{salary},
        </if>
        <if test="gender == 0 or gender == 1">
            gender = #{gender},
        </if>
    </set>
    where id = #{id}
</update>

十二、缓存机制【理解】

  • 作用
    • 提高查询效率
    • 减轻数据库访问压力

1、分类

  • 一级缓存
  • 二级缓存

2、区别

  • 一级缓存
    • 级别:SqlSession
    • 存储介质:内存
    • 存储类型:对象副本
    • 失效情况

  • 二级缓存
    • 级别:NameSpace
    • 存储介质:磁盘
    • 存储类型:散装数据
    • 触发条件

3、Mybatis执行查询流程

Mybatis执行查询流程
在这里插入图片描述

4、一级缓存

  • 一级缓存默认开启,我们无法关闭他

  • 在执行两次相同的查询时,第一次会向数据库发送SQL,并且写入一份到一级缓存中,那么后面的查询操作就直接从缓存中获取数据。不会向数据库发送SQL

  • 一级缓存失效的情况

    • 不是同一个SqlSession
    • 两次相同的查询中间执行增删改
    • 两次相同的查询中间手动清空缓存
    • 两次相同的查询中间手动提交事务
@Test
public void firstLevelCacheTest() throws Exception {
   IEmployeeMapper employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
   Employee employee1 = employeeMapper.findById(10);
   System.out.println(employee1);


   //1.两个SqlSession
   //sqlSession = factory.openSession();
   //employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);

   //2.执行增删改
   //employeeMapper.deleteById(111111);

   //3.手动清空缓存
   //sqlSession.clearCache();

   //4.手动提交事务
   sqlSession.commit();

   Employee employee2 = employeeMapper.findById(10);
   System.out.println(employee2);

   System.out.println(employee1 == employee2);

}

5、二级缓存

  • 二级缓存默认开启,我们可以通过配置文件对其关闭
  • 二级缓存
    • 必须在SqlSession关闭之后,数据才会被写入到二级缓存中。
    • 存储介质是磁盘,所以写入的字节数据,那么要被写入的对象所在的类必须实现Serializable接口
  • 实现步骤
    • 全局配置文件开启
    • 在要使用二级缓存的namespace中配置一个<cache/>标签
    • 关闭SqlSession
/**
 * 二级缓存
 **/
@Test
public void secondLevelCacheTest() throws Exception {
    IEmployeeMapper employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
    Employee employee1 = employeeMapper.findById(10);
    System.out.println(employee1);

    sqlSession.close();
    sqlSession = factory.openSession();
    employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
    Employee employee2 = employeeMapper.findById(10);
    System.out.println(employee2);

    System.out.println(employee1 == employee2);

}

十三、#{}和${}的区别

  • #{}
    • 在填充参数时,是通过?占位符的方式,能够避免SQL注入
    • 只是获取跟数据库表列相关的值
  • ${}
    • 在填充参数时,使用的是直接进行字符串拼接,会有SQL注入的风险
    • 使用其可以操作非数据库表列的取值

优先选择#{}取值,如果不行,则使用${}

十四、注解开发【会用】

  • 注解开发跟配置文件开发,选择一种
package com.qf.java2107.mapper;

import com.qf.java2107.pojo.Department;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-27
 **/
public interface IDepartmentMapper {

    @Insert("insert into t_department (dept_name) values (#{deptName})")
    @SelectKey(statement = "select last_insert_id()",
            keyProperty = "id",
            keyColumn = "dept_id",
            before = false,
            resultType = int.class)
    int save(Department department);

    /**
     * @Results 等同于配置文件的resultMap标签
     *          id 等同于配置文件的resultMap标签中id属性
     *
     *  @Result : 映射单个属性,用boolean来区分是否是主键映射
     */
    @Results(
            id = "BaseResultMap",
            value = {
                @Result(id = true, column = "id", property = "id"),
                @Result(id = false, column = "dept_name", property = "deptName")
            }
    )
    @Select("select id, dept_name from t_department")
    List<Department> findAll();

    @ResultMap("BaseResultMap")  //引用其他已经定义好的ResultMap
    @Select("select id, dept_name from t_department where id = #{id}")
    Department findById(Integer id);

}
  • 分步查询【IEmployeeAnnoMapper.class】
package com.qf.java2107.mapper;

import com.qf.java2107.pojo.Employee;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-27
 **/
public interface IEmployeeAnnoMapper {

    /**
     * 根据ID查询员工关联部门
     * @param id
     * @return
     */
    @Results({
            @Result(id = true, column = "id", property = "id"),
            @Result(id = false, column = "emp_name", property = "empName"),
            @Result(id = false, column = "gender", property = "gender"),
            @Result(id = false, column = "birthday", property = "birthday"),
            @Result(id = false, column = "hire_date", property = "hireDate"),
            @Result(id = false, column = "salary", property = "salary"),
            @Result(id = false, column = "address", property = "address"),
            @Result(id = false, column = "dept_id", property = "deptId"),
            @Result(id = false, property = "dept", column = "dept_id",
                    //one 一对一映射
                    one = @One(select = "com.qf.java2107.mapper.IDepartmentMapper.findById",
                            fetchType = FetchType.LAZY))
    })
    @Select("select * from t_employee where id = #{id}")    //dept_id
    Employee findByIdUseStep(Integer id);

}

十五、源码分析【听一遍】

1、查询

  • 查询单个
    • DefaultSqlSession的selectOne方法
    • DefaultSqlSession的selectList方法,得到结果后,进行集合数量判断,如果是1,直接返回。>1,否则就报错。0返回null
      • CachingExecutor的query方法
        • BaseExecutor的query方法
          • BaseExecutor的queryFromDatabase方法
            • BaseExecutor的doQuery方法
              • 通过SimpleExecutor中去调用JDBC的execute()
  • 查询集合
    • DefaultSqlSession的selectList方法,得到结果后,直接返回
      • CachingExecutor的query方法
        • BaseExecutor的query方法
          • BaseExecutor的queryFromDatabase方法
            • BaseExecutor的doQuery方法
              • 通过SimpleExecutor中去调用JDBC的execute()

2、增删改方法

  • 修改
    • DefaultSqlSession的update方法
      • CachingExecutor的update方法:会清空缓存操作
        • BaseExecutor的doUpdate方法
          • 通过SimpleExecutor中去调用JDBC的execute()
  • 增加、删除
    • 先调用DefaultSqlSession的insert方法或者delete方法
      • 接下来就会执行DefaultSqlSession的update方法

十六、面试题

1、Mybatis支持延迟加载吗?

2、#{}和${}的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XEsxGmym-1654564113636)(C:\Users\李泽楷\AppData\Roaming\Typora\typora-user-images\1654067290282.png)]

3、Mybatis应用到的设计模式

  • 构建者模式
  • 工厂模式
  • 代理模式

4、Mybatis的Mapper接口是否支持方法重载

  • 不支持
    • mapper接口的方法名就是mapper映射文件的statementId,是用来获取执行SQL的唯一标识

5、Mybatis的Mapper映射文件的标签,你知道哪些?

  • 除之前讲的之外
    • sql:抽取的SQL片段
    • include:引用抽取的SQL片段
    <!-- List<Employee> findByIds(List<Integer> ids); -->
    <select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
        <include refid="BaseSelect"></include>
        <where>
            <if test="list != null and list.size() > 0">
                <foreach collection="list" item="id" separator="," open="id IN (" close=")">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
    <sql id="BaseSelect">
        SELECT
         <include refid="BaseColumn"></include>
         FROM t_employee
    </sql>

    <sql id="BaseColumn">
        id,
        emp_name,
        gender,
        birthday,
        hire_date,
        salary,
        address,
        dept_id
    </sql>

十七、Mybatis Generator插件

  • 一个业务实体,基本都有CRUD操作。

    • 这些写多了就都是体力活。这些重复性的代码,写多了是对技术毫无帮助。
    • 已经有了很多的代码生成器,能够帮我们生成这些,无需我们自己写
  • 一个完整的项目

    • 会改其中的配置,生成代码即可
  • 代码

    • 参考工程
  • 学习

    • 参考视频

    • DefaultSqlSession的selectList方法,得到结果后,直接返回

      • CachingExecutor的query方法
        • BaseExecutor的query方法
          • BaseExecutor的queryFromDatabase方法
            • BaseExecutor的doQuery方法
              • 通过SimpleExecutor中去调用JDBC的execute()

2、增删改方法

  • 修改
    • DefaultSqlSession的update方法
      • CachingExecutor的update方法:会清空缓存操作
        • BaseExecutor的doUpdate方法
          • 通过SimpleExecutor中去调用JDBC的execute()
  • 增加、删除
    • 先调用DefaultSqlSession的insert方法或者delete方法
      • 接下来就会执行DefaultSqlSession的update方法

十六、面试题

1、Mybatis支持延迟加载吗?

2、#{}和${}的区别

[外链图片转存中…(img-XEsxGmym-1654564113636)]

3、Mybatis应用到的设计模式

  • 构建者模式
  • 工厂模式
  • 代理模式

4、Mybatis的Mapper接口是否支持方法重载

  • 不支持
    • mapper接口的方法名就是mapper映射文件的statementId,是用来获取执行SQL的唯一标识

5、Mybatis的Mapper映射文件的标签,你知道哪些?

  • 除之前讲的之外
    • sql:抽取的SQL片段
    • include:引用抽取的SQL片段
    <!-- List<Employee> findByIds(List<Integer> ids); -->
    <select id="findByIds" parameterType="list" resultType="com.qf.java2107.pojo.Employee">
        <include refid="BaseSelect"></include>
        <where>
            <if test="list != null and list.size() > 0">
                <foreach collection="list" item="id" separator="," open="id IN (" close=")">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
    <sql id="BaseSelect">
        SELECT
         <include refid="BaseColumn"></include>
         FROM t_employee
    </sql>

    <sql id="BaseColumn">
        id,
        emp_name,
        gender,
        birthday,
        hire_date,
        salary,
        address,
        dept_id
    </sql>

十七、Mybatis Generator插件

  • 一个业务实体,基本都有CRUD操作。

    • 这些写多了就都是体力活。这些重复性的代码,写多了是对技术毫无帮助。
    • 已经有了很多的代码生成器,能够帮我们生成这些,无需我们自己写
  • 一个完整的项目

    • 会改其中的配置,生成代码即可
  • 代码

    • 参考工程
  • 学习

    • 参考视频
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值