mybatis入门到出家

一、JDBC

1.JDBC介绍

JDBC全称Java DataBase Connectivity,即Java数据库连接,集成在JDK中,提供基础的数据库访问API。

2.JDBC使用

/**
 * 通过JDBC查询用户信息
 */
public void queryUser(Integer id) {
    Connection conn = null;
    Statement stmt = null;

    try {
        // 1.获取连接
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/baiTest?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false", "root", "123456");
        // 2.创建statement
        stmt = conn.createStatement();
        String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where id = " + id;
        // 3.执行脚本
        ResultSet rs = stmt.executeQuery(sql);
        // 4.获取结果集
        while (rs.next()) {
            User user = new User();
            Integer uId = rs.getInt("id");
            String userName = rs.getString("user_name");
            String realName = rs.getString("real_name");
            Integer age = rs.getInt("age");
            String password = rs.getString("password");
            Integer did = rs.getInt("d_id");
            user.setId(uId);
            user.setUserName(userName);
            user.setRealName(realName);
            user.setAge(age);
            user.setPassword(password);
            user.setDId(did);

            System.out.println(user);
        }
        //5.关闭资源
        rs.close();
        stmt.close();
        conn.close();
    } catch (SQLException se) {
        se.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (stmt != null) {
                stmt.close();
            }
        } catch (SQLException se2) {
        }
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException se) {
            se.printStackTrace();
        }
    }
}
  1. getConnection创建连接;
  2. 创建statement;
  3. 调用excute()执行sql语句;
  4. 获取到ResultSet结果集,然后给pojo赋值;
  5. 关闭资源

显然傻瓜式的操作存在诸多弊端 :代码重复、资源管理、pojo对象映射、sql耦合

二、Spring JDBC

在Spring框架平台下,也提供的有JDBC的封装操作,在Spring中提供了一个模板方法JdbcTemplate,里面封装了各种各样的 execute,query和update方法。

Instances of the JdbcTemplate class are thread-safe, once configured. This is important because it means that you can configure a single instance of a JdbcTemplate and then safely inject this shared reference into multiple DAOs (or repositories). The JdbcTemplate is stateful, in that it maintains a reference to a DataSource, but this state is not conversational state.

JdbcTemplate这个类是JDBC的核心包的中心类,简化了JDBC的操作,可以避免常见的异常,它封装了JDBC的核心流程,应用只要提供SQL语句,提取结果集就可以了,这玩意线程安全,记住这点很重要。

步骤简化了不少:

  1. 配置数据源,resource下新建druid.properties
  2. 注入template
@Configuration
@ComponentScan
public class SpringConfig {
    @Bean
    public DataSource dataSource() {
        Properties properties = new Properties();
        //获取数据源配置
        InputStream in = SpringConfig.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //使用Druid连接(也可以用MysqlDataSource替换)
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.configFromPropety(properties);
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate template = new JdbcTemplate();
        template.setDataSource(dataSource);
        return template;
    }
}

三、Mybatis概览

1.什么是 MyBatis?

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

2.前世今生

原是apache的一个开源项目iBatis, 2010年6月这个项目由apache software foundation迁移到了google code,随着开发团队转投Google Code旗下,ibatis3.x正式更名为Mybatis ,代码于2013年11月迁移到Github

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL MapsData Access Objects(DAO)

3.mybatis架构

在这里插入图片描述

3.1 接口层

首先接口层是我们打交道最多的。核心对象是SqlSession,它是上层应用和MyBatis打交道的桥梁,SqlSession上定义了非常多的对数据库的操作方法。接口层在接收到调用请求的时候,会调用核心处理层的相应模块来完成具体的数据库操作。

3.2 核心处理层

接下来是核心处理层。既然叫核心处理层,也就是跟数据库操作相关的动作都是在这一层完成的。核心处理层主要做了这几件事:

  1. 把接口中传入的参数解析并且映射成JDBC类型;
  2. 解析xml文件中的SQL语句,包括插入参数,和动态SQL的生成;
  3. 执行SQL语句;
  4. 处理结果集,并映射成Java对象。插件也属于核心层,这是由它的工作方式和拦截的对象决定的。
3.3 基础支持层

最后一个就是基础支持层。基础支持层主要是一些抽取出来的通用的功能(实现复用),用来支持核心处理层的功能。比如数据源、缓存、日志、xml解析、反射、IO、事务等等这些功能

四、Mybatis应用

1.编译环境

1.1 下载源码

git clone https://github.com/mybatis/parent
git clone https://github.com/mybatis/mybatis-3

在这里插入图片描述

1.2 编译打包

  • 用idea分别对parent和mybatis-3打包

  • 对mybatis打包为了辨识可以在版本version上加snapshot

  • 这样本地仓库中就有了mybatis/3.5.4-snapshot的jar包

  • 引入jar包即可

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4-snapshot</version>
    </dependency>
    

1.3 关联源码

关联源码的目的是可以方便对源码进行编辑、注释。

1.3.1 Project Structure→Libraries,点击+

在这里插入图片描述

1.3.2 找到源码项目,添加

在这里插入图片描述
ok!!!

2 项目构建

2.1 db环境

2.1.1 本地环境准备

需要准备mysql和redis,本地搭建即可。此处省略,建议使用docker安装:https://docs.docker.com/docker-for-windows/install/

mysqlredis
urllocalhost:3306localhost:6379
数据库test_db-
账号root-
密码123456-
2.1.2 添加表
CREATE TABLE `t_department` (
	`did` INT(10) NOT NULL AUTO_INCREMENT,
	`d_name` VARCHAR(30) NOT NULL DEFAULT '' COLLATE 'utf8mb4_0900_ai_ci',
	`d_desc` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8mb4_0900_ai_ci',
	PRIMARY KEY (`did`) USING BTREE
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
AUTO_INCREMENT=10002;

CREATE TABLE `t_user` (
	`id` INT(10) NOT NULL AUTO_INCREMENT,
	`user_name` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8mb4_0900_ai_ci',
	`real_name` VARCHAR(30) NOT NULL DEFAULT '' COLLATE 'utf8mb4_0900_ai_ci',
	`password` VARCHAR(50) NOT NULL DEFAULT '1234' COLLATE 'utf8mb4_0900_ai_ci',
	`age` INT(10) NOT NULL DEFAULT '0',
	`d_id` INT(10) NOT NULL DEFAULT '10000',
	PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
AUTO_INCREMENT=5;

2.2 创建maven项目

new->project->maven->next

2.3 添加依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.4-snapshot</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>

2.4 配置文件

位于resources目录

2.4.1 添加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="db.properties"></properties>
    <settings>
        <!-- 打印查询语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>

        <!-- 控制全局缓存(二级缓存),默认 true-->
        <setting name="cacheEnabled" value="true"/>

        <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false ,可通过select标签的 fetchType来覆盖 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
        <!--<setting name="proxyFactory" value="CGLIB" />-->
        <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
        <!--
                <setting name="localCacheScope" value="STATEMENT"/>
        -->
        <setting name="localCacheScope" value="SESSION"/>
    </settings>

    <!-- 定义***Mapper.xml中resultMap的类型别名(别名的好处就是避免重复)-->
    <typeAliases>
        <typeAlias type="com.byron.tuhu.domain.User" alias="user"></typeAlias>
        <typeAlias type="com.byron.tuhu.domain.Dept" alias="dept"></typeAlias>
    </typeAliases>

    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名(要使用的话需添加pom依赖) -->
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
            <!-- 该参数默认为false -->
            <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
            <!-- 和startPage中的pageNum效果一样 -->
            <property name="offsetAsPageNum" value="true"/>
            <!-- 该参数默认为false -->
            <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
            <property name="rowBoundsWithCount" value="true"/>
            <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
            <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型) -->
            <property name="pageSizeZero" value="true"/>
            <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
            <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
            <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
            <property name="reasonable" value="false"/>
            <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
            <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
            <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 -->
            <!-- 不理解该含义的前提下,不要随便复制该配置 -->
            <property name="params" value="pageNum=start;pageSize=limit;"/>
            <!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
            <property name="returnPageInfo" value="check"/>
        </plugin>
    </plugins>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
            <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>

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

</configuration>
2.4.2 添加db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test_db?characterEncoding=utf-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false
jdbc.username=root
jdbc.password=123456
2.4.3 添加UserMapper.xml
  • namespace对应的是mapper接口的全路径
  • mapper中接口名以及参数和xml中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.byron.tuhu.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="user">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR" />
        <result property="realName" column="real_name" jdbcType="VARCHAR" />
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
        <result property="dId" column="d_id" jdbcType="INTEGER"/>
    </resultMap>

    <sql id="baseSQL">
        id,user_name,real_name,password,age,d_id
    </sql>

    <select id="selectUserById" resultType="user" statementType="PREPARED"  >
        select
         id,
         user_name userName,
         real_name realName,
         password,
         age,
         d_id
         from t_user where id = #{id}
    </select>

    <!-- $只能用在自定义类型和map上 -->
    <select id="selectUserByBean"  parameterType="user" resultMap="BaseResultMap" >
        select * from t_user where user_name = '${userName}'
    </select>

    <select id="selectUserList" resultMap="BaseResultMap" >
        select * from t_user
    </select>

    <select id="queryUserList" resultMap="BaseResultMap" >
        select * from t_user
    </select>

    <insert id="insertUser" parameterType="user">
        insert into t_user(user_name,real_name)values(#{userName},#{realName})
    </insert>

    <!-- 批量插入
        insert into t_user() values (),(),(),()
    -->
    <insert id="insertUserList" parameterType="java.util.List" >
        insert into t_user(user_name,real_name)
        values
        <foreach collection="list" item="user" separator=",">
            (#{user.userName},#{user.realName})
        </foreach>

    </insert>

    <update id="updateUserList">
        update t_user set
        user_name =
        <foreach collection="list" item="user" index="index" separator=" " open="case id" close="end">
            when #{user.id} then #{user.userName}
        </foreach>
        ,real_name =
        <foreach collection="list" item="user" index="index" separator=" " open="case id" close="end">
            when #{user.id} then #{user.realName}
        </foreach>
        where id in
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item.id,jdbcType=INTEGER}
        </foreach>
    </update>

    <delete id="deleteByList" parameterType="java.util.List">
        delete from t_user where id in
        <!-- ( 1 , 2  , 3) -->
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item.id,jdbcType=INTEGER}
        </foreach>
    </delete>


    <!-- if 的使用 -->
    <select id="selectListIf" parameterType="user" resultMap="BaseResultMap" >
        select
        <include refid="baseSQL"></include>
        from t_user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="userName != null">
                and user_name = #{userName}
            </if>
        </where>
    </select>

    <!-- choose 的使用 -->
    <select id="selectListChoose" parameterType="user" resultMap="BaseResultMap" >
        select
        <include refid="baseSQL"></include>
        from t_user
        <where>
            <choose>
                <when test="id != null">
                    id = #{id}
                </when>
                <when test="userName != null and userName != ''">
                    and user_name like CONCAT(CONCAT('%',#{userName,jdbcType=VARCHAR}),'%')
                </when>
                <otherwise>

                </otherwise>
            </choose>
        </where>
    </select>

    <!--
        trim 的使用
        替代where标签的使用
    -->
    <select id="selectListTrim" resultMap="BaseResultMap"
            parameterType="user">
        select <include refid="baseSQL"></include>
        <!-- <where>
            <if test="username!=null">
                and name = #{username}
            </if>
        </where> -->
        <trim prefix="where" prefixOverrides="AND |OR ">
            <if test="userName!=null">
                and user_name = #{userName}
            </if>
            <if test="age != 0">
                and age = #{age}
            </if>
        </trim>
    </select>

    <!-- 替代set标签的使用 -->
    <update id="updateUser" parameterType="User">
        update t_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">
                user_name = #{userName},
            </if>
            <if test="age != 0">
                age = #{age}
            </if>
        </trim>
        where id=#{id}
    </update>

    <!-- 嵌套查询 1对1 1个用户对应一个部门-->
    <resultMap id="nestedMap1" type="user">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR" />
        <result property="realName" column="real_name" jdbcType="VARCHAR" />
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
        <result property="dId" column="d_id" jdbcType="INTEGER"/>
        <association property="dept" javaType="dept">
            <id column="did" property="dId"/>
            <result column="d_name" property="dName"/>
            <result column="d_desc" property="dDesc"/>
        </association>
    </resultMap>

    <select id="queryUserNested" resultMap="nestedMap1">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t2.`did`
            ,t2.`d_name`
            ,t2.`d_desc`
        FROM t_user t1
        LEFT JOIN
            t_department t2
            ON t1.`d_id` = t2.`did`
    </select>

    <!-- 延迟加载 1对1 -->
    <resultMap id="nestedMap1Lazy" type="user">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR" />
        <result property="realName" column="real_name" jdbcType="VARCHAR" />
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
        <result property="dId" column="d_id" jdbcType="INTEGER"/>
        <association property="dept" javaType="dept" column="d_id" select="queryDeptByUserIdLazy">

        </association>
    </resultMap>
    <resultMap id="baseDept" type="dept">
        <id column="did" property="dId"/>
        <result column="d_name" property="dName"/>
        <result column="d_desc" property="dDesc"/>
    </resultMap>
    <select id="queryUserNestedLazy" resultMap="nestedMap1Lazy">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t1.d_id
        FROM t_user t1
    </select>
    <select id="queryDeptByUserIdLazy" parameterType="int" resultMap="baseDept">
        select * from t_department where did = #{did}
    </select>

    <!-- 嵌套查询 1对多 1个部门有多个用户-->
    <resultMap id="nestedMap2" type="dept">
        <id column="did" property="dId"/>
        <result column="d_name" property="dName"/>
        <result column="d_desc" property="dDesc"/>
        <collection property="users" ofType="user">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="userName" column="user_name" jdbcType="VARCHAR" />
            <result property="realName" column="real_name" jdbcType="VARCHAR" />
            <result property="password" column="password" jdbcType="VARCHAR"/>
            <result property="age" column="age" jdbcType="INTEGER"/>
            <result property="dId" column="d_id" jdbcType="INTEGER"/>
        </collection>
    </resultMap>
    <select id="queryDeptNested" resultMap="nestedMap2">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t2.`did`
            ,t2.`d_name`
            ,t2.`d_desc`
        FROM t_user t1
        RIGHT JOIN
            t_department t2
            ON t1.`d_id` = t2.`did`
    </select>

    <!-- 1对多 延迟加载 -->
    <resultMap id="nestedMap2Lazy" type="dept">
        <id column="did" property="dId"/>
        <result column="d_name" property="dName"/>
        <result column="d_desc" property="dDesc"/>
        <collection property="users" ofType="user" column="did" select="queryUserByDeptLazy">
        </collection>
    </resultMap>

    <select id="queryDeptNestedLazy" resultMap="nestedMap2">
        SELECT
            ,t2.`did`
            ,t2.`d_name`
            ,t2.`d_desc`
        FROM
            t_department t2
    </select>

    <select id="queryUserByDeptLazy"  resultMap="BaseResultMap" >
        select * from t_user where d_id = #{did}
    </select>
</mapper>

2.5 创建实体

2.5.1 添加domain
  1. 用户实体:

    package com.byron.tuhu.domain;
    
    import lombok.Data;
    
    @Data
    public class User implements Serializable {
        private Integer id;
        private String userName;
        private String realName;
        private String password;
        private Integer age;
        private Integer dId;
        private Dept dept;
    }
    
  2. 部门实体:

    package com.byron.tuhu.domain;
    
    import lombok.Data;
    import java.util.List;
    
    @Data
    public class Dept implements Serializable{
        private Integer dId;
        private String dName;
        private String dDesc;
        private List<User> users;
    }
    
2.5.2 添加mapper接口
  • 这个package和UserMapper.xml的namespace一一对应
  • 接口名和id一致
package com.byron.tuhu.mapper;

import com.byron.tuhu.domain.Dept;
import com.byron.tuhu.domain.User;
import org.apache.ibatis.session.RowBounds;

import java.util.List;

public interface UserMapper {
    public List<User> selectUserList();
    public User selectUserById(Integer id);
    public Integer insertUser(User user);
    public Integer insertUserList(List<User> list);
    public Integer updateUserList(List<User> list);
    public Integer deleteByList(List<User> list);
    public List<User> queryUserNested();
    public List<User> queryUserNestedLazy();
    public List<Dept> queryDeptNested();
    public List<User> queryUserList(RowBounds rowBounds);
}
  • 至此,项目基础搭建大功告成,项目结构如下:

在这里插入图片描述

3.CRUD操作

引入junit依赖

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

3.1 查询

test->java添加ReadTest

3.1.1 普通查询
import com.byron.tuhu.domain.User;
import com.byron.tuhu.mapper.UserMapper;
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.InputStream;
import java.util.List;

/**
 * @Author: byron
 * @Description:
 * @Date: 2021-07-29 08:13
 * @ModifiedBy:byron
 **/

public class ReadTest {
    /**
     * MyBatis API 的使用
     *
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        SqlSession sqlSession = factory.openSession();
        // 4.通过SqlSession中提供的 API方法来操作数据库
        List<User> list = sqlSession.selectList("com.byron.tuhu.mapper.UserMapper.selectUserList");
        for (User user : list) {
            System.out.println(user);
        }
        // 5.关闭会话
        sqlSession.close();
    }

    /**
     * MyBatis getMapper 方法的使用
     */
    @Test
    public void test2() throws Exception {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        SqlSession sqlSession = factory.openSession();
        // 4.通过SqlSession中提供的 API方法来操作数据库
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = mapper.selectUserList();
        for (User user : list) {
            System.out.println(user);
        }
        // 5.关闭会话
        sqlSession.close();
    }
}
3.1.2 关联查询

表之间关联查询,参考mapper配置文件的脚本,结合代码理解会好一点

import com.byron.tuhu.domain.Dept;
import com.byron.tuhu.domain.User;
import com.byron.tuhu.mapper.UserMapper;
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;
import java.util.List;

/**
 * @Author: byron
 * @Description:
 * @Date: 2021-07-29 09:19
 * @ModifiedBy:byron
 **/

public class NestedTest {
    public SqlSession session;


    public void init() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
    }

    /**
     * 1对1  关联查询
     *
     * @throws Exception
     */
    @Test
    public void test01() throws Exception {
        init();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.queryUserNested();
        for (User user : users) {
            System.out.println(user + " " + user.getDept());
        }
    }

    /**
     * 1对1  关联查询 延迟加载
     *
     * @throws Exception
     */
    @Test
    public void test02() throws Exception {
        init();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.queryUserNestedLazy();
        for (User user : users) {
            System.out.println(user.getUserName() + "---->" + user.getDept());
        }
    }

    /**
     * 1对多的关联关系查询
     *
     * @throws Exception
     */
    @Test
    public void test03() throws Exception {
        init();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<Dept> depts = mapper.queryDeptNested();
        for (Dept dept : depts) {
            System.out.println(dept + " " + dept.getUsers());
        }
    }
}
3.1.3 分页查询
1. 逻辑分页

mybatis提供了Rowbounds对象,包含了offset和limit两个int属性,类似linq的skip然后take。

只需要在mapper接口中添加接口

public List<User> queryUserList(RowBounds rowBounds);
  • 然后添加单元测试
@Test
public void test01() throws Exception{
  init();
  UserMapper mapper = session.getMapper(UserMapper.class);
  RowBounds rowBounds = new RowBounds(1,3);
  List<User> users = mapper.queryUserList(rowBounds);
  for (User user : users) {
      System.out.println(user);
  }
}
  • 运行观察打印sql

在这里插入图片描述
握草,为啥sql是物理分页呢???

  • 原来是因为PageHelper插件
    在这里插入图片描述
  • 注释PageHelper后再观察

在这里插入图片描述

  • RowBounds逻辑分页原理

    RowBounds的其实是对ResultSet的处理。它会舍弃掉前面offset条数据,然后再取剩下的数据的limit条。

    // DefaultResultSetHandler.java
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping 
    parentMapping) throws SQLException {
       DefaultResultContext<Object> resultContext = new DefaultResultContext();
       ResultSet resultSet = rsw.getResultSet();
       this.skipRows(resultSet, rowBounds);
       while(this.shouldProcessMoreRows(resultContext, rowBounds) && 
    !resultSet.isClosed() && resultSet.next()) {
           ResultMap discriminatedResultMap = 
    this.resolveDiscriminatedResultMap(resultSet, resultMap, (String)null);
           Object rowValue = this.getRowValue(rsw, discriminatedResultMap, 
    (String)null);
           this.storeObject(resultHandler, resultContext, rowValue, parentMapping, 
    resultSet);
      }
    }
    
2.物理分页

取消注释PageHelper插件

还可以通过在查询之前添加代码

//获取第1页前5条
PageHelper.startPage(1,5);
List<User> users = mapper.selectUserList();

原理是通过提供Interceptor实现类,拦截query方法,然后改写sql,暂不展开。

3.2 新增

import com.byron.tuhu.domain.User;
import com.byron.tuhu.mapper.UserMapper;
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;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: byron
 * @Description:
 * @Date: 2021-07-29 08:45
 * @ModifiedBy:byron
 **/

public class BathCreateTest {
    public SqlSession session;

    public void init() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
    }

    /**
     * 循环插入10000
     */
    @Test
    public void test1() throws Exception {
        init();
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        for (int i = 2000; i < count; i++) {
            User user = new User();
            user.setUserName("a" + i);
            user.setRealName("A" + i);
            user.setPassword("1234");
            user.setAge(i / 30);
            user.setDId(10000 + i);
            mapper.insertUser(user);
        }
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("循环批量插入" + count + "条,耗时:" + (end - start) + "毫秒");
    }

    /**
     * 批量插入
     */
    @Test
    public void test2() throws Exception {
        init();
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        List<User> list = new ArrayList<>();
        for (int i = 2000; i < count; i++) {
            User user = new User();
            user.setUserName("a" + i);
            user.setRealName("A" + i);
            user.setPassword("1234");
            user.setAge(i / 30);
            user.setDId(10000 + i);
            list.add(user);
        }
        mapper.insertUserList(list);
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("循环批量插入" + count + "条,耗时:" + (end - start) + "毫秒");
    }
}

test1使用for循环逐条插入,执行结果:

在这里插入图片描述

test2使用批量插入,执行结果:

在这里插入图片描述

3.3 更新

import com.byron.tuhu.domain.User;
import com.byron.tuhu.mapper.UserMapper;
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;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: byron
 * @Description:
 * @Date: 2021-07-29 08:56
 * @ModifiedBy:byron
 **/

public class BatchUpdateTest {
    public SqlSession session;


    public void init() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
    }

    /**
     * 批量更新
     */
    @Test
    public void test() throws Exception {
        init();
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        List<User> list = new ArrayList<>();
        for (int i = 2000; i < count; i++) {
            User user = new User();
            user.setId(i);
            user.setUserName("a" + i);
            user.setRealName("A" + i);
            user.setPassword("12345");
            user.setAge(i / 40);
            user.setDId(1000 + i);
            list.add(user);
        }
        mapper.updateUserList(list);
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("批量更新" + count + "条,耗时:" + (end - start) + "毫秒");
    }
}

3.4 删除

import com.byron.tuhu.domain.User;
import com.byron.tuhu.mapper.UserMapper;
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;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: byron
 * @Description:
 * @Date: 2021-07-29 09:03
 * @ModifiedBy:byron
 **/

public class DeleteTest {
    public SqlSession session;

    public void init() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
    }

    /**
     * 删除
     */
    @Test
    public void test() throws Exception {
        init();
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        List<User> list = new ArrayList<>();
        for (int i = 2000; i < count; i++) {
            User user = new User();
            user.setId(i);
            list.add(user);
        }
        Integer realCount = mapper.deleteByList(list);
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("批量删除" + realCount + "条,耗时:" + (end - start) + "毫秒");
    }
}

4.延迟加载

​ 延迟加载:就是在你要用到的时候才去数据库查询,也叫按需查询或懒加载。

<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="false"/>

代码位于3.1.1 关联查询

  1. 设置lazyLoadingEnable为false,运行test02,打断点观察,搜索Preparing,可以看到部门表也被查询,比较常规

    在这里插入图片描述

  2. 设置lazyLoadingEnable为true,打断点观察

    执行use.getDept之前:只查询了用户表

    在这里插入图片描述

    执行后:延迟查询部门表

    在这里插入图片描述

五、Mybatis进阶

1.缓存

1.1 一级缓存

​ 一级缓存也叫本地缓存(Local Cache),MyBatis的一级缓存是在会话(SqlSession)层面进行缓存的。MyBatis的一级缓存是默认开启的,不需要任何的配置。

在BaseExecutor对象的query方法中有关闭一级缓存的逻辑(如下,如果要关闭,localCacheScope设置为STATEMENT即可)。

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
            this.clearLocalCache();
        }

        List list;
        try {
            ++this.queryStack;
            list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
            if (list != null) {
                this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator var8 = this.deferredLoads.iterator();

            while(var8.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            //配置<setting name="localCacheScope" value="STATEMENT"/>
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                //清除缓存
                this.clearLocalCache();
            }
        }

        return list;
    }
}
  • 测试一级缓存,添加CachedTest测试类,首先用session1做两次同样的查询:
import com.byron.tuhu.domain.User;
import com.byron.tuhu.mapper.UserMapper;
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;
import java.util.List;

/**
 * @Author: byron
 * @Description:
 * @Date: 2021-08-02 08:40
 * @ModifiedBy:byron
 **/

public class CachedTest {

    @Test
    public void test01() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        SqlSession session1 = factory.openSession();
        SqlSession session2 = factory.openSession();

        UserMapper userMapper = session1.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1);
        System.out.println(user.getUserName());
        User user1 = userMapper.selectUserById(1);
        System.out.println(user1.getUserName());
    }
}
  • 观察打印的sql,发现只访问了一次数据库

在这里插入图片描述

其它测试场景:session1同方法不同参数、session1和session2同方法同参数、关闭一级缓存session1同方法同参数。(略)

1.2 二级缓存

1.2.1 生命周期

​ 二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace级别的,可以被多个SqlSession共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。

  • 二级缓存的设置,首先是settings中的cacheEnabled要设置为true,当然默认的就是为true,这个步骤决定了在创建Executor对象的时候是否通过CachingExecutor来装饰。

在这里插入图片描述

  • 但是,这样并不能让二级缓存生效,还需要在mapper配置中添加cache标签才行:
<cache type="org.apache.ibatis.cache.impl.PerpetualCache"
       size="1024"
       eviction="LRU"
       flushInterval="120000"
       readOnly="false"/>
属性说明取值
type缓存实现类需要实现Cache接口,默认PerpectualCache,可以使用三方缓存
size最大缓存个数默认1024
eviction缓存淘汰算法LRU – 最近最少使用的:移除最长时间不被使用的对象(默认)。
FIFO– 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInterval执行缓存清除的间隔单位ms,即缓存失效的绝对时间,不配置则调用insert/update时缓存失效
readOnly是否只读true:只读缓存,会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化),不会共享。这会慢一些,但是安全,因此默认是 false。改为false可读写时,对象必须支持序列化。
blocking启用阻塞缓存通过在get/put方式中加锁,保证只有一个线程操作缓存,基于Java重入锁实现
  • 再看源码中的实现,根据sql创建CacheKey

    在这里插入图片描述

  • 二级缓存对于当前映射文件中的所有查询都生效,如果针对某个查询想关闭二级缓存,只需要添加属性useCache=“false”

    在这里插入图片描述

  • 当我们执行DML操作时会清空一、二级缓存

    在这里插入图片描述

1.2.2 代码演示
  • 创建2个会话,分别执行同一个查询
@Test
public void test01() throws IOException {
    // 1.获取配置文件
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    // 2.加载解析配置文件并获取SqlSessionFactory对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    // 3.根据SqlSessionFactory对象获取SqlSession对象
    SqlSession session1 = factory.openSession();
    SqlSession session2 = factory.openSession();

    UserMapper mapper1 = session1.getMapper(UserMapper.class);
    User user1 = mapper1.selectUserById(1);
    System.out.println(user1);

    UserMapper mapper2 = session2.getMapper(UserMapper.class);
    User user2 = mapper2.selectUserById(1);
    System.out.println(user2);

}
  • 我ca,没命中,翻车!!!

    在这里插入图片描述

  • 经过不断地探索(百度),原来如此,session1需要先提交,然后session2才能命中啊

    在这里插入图片描述

    close()中会执行提交操作

    在这里插入图片描述

    至此,浅显的二级缓存探索结束,跨不同的namespace以及cache-ref的使用也就不展开了。

1.3 三方缓存

​ 实际生产中,都是分布式系统,很少会采用本地二级缓存,mybatis提供三方缓存工具来实现,比如redis。

https:github.com/mybatis/redis-cache

  • 添加依赖

    <dependency>
       <groupId>org.mybatis.caches</groupId>
       <artifactId>mybatis-redis</artifactId>
       <version>1.0.0-beta2</version>
    </dependency>
    
  • 更改二级缓存配置文件

    <cache type="org.mybatis.caches.redis.RedisCache"
               size="1024"
               eviction="LRU"
               flushInterval="120000"
               readOnly="false"/>
    
  • 添加redis.properties配置

    host=localhost
    port=6379
    connectionTimeout=5000
    soTimeout=5000
    database=0
    
  • 运行CachedTest.cs

    在这里插入图片描述

  • 查看redis,数据已经同步到redis,留意一下这个hasmap的name,哈,就是namespace,hash的key是sql语句

    在这里插入图片描述

    至于redis为什么能替换PERPETUAL,具体实现原理同样不展开,可以参见源码XmlMapperBuilder.java的cacheElement方法。

2.TypeHandler

​ 由于Java类型和数据库的JDBC类型不是一一对应的(比如String与varchar、char、text),所以我们把Java对象转换为数据库的值,和把数据库的值转换成Java对象,需要经过一定的转换,这两个方向的转换就要用到TypeHandler。

​ 当参数类型和返回值是一个对象的时候,我没有做任何的配置,为什么对象里面的一个String属性,可以转换成数据库里面的varchar字段?

​ 这是因为MyBatis已经内置了很多TypeHandler(在type包下),它们全部全部注册在TypeHandlerRegistry中,他们都继承了抽象类BaseTypeHandler,泛型就是要处理的Java数据类型。

​ 这个也是为什么大部分类型都不需要处理。当我们查询数据和登记数据,做数据类型转换的时候,就会自动调用对应的TypeHandler的方法。

在这里插入图片描述

​ 为了加深理解,我们来实现一个自定义的TypeHandler:查询User表,如果数据中存在值为zhaosi的人,返回数据时前缀加上:【东北舞王】

  1. 首先在typeHandler下添加ZhaoSiHandler.java

    /**
     * @Author: byron
     * @Description:
     * @Date: 2021-08-03 23:38
     * @ModifiedBy:byron
     **/
    package com.byron.tuhu.typeHandler;
    
    import org.apache.ibatis.type.BaseTypeHandler;
    import org.apache.ibatis.type.JdbcType;
    
    import java.sql.CallableStatement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class ZhaoSiHandler extends BaseTypeHandler<String> {
    
        /**
         * 插入数据时回调
         *
         * @param ps
         * @param i
         * @param parameter
         * @param jdbcType
         * @throws SQLException
         */
        @Override
        public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    
        }
    
        /**
         * 获取数据时回调
         *
         * @param rs
         * @param columnName
         * @return
         * @throws SQLException
         */
        @Override
        public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
            String val = rs.getString(columnName);
            if ("zhaosi".equalsIgnoreCase(val)) {
                return "【东北舞王】" + val;
            }
            return val;
        }
    
        @Override
        public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
            String val = rs.getString(columnIndex);
            if ("zhaosi".equalsIgnoreCase(val)) {
                return "【东北舞王】" + val;
            }
            return val;
        }
    
        @Override
        public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
            String val = cs.getString(columnIndex);
            if ("zhaosi".equalsIgnoreCase(val)) {
                return "【东北舞王】" + val;
            }
            return val;
        }
    }
    
  2. 然后mybatis-config中注册

    <typeHandlers>
        <typeHandler handler="com.byron.tuhu.typeHandler.ZhaoSiHandler"></typeHandler>
    </typeHandlers>
    
  3. mapper中对应的列配置生效

    在这里插入图片描述

  4. 执行ReadTest,切记先关闭二级缓存,因为本人被那玩意坑了半天

    在这里插入图片描述

    东北舞王登场了!!!

3.MBG

​ 官方文档:http://mybatis.org/generator/

​ 我们在项目中使用MyBaits的时候,针对需要操作的一张表,需要创建实体类、Mapper映射器、Mapper接口,里面又有很多的字段和方法的配置,这部分的工作贼jr恶心。而大部分时候我们对于表的基本操作是相同的,比如根据主键查询、根据Map查询、单条插入、批量插入、根据主键删除等等等等。当我们的表很多的时候,意味着有大量的重复工作,违背DRY。

​ 所以有没有一种办法,可以根据我们的表,自动生成实体类、Mapper映射器、Mapper接口,里面包含了我们需要用到的这些基本方法和SQL呢?
​ MyBatis也提供了一个代码生成器,叫做MyBatis Generator,简称MBG(它是MyBatis的一个插件)。我们只需要修改一个配置文件,使用相关的jar包命令或者Java代码就可以帮助我们生成实体类、映射器和接口文件。这也是我们目前工作中在使用的一种模式。

​ MBG的配置文件里面有一个Example的开关,这个东西用来构造复杂的筛选条件的,换句话说就是根据我们的代码去生成where条件。
​ 原理:在实体类中包含了两个有继承关系的Criteria(这玩意我们熟悉吧!),用其中自动生成的方法来构建查询条件。把这个包含了Criteria的实体类作为参数传到查询参数中,在解析Mapper映射器的时候会转换成SQL条件。

3.1 添加插件

<build>        
    <plugins>            
        <plugin>                
            <groupId>org.mybatis.generator</groupId>               
            <artifactId>mybatis-generator-maven-plugin</artifactId>                
            <version>1.3.2</version>               
            <configuration>                    
                <!-- 指定配置文件的位置 -->                    
                <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>               
            </configuration>            
        </plugin>        
    </plugins>    
</build>

3.2 添加配置文件

文件名:generatorConfig.xml

location位置使用全路径,指向mysql-connector-java-X*.X*.X.jar

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--mysql 连接数据库jar 这里选择自己本地仓库位置-->
    <classPathEntry location="E:\maven\repository\mysql\mysql-connector-java\8.0.11\mysql-connector-java-8.0.11.jar" />
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/test_db?useSSL=false" userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
           NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO类的位置 -->
        <javaModelGenerator targetPackage="com.byron.tuhu.domain"
                            targetProject="src/main/java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置
           如果maven工程只是单独的一个工程,targetProject="src/main/java"
           若果maven工程是分模块的工程,targetProject="所属模块的名称",例如:
           targetProject="ecps-manager-mapper",下同-->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="src/main/resources">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.byron.tuhu.mapper"
                             targetProject="src/main/java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表 -->
        <table tableName="t_user" domainObjectName="UserMGB" enableCountByExample="true" enableUpdateByExample="true"
               enableDeleteByExample="true" enableSelectByExample="true"
               selectByExampleQueryId="true">
            <columnOverride column="d_id" property="departmentId" javaType="java.lang.Integer"/>
        </table>
    </context>
</generatorConfiguration>

3.3 生成代码

运行插件,成功后即可看到项目路径下自动生成了项目文件

在这里插入图片描述

3.4 测试demo

3.4.1 配置config
<mappers>
    <mapper resource="mapper/UserMapper.xml"/>
    <mapper resource="mapper/UserMGBMapper.xml"/>
</mappers>
3.4.2 MGBTest.java
import com.byron.tuhu.domain.User;
import com.byron.tuhu.domain.UserMGB;
import com.byron.tuhu.domain.UserMGBExample;
import com.byron.tuhu.mapper.UserMGBMapper;
import com.byron.tuhu.mapper.UserMapper;
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;
import java.util.List;

/**
 * @Author: byron
 * @Description:
 * @Date: 2021-08-05 09:37
 * @ModifiedBy:byron
 **/

public class MGBTest {

    @Test
    public void test01() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        SqlSession sqlSession = factory.openSession();
        // 4.通过SqlSession中提供的 API方法来操作数据库
        UserMGBMapper mapper = sqlSession.getMapper(UserMGBMapper.class);
        UserMGBExample example = new UserMGBExample();
        UserMGBExample.Criteria criteria = example.createCriteria();
        criteria.andIdEqualTo(2);
        List<UserMGB> userMGBS = mapper.selectByExample(example);
        for (UserMGB user : userMGBS) {
            System.out.println(user);
        }
        // 5.关闭会话
        sqlSession.close();
    }
}

运行,成功:

在这里插入图片描述

4.MyBatis-Plus

https://mp.baomidou.com/

在这里插入图片描述

MyBatis-Plus的核心功能:
**通用 CRUD:**定义好Mapper接口后,只需要继承BaseMapper 接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件。
**条件构造器:**通过EntityWrapper(实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的SQL。
**代码生成器:**支持一系列的策略配置与全局配置,比MyBatis的代码生成更好用。
**分页:**内置分页插件,基于mybatis实现物理分页,写分页就像普通list查询一样easy。

进一步深入依然不做展开,动手即可,一个字:干!

六、FQA

  1. 报错:Cause: java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed

    官方解释:https://mysqlconnector.net/connection-options/

    解决办法:url后追加allowPublicKeyRetrieval=true

    在这里插入图片描述

  2. 批量操作限制

  • MyBatis的动态标签的批量操作存在一定的缺点,比如数据量特别大的时候,拼接出来的SQL语句过大。
  • MySQL的服务端对于接收的数据包有大小限制,max_allowed_packet 默认是 4M,需要修改默认配置或者手动地控制条数
  1. 延迟aggressiveLazyLoading配置

    如果设置为true,那其实lazyLoadingEnabled=true毫无意义

  2. 为什么二级缓存开启还要添加cache标签?

    参见源码:

    在这里插入图片描述

  3. MGB生成代码时报错:The specified target project directory .src/main/java does not exist

    Edit Configrations…–>working derictory,复制出来使用全路径,比如

    在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

by_ron

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值