侧边栏壁纸
博主头像
毕发胖博主等级

沉默会被理解为赞同

  • 累计撰写 8 篇文章
  • 累计创建 0 个标签
  • 累计收到 0 条评论

MyBatis详细入门

毕发胖
2022-09-21 / 0 评论 / 1 点赞 / 72 阅读 / 44,232 字
温馨提示:
本文最后更新于 2022-09-21,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

MyBatis笔记

1、Mybatis解决了什么问题

1、以往的JDBC使用会造成大量的重复代码,如数据库的连接,释放连接等操作,过于频繁

2、SQL语句编写在代码中,硬编码的操作会造成对代码不方便维护,实际应用中SQL语句的变化可能比较大,如果一旦出现变化,就必须改变Java类

3、在使用preparedStatement的时候传递参数使用占位符,其实也是存在硬编码的,因为如果我们SQL语句出现变化,就必须修改源码

4、对结果集的解析中也存在硬编码

2、MyBatis的入门程序

2.1 、创建数据表

image20220502193607097.png

2.2、创建Maven项目

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

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

  <groupId>com.bifapang</groupId>
  <artifactId>MyBatis1</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>MyBatis1</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.28</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

2.3、创建mybatis配置文件

mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis全局配置文件:节点的顺序参考:按住ctrl+configuration节点,进入文件观看-->
<configuration>
<!--配置mybatis环境-->
    <environments default="development">    <!--标识-->
        <environment id="development">
            <!--使用JDBC控制事务 因为mybatis封装的是jdbc,所以这里写jdbc-->
            <transactionManager type="JDBC"></transactionManager>   
            <!--mybatis默认的连接池,设置完连接池后设置四大参数 1.驱动 2.连接地址 3.用户名 4.密码-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=UTC&amp;characterEncoding=utf8&amp;useUnicode=true&amp;useSSL=false""/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

2.4、创建实体类

package com.bifapang.pojo;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

@Data	//使用了lombok ,不使用的时候需要自行增加set/get方法
public class Team implements Serializable {
    private Integer teamId;
    private String teamName;
    private String location;
    private Date createTime;
    
}

2.5、编写实体类对应的配置文件(ORM映射文件)

在mybatis中每一个实体类都有一个对应的配置文件

这里就针对Team实体类写一个配置文件

Team.xml

//这里查询所有,会查询出多行数据,需要将结果存放到集合中,否则会报错

<?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">
<!--映射文件:实体类与数据库表的映射  ORM思想 object relation mapping
        SQL语句从代码的硬编码中抽取出来了
    -->
<!--namespace=“完全限定名,实体类的完全限定名” 指的是对应的那个实体类的包名+类名-->
<mapper namespace="com.bifapang.pojo.Team">
    <!--id就是标识,不能重复,相当于Dao中的一个方法名
        resultType用来声明返回结果类型,要求表中列名必须与实体类中属性名完全一致-->
    <select id="findAll" resultType="com.bifapang.pojo.Team">
        select * from team;
    </select>
</mapper>

实体类配置文件写完后需要加入到mybatis配置文件中

2.6、将映射文件注册到mybatis配置文件中

<?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">
<!--mybatis全局配置文件:节点的顺序参考:按住ctrl+configuration节点,进入文件观看-->
<configuration>
<!--配置mybatis环境-->
    <environments default="development">    <!--标识-->
        <environment id="development">
            …………………………
        </environment>
    </environments>
    <!--注册映射文件-->
    <mappers>
        <!--有多少个表多少个映射文件,这里就写多少个-->
        <mapper resource="com/bifapang/pojo/Team.xml"></mapper>
    </mappers>
</configuration>

配置完后需要进行扫描

2.7、配置映射文件的扫描位置

pom.xml

<resources>
            <resource>
                <directory>src/main/java</directory><!--所在的目录-->
                <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
</resources>

2.8、开始测试

测试类

想要执行编写的sql语句,就得需要以下几个步骤

1、读取 mybatis.xml 这就需要用到 Resource.getResourceAsReader() 来读取了

2、创建 SqlSessionFactory,通过 SqlSessionFactory 来获取 SqlSesison 对象,我们主要是通过这个 SqlSession 来操作数据库

3、拿到SqlSession对象,通过 sqlSessionFactory.openSession() 来获取

4、在开始操作数据库,通过sqlSession的方法来执行sql语句,

​ (1)如果是查询多个 就是sqlSession.selectList();

​ (2)如果是查询单个 就是sqlSession.selectOne(); 其实也可以用来执行增删改操作,但是不推荐,因为缺少了事务,该方法本来就是用来查询的,查询是不需要提交事务的

​ (3)如果是删除操作 就是 sqlSession.delete(); 注意,增删改操作需要手动提交事务,否则不生效

​ (4)如果是增加操作 就是 sqlSession.insert(); 注意,增删改操作需要手动提交事务,否则不生效

​ (5)如果是修改操作 就是 sqlSession.update(); 注意,增删改操作需要手动提交事务,否则不生效

package com.bifapang;

import static org.junit.Assert.assertTrue;

import com.bifapang.pojo.Team;
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.Reader;
import java.util.List;

/**
 * Unit test for simple App.
 */
public class AppTest {
    /**
     * Rigorous Test :-)
     */
    @Test
    public void test01() throws IOException {
        //1.读取 mybatis.xml
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        //2.创建 SqlSessionFactory,通过 SqlSessionFactory 获取 SqlSession 的对象,再通过 SqlSession 来操作数据库
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //3.获取 SQLSession 对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //4.执行SQL语句
        List<Team> teamList = sqlSession.selectList("com.bifapang.pojo.Team.findAll");//查询所有
        //5.输出每条语句
        for (Team team : teamList) {
            System.out.println(team);
        }
        //6.关闭SqlSession对象
        sqlSession.close();

    }
}

这样就完成了我们的第一个MyBatis程序

2.9、封装重复内容

我们发现,如果我需要对同一张表做多种操作的话,上面的读取也好、创建SqlSessionFactory也好,重复性的代码要敲很多,所以我们用到java中封装的特性,然后使用我们先前学的Spring中的AOP的思想使用 before 跟 after

这样在主方法执行前先执行before,然后再主方法执行完后,再使用after

2.9.1、封装代码

package com.bifapang;

import com.bifapang.pojo.Team;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.Date;

public class TeamTest {
    private SqlSession sqlSession;

    @Before
    public void before() throws IOException {
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.close();
    }
}

这样一来,写sql就简单许多了

2.10、增删改

Team.xml的映射文件中添加:

// 插入和修改一般是以对象为参数,而不是Integer这类的类型

// 如果是以对象作为参数,那么#{} 中填写的就是该实体类中的属性名称,必须一致,否则获取不到

// 基础数据类型或者包装类 以及String 类型时,#{}里面的名称是没有意义的,只有当返回值为对象时,对#{}里面的内容才有要求

<!--resultType是返回值的意思,parameterType是参数类型的意思-->
<!--    #{}代表的就是用来接受传过来的值-->
    <select id="findById" resultType="com.bifapang.pojo.Team" parameterType="int">
        select * from team where teamId=#{id};
    </select>

    <delete id="deleteById" parameterType="int">
        delete from team where teamId=#{id};
    </delete>

    <!--修改的时候是传进来一个对象作为参数-->
    <update id="updateById" parameterType="com.bifapang.pojo.Team">
        update team set teamName=#{teamName},location=#{location} where teamId=#{teamId};
    </update>

    <!--新增的时候是传进来一个对象作为参数-->
    <insert id="insertTeam" parameterType="com.bifapang.pojo.Team">
        insert into team (teamName,location,createTime) values (#{teamName},#{location},#{createTime});
    </insert>
</mapper>

测试类中添加如下方法:

    @Test
    public void findById() {
        Team team = sqlSession.selectOne("com.bifapang.pojo.Team.findById", 1);//根据id查询
        System.out.println(team);
    }

    @Test
    public void delete() {
        int team = sqlSession.delete("com.bifapang.pojo.Team.deleteById", 3);
        sqlSession.commit();    //提交事务
        System.out.println(team);
    }

    @Test
    public void insert() {
        Team team = new Team();
        team.setTeamName("毕发胖");
        team.setLocation("江西九江");
        team.setCreateTime(new Date());
        int insert = sqlSession.insert("com.bifapang.pojo.Team.insertTeam", team);
        sqlSession.commit();
        System.out.println(insert);
    }

    @Test
    public void updateTeam(){
        Team team = new Team();
        team.setTeamId(5);
        team.setTeamName("三百斤的小胖子");
        team.setLocation("地球村");
        sqlSession.update("com.bifapang.pojo.Team.updateById",team);
        sqlSession.commit();
    }
}

3、MyBatis对象分析

3.1、Resources

Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。

3.2、SqlSessionFactory

SqlSessionFactory是一个开销很大的一个接口对象,它是线程安全的,所以一个应用只需要一个就可以了,我们创建SqlSession需要使用到SqlSessionFactory接口中的openSession方法

默认的openSession()没有参数,默认会创建如下特性的SqlSession:
	1、会开启一个事务(也就是不自动提交)
	2、将会从我们在mybatis配置文件中配置的DataSource实例中获取Connection对象,
	3、预处理语句不会被复用,也不会批量处理更新
	
openSession(true)	:自动提交事务
openSession(false)	:创建一个非自动提交的SqlSession 需要手动提交
openSession()等同于openSession(false)

4、配置日志文件

4.1添加jar包依赖

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

4.2添加日志配置文件

log4j.properties

# Global logging configuration  info  warning  error
#stdout向外输出的意思 有几种模式,分别是 info 输出正常信息 warning 输出危险信息 error 输出错误信息
log4j.rootLogger=DEBUG,stdout
#debug 能够显示程序底层是怎么执行的
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

4.3 mybatis配置文件中添加日志的配置

<configuration> 
<!--配置日志,注意顺序:查看属性点击configuration进入查看即可--> 
<settings> 
	<setting name="logImpl" value="LOG4J" /> 
</settings>

5、插入数据时获取自增ID

想要在插入时获取自增的ID,必须要去映射文件中进行配置

TeamMapper.xml

<!--新增的时候是传进来一个对象作为参数-->
    <insert id="insertTeam" parameterType="com.bifapang.pojo.Team">
        <selectKey keyProperty="teamId" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID();
        </selectKey>
        insert into team (teamName,location,createTime) values (#{teamName},#{location},#{createTime});
    </insert>

这里设置了 <selectKey></selectKey> ,分别需要设置三个参数,keyProperty,等于查询出来的数据存放在这个属性中,order 指定顺序,是先执行主 sql 语句后再执行该语句,还是先执行该语句再执行主 sql ,resultType 是返回值类型

select LAST_INSERT_ID(); //获取最后自增的 id 值,如果指定顺序是 after,就是获取在新增之后自增的那个id,如果是 before,那么就是查询新增之前最后的那个id

6、通过UUID来设置主键ID

需要在mapper接口对应的配置文件中设置

主要方法如下

<selectKey keyProperty="recordId" order="BEFORE" resultType="java.lang.String">
	select UUID();
</selectKey>

此时我们写一条插入语句

<insert id="insert" parameterType="com.bifapang.mapper.GameRecordMapper">
    <selectKey keyProperty="recordId" order="BEFORE" resultType="java.lang.String">
        select UUID();
    </selectKey>
    insert into gamerecord(recordId, homeTeamId, gameDate, score, visitingTeamId)
    values(#{recordId},#{homeTeamId},#{gameDate},#{score},#{visitingTeamId});
</insert>

意思就是将UUID作为recordId传入到 insert 语句中

开始测试

6.1测试是否成功通过UUID设置主键

    @Test
    public void test01(){
        SqlSession sqlSession = MyBatisUtil.getSession();
        GameRecordMapper mapper = sqlSession.getMapper(GameRecordMapper.class);
        GameRecord gameRecord = new GameRecord();
        gameRecord.setHomeTeamId(1024); //主队ID
        gameRecord.setVisitingTeamId(1023); //客队ID
        gameRecord.setScore(99);    //分数
        gameRecord.setGameDate(new Date());
        mapper.insert(gameRecord);
        sqlSession.commit();
    }

image20220509110347382.png

成功!

7、输入映射

7.1、通过参数顺序传递参数

7.1.2、arg传递

TeamMapper接口

 //输入参数
    List<Team> queryByBanged1(Integer min,Integer max);

mapper配置文件

arg0 —— 第一个参数 arg1第二个参数

 <!--不识别 <符号 需要用&lt;来表示
        当需要传入多个参数时,可以按照参数的顺序进行传递,形参名是无法识别的,可以通过固定的标识来传递参数
        arg0 —— 第一个参数   arg1第二个参数
        param1 —— 第一个参数     param2 —— 第二个参数 注意这里是1开始-->
    <select id="queryByBanged1" resultType="com.bifapang.pojo.Team">
        select *
        from team
        where teamId>=#{arg0} and teamName &lt;=#{arg1};
    </select>

7.1.3、param 参数传递

TeamMapper接口

 //输入参数
    List<Team> queryByBanged1(Integer min,Integer max);

mapper配置文件

param1 —— 第一个参数 param2 —— 第二个参数 注意这里是1开始

 <!--不识别 <符号 需要用&lt;来表示
        当需要传入多个参数时,可以按照参数的顺序进行传递,形参名是无法识别的,可以通过固定的标识来传递参数
        arg0 —— 第一个参数   arg1第二个参数
        param1 —— 第一个参数     param2 —— 第二个参数 注意这里是1开始-->
    <select id="queryByBanged1" resultType="com.bifapang.pojo.Team">
        select *
        from team
        where teamId>=#{param1} and teamName &lt;=#{param2};
    </select>

7.2、@Param注解传递

这里就有点像 SpringMVC 中的 @RequestParam ,通过该注解声明配置文件中的属性值,所以不论后面的形参名是啥,最终都会赋值到

// #{} 中的名称必须与接口的方法中的参数注解@Param()中填写的内容相同,注解方式可以用param1、param2来进行传值,但是arg不行

配置文件中对应的那个属性里去

  //通过注解的方式传入参数
    List<Team> queryByBanged2(@Param("min") Integer min,@Param("max") Integer max);

mapper配置文件

<select id="queryByBanged2" resultType="com.bifapang.pojo.Team">
        select *
        from team
        where teamId>=#{min} and teamName &lt;=#{max};
    </select>

7.3、通过map来传递多个参数

Map 集合可以存储多个值,使用Map向 mapper 文件一次传入多个参数,Map 集合使用 String 的 key,Object 类型的值存储参数

// 这里#{} 里面的内容必须要跟 map 中存的那个 key 要一致

TeamMapper

//map方式传入多个参数 <String ,Object>
List<Team> queryByBanged3(Map<String ,Object> map);

mapper 配置文件

<select id="queryByBanged2" resultType="com.bifapang.pojo.Team">
        select *
        from team
        where teamId>=#{min} and teamName &lt;=#{max};
    </select>

测试类:

//map 传递多个值
    @Test
    public void test03(){
        SqlSession session = MyBatisUtil.getSession();
        TeamMapper mapper = session.getMapper(TeamMapper.class);
        Map<String ,Object> map=new HashMap<>();
        map.put("min",0);
        map.put("max",10);
        List<Team> teamList = mapper.queryByBanged3(map);
        for (Team team : teamList) {
            System.out.println(team);
        }
    }

7.4、通过pojo类传递多个参数

通过 Map 直接去传递多个参数有的时候容易弄混参数顺序问题

这个时候还有一种通过 pojo 类来实现的方式

我这里直接使用 Lombok 的方式来生成方法

package com.bifapang.vo;

import lombok.Data;

@Data
public class QueryVo {
    private String name;
    private String location;
    private Integer min;
    private Integer max;

}

TeamMapper 接口

 List<Team> queryByBanged4(QueryVo vo);

mapper.xml 映射文件

  <!--方式4:通过pojo/vo类传递多个参数:映射文件中的参数占位符必须和pojo/vo类中的字段完全一致-->
    <select id="queryByBanged4" resultType="com.bifapang.pojo.Team">
        select *
        from team
        where teamId>=#{min} and teamName &lt;=#{max}
        and teamName like #{name} and location= #{location};
    </select>

测试类

//通过pojo/vo类来传递多个参数,pojo/vo类的属性必须与sql语句中占位符的字段值一样
@Test
public void test04(){
    SqlSession session = MyBatisUtil.getSession();
    TeamMapper mapper = session.getMapper(TeamMapper.class);
    QueryVo queryVo = new QueryVo();
    queryVo.setMin(0);
    queryVo.setMax(10);
    queryVo.setName("查宝宝");
    queryVo.setLocation("九江");
    List<Team> teamList = mapper.queryByBanged4(queryVo);
    for (Team team : teamList) {
        System.out.println(team);
    }
}

8、#{} 和 ${} 的区别 考点!

#{} 代表的是占位符,代替 sql 中的 ?

8.1、#{} 占位符

映射文件,注意看,这里的 min 跟 max 使用的是#{}

<select id="queryByBanged3" resultType="com.bifapang.pojo.Team">
        select *
        from team
        where teamId>=#{min} and teamName &lt;=#{max};
</select>

测试

//map 传递多个值
@Test
public void test03(){
    SqlSession session = MyBatisUtil.getSession();
    TeamMapper mapper = session.getMapper(TeamMapper.class);
    Map<String ,Object> map=new HashMap<>();
    //这里的key需要跟mapper里定义的属性一样,如果值不一样,就会出现null
    map.put("min",0);
    map.put("max",10);
    List<Team> teamList = mapper.queryByBanged3(map);
    for (Team team : teamList) {
        System.out.println(team);
    }
}

咱们看看效果

image20220510110130179.png

看到这里就知道 #{} 其实就是占位符

8.2、${} 字符串替换

mapper.xml

<select id="findById" resultType="com.bifapang.pojo.Team" parameterType="int">
    select *
    from team
    where teamId = ${id};
</select>

测试类

//${}字符串替换
 @Test
 public void test05(){
     SqlSession session = MyBatisUtil.getSession();
     TeamMapper mapper = session.getMapper(TeamMapper.class);
     Team byId = mapper.findById(1);
     System.out.println(byId);
 }

测试结果

image20220510111024100.png

可以看到这里是直接替换了,没有出现占位符的字样,

8.3、总结

#{} 与 ${} 的区别在于 #{} 指代的是占位符,而 ${} 指代的是替换符

9、输出映射

9.1、返回单个值

这做一个查询表中id数的SQL

TeamMapper文件

int getCount();

TeamMapper.xml 映射文件

<select id="getCount" resultType="java.lang.Integer">
	select count(teamId) from team;
</select>

测试类:

@Test
public void test01(){
	TeamMapper teamMapper= MyBatis.getSession.getMapper(TeamMapper.class);
    System.out.println(teamMapper.getCount());
}
测试结果:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 100929741.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@60410cd]
DEBUG [main] - ==>  Preparing: select count(teamId) from team;
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 1
总人数:10

进程已结束,退出代码为 0

这里是单个返回值的测试,当出现多个返回值时,就无法获得后面的值,只能获取第一个返回值

例如:

<select id="getCount" resultType="java.lang.Integer">
        select count(teamId) ,max(teamId) from team;
    </select>

这里有两个返回值,分别是 count(teamId) 和 max(teamId) 此时注意,count(teamId)在前面

我们测试下:

@Test
public void test01(){
	TeamMapper teamMapper= MyBatis.getSession.getMapper(TeamMapper.class);
    System.out.println(teamMapper.getCount());
}

测试结果:
………………………………
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 507819576.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1e44b638]
DEBUG [main] - ==>  Preparing: select count(teamId) ,max(teamId) from team;
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 1
总人数:10

这里能够看到,只返回了一个值,后面的 max 值压根没有返回出来

此时如果 max 的值在前面,那么返回的就是max的值。

9.2、返回多个值

返回多个值时,建议使用Map来进行封装多个返回值,这样就不会出现上面的只返回一个值的情况

一般我们用Map作为返回值的时候,key的类型一般是String,value 的类型一般是Object,因为我们也不清楚传过来的到底是什么类型的值。

TeamMapper

Map<String ,Object> getTwoParam();

TeamMapper映射文件

<select id="getTwoParam" resultType="java.util.HashMap" >
	select count(teamId),max(teamId) from team;
</select>

9.3、返回多行数据

直接返回多行数据会出现TooManyResultsException异常 简单来说就是正常只能获取一行数据,我们的结果太多了,所以需要通过List进行封装

TeamMapper

List<Team> findAll();
<!--返回值是一个集合,resultType返回值一定是元素的类型-->
<select id="findAll" resultType="com.bifapang.pojo.Team">
    select *
    from team;
</select>
	@Test
    public void test01(){
        //这里没写实现类,原理在于.getMapper(TeamMapper.class);底层帮助我们生成动态代理,系统生成的代理类通过指定的findAll方法作为id去配置文件中进行匹配,
        // 返回值为Team,那么就封装到List里面去
        TeamMapper mapper = sqlSession.getMapper(TeamMapper.class);
        System.out.println(mapper.getClass());
        List<Team> all = mapper.findAll();
        for (Team team : all) {
            System.out.println(team);
        }
    }

9.4、返回多行多列数据

举一反三,返回多个列的值需要使用Map来封装,返回多行的值需要使用List来封装,那么返回多行多列的数据就自然是通过List<Map<String,Object>> 意思就是,List集合中存放着Map集合,Map集合中用String作为key,用Objetc作为value,因为我们不清楚value到底是什么类型

返回值是一个集合,resultType返回值一定是元素的类型, 比如,返回值是多个球队的信息,那么返回值类型一定是List<Team>

Team就是元素的类型,如果是List<Map<String,Object>>,那么也就说明Map就是List的元素,resultType就是java.util.HashMap

TeamMapper

 List<Map<String ,Object>> queryByListByMap();

TeamMapper.xml

 <!--这里用Map来接受每一行的数据,每一个Map放进List里面去-->
    <select id="queryByListByMap" resultType="java.util.HashMap">
        select teamId,teamName,location from team;
    </select>

开始测试:

 @Test
    public void test03(){
        List<Map<String, Object>> maps = teamMapper.queryByListByMap();
        for (Map<String, Object> map : maps) {
            //把Map遍历出来,然后再遍历Map将值取出来
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                System.out.println(entry);
            }
        }
    }

测试结果:
…………………………
DEBUG [main] - ==>  Preparing: select teamId,teamName,location from team;
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 10
teamName=查宝宝
teamId=1
location=九江
teamName=毕文超
teamId=2
location=九江
teamName=三百斤的小胖子
teamId=5
location=地球村
teamName=赛少
teamId=7
location=m78
…………………………………………

进程已结束,退出代码为 0

9.5、resultMap

<resultMap> 标签是用来标记Sql查询的列名跟java对象的属性之间的映射关系

resultMap简单来说就是我们自己写映射关系,然后作为返回值放到select标签中

比较常用在数据库列名跟实体类属性名不一致时

TeamMapper

List<Team> findAll2();

TeamMapper.xml

这里分为两个部分写,一个写SQL语句,一个写 SQL 列名与 java 对象属性的映射

参数:

column

jdbcType 代表的是数据库中列名的属性 可以省略

javaType 代表的是实体类中对应的属性的类型 可以省略,因为MyBatis会自己推断出类型

resultMap 映射 可以再里面声明类型

<resultMap id="baseResultMap"
               type="com.bifapang.pojo.Team">
    <!--一般主键列用id,其余列用result column:表示数据库表中的列名,不区分大小写
               property:表示实体类中的对应的属性名,区分大小写
               javaType:实体类中的对应的属性的类型,可以省略,mybatis会自己推断
               jdbcType="数据库中的类型column的类型" 一般省略 -->
    <id column="teamId" property="teamId" javaType="java.lang.Integer"></id>
    <result column="teamName" property="teamName" javaType="java.lang.String"></result>
    <result column="location" property="location" javaType="java.lang.String"></result>
    <result column="createTime" property="createTime" javaType="java.util.Date"></result>
    </resultMap>

select语句

 <!--select标签中使用了参数resultMap,那么指向的就是自定义的一个映射关系-->
    <select id="findAll2" resultMap="baseResultMap">
    	select * from team; 
    </select>

测试类:

 @Test
    public void test04(){
        List<Team> all2 = teamMapper.findAll2();
        for (Team team : all2) {
            System.out.println(team);
        }
    }

测试类:
    DEBUG [main] - ==>  Preparing: select * from team;
    DEBUG [main] - ==> Parameters: 
    DEBUG [main] - <==      Total: 10
    Team(teamId=1, teamName=查宝宝, location=九江, createTime=Mon Aug 20 00:00:00 CST 2001)
    Team(teamId=2, teamName=毕文超, location=九江, createTime=Sat Apr 20 00:00:00 CST 2002)
    Team(teamId=5, teamName=三百斤的小胖子, location=地球村, createTime=Thu May 05 00:00:00 CST 2022)
    Team(teamId=7, teamName=赛少, location=m78, createTime=Fri May 06 00:00:00 CST 2022)
    Team(teamId=8, teamName=赛罗, location=光之国, createTime=Fri May 06 00:00:00 CST 2022)
    Team(teamId=10, teamName=雷格罗斯1, location=G60, createTime=Fri May 06 00:00:00 CST 2022)
    Team(teamId=11, teamName=雷格罗斯, location=G60, createTime=Fri May 06 00:00:00 CST 2022)
    Team(teamId=12, teamName=雷格罗斯, location=G60, createTime=Fri May 06 00:00:00 CST 2022)
    Team(teamId=13, teamName=雷格罗斯1, location=G60, createTime=Fri May 06 00:00:00 CST 2022)
    Team(teamId=14, teamName=雷格罗斯2, location=G60, createTime=Sun May 08 00:00:00 CST 2022)

    进程已结束,退出代码为 0

那么可能会有人感觉结果跟findAll没有任何区别,那么通过下面这个案例来解释

首先新建一张表

use mybatis; 
DROP TABLE IF EXISTS `users`; 
CREATE TABLE `users` ( 
    `user_id` int NOT NULL AUTO_INCREMENT COMMENT '用户id', 
    `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户姓名', 
    `user_age` int NULL DEFAULT NULL COMMENT '用户年龄', 
    PRIMARY KEY (`user_id`) USING BTREE 
	) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 
	INSERT INTO `users` VALUES (1, '贾宝玉', 14); 
	INSERT INTO `users` VALUES (2, '林黛玉', 13); 
	INSERT INTO `users` VALUES (3, '薛宝钗', 15); 
SET FOREIGN_KEY_CHECKS = 1;

然后我们区别列名去写一个java对象的属性:

import lombok.Data;

@Data
public class Users { 
    private Integer userId; 
    private String userName; 
    private Integer userAge;
}

此时注意java对象的属性名跟mysql表的列名是不一样的

然后我们去写 mapper 文件

public interface UserMapper {
    Users findUserById(Integer userId);
}

此时编写相应的配置文件

<select id="findUserById" resultType="com.bifapang.pojo.Users">
       select * from users where user_id=#{userId}
</select>

别忘了去mybatis.xml中将配置文件添加进去

    <mappers>
        <!--有多少个表多少个映射文件,这里就写多少个-->
     	……………………
        <mapper resource="com/bifapang/mapper/UserMapper.xml"/>
    </mappers>

然后去进行测试

测试类:

private UserMapper userMapper= MyBatisUtil.getSession().getMapper(UserMapper.class);
@Test
    public void test05(){
        Users userById = userMapper.findUserById(1);
        System.out.println(userById);
    }

测试结果:
        DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@17d88132]
    DEBUG [main] - ==>  Preparing: select * from users where user_id=?
    DEBUG [main] - ==> Parameters: 1(Integer)
    DEBUG [main] - <==      Total: 1
    null
    
进程已结束,退出代码为 0

这里能够看到,当列名与属性名不相同时,值是无法获取的,就会出现空,这种情况就需要使用到resultMap

通过resultMap来指定名称的映射关系

<select id="findUserById" resultMap="baseResultMap">
       select * from users where user_id=#{userId}
   </select>

<resultMap id="baseResultMap" type="com.bifapang.pojo.Users">
        <result column="user_id" property="userId"/>
        <result column="user_name" property="userName"/>
        <result column="user_age" property="userAge"/>
    </resultMap>

在指定了映射关系后,我们开始测试:

@Test
    public void test05(){
        Users userById = userMapper.findUserById(1);
        System.out.println(userById);
    }
测试结果:
    DEBUG [main] - ==>  Preparing: select * from users where user_id=?
    DEBUG [main] - ==> Parameters: 1(Integer)
    DEBUG [main] - <==      Total: 1
    Users(userId=1, userName=贾宝玉, userAge=14)

    进程已结束,退出代码为 0

此时结果出现!

9.6、通过别名解决列名属性名不一致问题

除了直接通过resultMap来指定,还可以直接通过别名的方式来指定,但是一般不推荐,因为容易混淆

语法: select 列名 as/空格 java属性名 列名2 as/空格 java属性名2 …… from 表名

<select id="findUserById" resultType="com.bifapang.pojo.Users">
    select user_id as userId,user_name as userName, user_age as userAge from users where user_id=#{userId}
</select>

测试:

@Test
    public void test05(){
        Users userById = userMapper.findUserById(1);
        System.out.println(userById);
    }
测试结果:
    DEBUG [main] - ==>  Preparing: select * from users where user_id=?
    DEBUG [main] - ==> Parameters: 1(Integer)
    DEBUG [main] - <==      Total: 1
    Users(userId=1, userName=贾宝玉, userAge=14)

    进程已结束,退出代码为 0

10、动态SQL

动态SQL是MyBatis中非常强大且实用的功能。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

10.1、where标签在select中的使用

where标签是在有查询条件的时候去写的

原来我们的多条件分析都是通过java中的字符串进行拼接的

//原有的多条件分析:都是通过java中的字符串拼接实现 

String sql="select * from team where 1 = 1 "; 

// 如果用户输入了名称,就模糊查询 

and teamName like '%?%' 

// 如果用户输入了日期,按照日期区间查询 

and createTime> ? and createTime< ? 

//如果输入了地区,按照地区查询 

and location =?"; 

而现在可以通过 if 标签去嵌套到 where 标签中,if 作为条件,只有满足了这个条件才会执行里面的 where 条件

这里的多判断语句就直接写在where标签中了 ,这里的and在只有一个条件的时候,MyBatis会自动去掉它

<select id="findTeam" resultType="com.bifapang.pojo.Team">
        select * from team
        <where>
            <if test="teamId!=null">
                and teamId = #{teamId}
            </if>
            <if test="teamName!=null">
                and teamName =#{teamName}
            </if>
            <if test="location!=null">
                and location=#{location}
            </if>
            <if test="createTime">
                and createTime=#{createTime}
            </if>
        </where>
    </select>

这样就不会影响前面测试的java代码

测试:

@Test
    public void test07(){
        Team team = new Team();
        team.setTeamName("查宝宝");
        team.setTeamId(1);
        List<Team> team1 = teamMapper.findTeam(team);
        for (Team team2 : team1) {
            System.out.println(team2);
        }
    }

测试结果:
    DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@14fc1f0]
    DEBUG [main] - ==>  Preparing: select * from team WHERE teamId = ? and teamName =?
    DEBUG [main] - ==> Parameters: 1(Integer), 查宝宝(String)
    DEBUG [main] - <==      Total: 1
    Team(teamId=1, teamName=查宝宝, location=九江, createTime=Mon Aug 20 00:00:00 CST 2001)

注意看

image20220512225503740.png

这里值设置了id跟name,其他的没有设置,所以其他的也不会出现在SQL语句中

10.2、set标签在select中的使用

修改操作的时候可以使用

TeamMapper

void updateTeam(Team team);

TeamMapper.xml

 <!--根据id修改表中内容-->
    <update id="updateTeam" parameterType="com.bifapang.pojo.Team">
        update team
        <set>
            <if test="teamName!=null">
                teamName=#{teamName},
            </if>
            <if test="location!=null">
                location=#{location},
            </if>
            <if test="createTime!=null">
                createTime=#{createTime},
            </if>
        </set>
        where teamId=#{teamId}
    </update>

注意每个语句后面需要加逗号

开始测试

@Test
public void test08() {
    SqlSession session = MyBatisUtil.getSession();
    TeamMapper teamMapper = session.getMapper(TeamMapper.class);
    Team team = new Team();
    team.setTeamName("查宝宝");
    team.setLocation("毕发胖的1211");
    team.setCreateTime(LocalDate.now());
    team.setTeamId(1);
    teamMapper.updateTeam(team);
    session.commit();
}
测试结果:
    DEBUG [main] - Setting autocommit to false on JDBC Connection 		   
    [com.mysql.cj.jdbc.ConnectionImpl@3abada5a]
	DEBUG [main] - ==>  Preparing: update team SET teamName=?, location=?, createTime=? where teamId=?
	DEBUG [main] - ==> Parameters: 查宝宝(String), 毕发胖的1211(String), 2022-05-13(LocalDate), 1(Long)
	DEBUG [main] - <==    Updates: 1
	DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3abada5a]

进程已结束,退出代码为 0

10.3、forEach

批量添加

批量删除

TeamMapper

void deleteTeam(List<Integer> teamList);

TeamMapper.xml

<delete id="deleteTeam" parameterType="com.bifapang.pojo.Team">
    delete from team where teamId in
    <foreach collection="list" item="teamId"
             separator="," open="("
             close=")">    <!--collection代表的是声明当前参数是什么类型,这里声明list集合,item声明list集合里到底放了什么类型,separtor代表的是用什么作为分隔-->
        #{teamId}
    </foreach>
</delete>

开始测试:

@Test
public void test09() {
    List<Integer> list=new ArrayList<>();
    list.add(10);list.add(11);list.add(12);list.add(13);
    teamMapper.deleteTeam(list);
    MyBatisUtil.getSession().commit();
}
测试结果:
    DEBUG [main] - ==>  Preparing: delete from team where teamId in ( ? , ? , ? , ? )
	DEBUG [main] - ==> Parameters: 10(Integer), 11(Integer), 12(Integer), 13(Integer)

11、全局配置文件

11.3、别名

11.3.1、内置别名

当我们去写返回值的时候,总是需要写全限定名,这么写很麻烦,就出现了一种别名简写的方式

例如:java.lang.Integer 别名:int 或 integer

java.util.HashMap 别名:hashmap

Mybatis内置的别名有如下:

image20220518200155944.png

11.3.2 、自定义别名

有两种方式:

​ 1、配置单独的对象

​ 2、直接扫描整个包,在该包下的对象,别名都是类名同名或者首字母小写

<typeAliases>
        两种方式,第一种,通过typeAlias来指定全限定名与别名
            第二种,直接扫描整个包,在该包底下的pojo类,都可以直接使用别名,这个别名就是跟类名同名或者首字母小写
            ,例如 com.bifapang.pojo.Team ,可以直接写team或者Team &ndash;&gt;
        <typeAlias type="com.bifapang.pojo.Team" alias="team"/>
        <package name="com.bifapang.pojo"/>
</typeAliases>-->

11.4、映射器Mapper

11.4.1、使用相对于类路径的资源引用

语法:<mapper resource=""/>
使用相对于类路径的资源,从 classpath 路径查找文件
例如:<mapper resource="com/kkb/mapper/TeamMapper.xml" />

11.4.2、使用映射器接口实现类的完全限定类名

语法:<mapper class=""/>
使用的mapper接口的完全限定名
要求:接口和映射文件同包同名
例如<mapper class="com.kkb.mapper.GameRecordMapper"/>

11.4.3、将包内的映射器接口实现全部注册为映射器--推荐

语法:<package name=""/>
指定包下的所有Mapper接口
如:<package name="com.kkb.mapper"/>
注意:此种方法要求 Mapper接口名称和 mapper 映射文件名称相同,且在同一个目录中。

11.5、dataSource标签

Mybatis 中访问数据库支持连接池技术,而且是采用的自己的连接池技术。在 Mybatis 的 mybatis.xml配置文件中,通过来实现 Mybatis 中连接池的配置。MyBatis 在初始化 时,根据的 type 属性来创建相应类型的的数据源 DataSource。

Mybatis 的数据源分为三类:

<!--POOLED 表示使用连接池
  UNPOOLED  表示不使用连接池
  JNDI 表示使用JNDI的数据源
  前两个都实现 javax.sql.DataSource 接口-->

11.6、事务

11.6.1、默认需要手动提交事务

Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的 Connection 对象的 commit(), rollback(),Connection 对象的 setAutoCommit() 方法来 设置事务提交方式的。自动提交和手工提交、

<transactionManager type="JDBC"/>

该标签用于 MyBatis 所使用的事务管理器,MyBatis所支持的事务管理器包括 JDBC 和 MANAGED

JDBC:使用JDBC的事务管理机制,通过Connection对象的 commit()方法提交,通过rollback()方法 回滚。默认情况下,mybatis将自动提交功能关闭了,改为了手动提交,观察 日志可以看出,所以我们在程序中都需要自己提交事务或者回滚事务。

12、MyBatis中的关系映射

12.1、实体类:

image20220520094050380.png

12.2、mapper接口

package com.bifapang.mapper;

import com.bifapang.pojo.Player;

/**
 * @Description: 面向对象面向君 ,不负对象不负卿
 * @Author: bifapang
 * @Date: 2022/5/19 10:46
 */
public interface PlayerMapper {
    Player queryById1(int playerId);
    Player queryById2(int playerId);
    Player queryById3(int playerId);
    Player queryById4(int playerId);
}

12.3、对一关系映射 方式一 通过关联对象打点调用属性来建立映射管理

要求:两表的连接查询

<!--方式一: 通过对象打点属性直接映射列名-->
<select id="queryById2" resultMap="joinTeamResultMap">
    select *
    from player p
             inner join team t on p.teamId = t.teamId
    where playerId = #{id}
</select>

<resultMap id="baseResultMap" type="Player">
        <id column="playerId" property="playerId"></id>
        <result column="playerName" property="playerName"></result>
        <result column="playerNum" property="playerNum"></result>
        <result column="teamId" property="teamId"></result>
    </resultMap>

<!--这里继承baseResultMap 相当于baseResultMap里面建立的映射关系在joinTeamResultMap也存在,
	 这里因为也要查询Team表,使team表跟Player表在一起做连接查询,需要为那个外键列做关系映射,
	这里的team1就是player实体类中的外键属性-->
<resultMap id="joinTeamResultMap" type="Player" extends="baseResultMap">
    <!--这里的team1指代的就是Player的属性team1-->
    <id column="teamId" property="team1.teamId"></id>
    <result column="teamName" property="team1.teamName"></result>
    <result column="location" property="team1.location"></result>
    <result column="createTime" property="team1.createTime"></result>
</resultMap>

12.4、对一关系映射 方式二 直接引用关联对象的Mapper映射

要求:1、两表的连接查询

​ 2、关联对象中已经存在被引用的resultMap

association 是多对一的时候使用的,这里是多名球员持有一个球队

collection hi

<!--方式二:直接引用关联对象的 Mapper 映射-->
    <select id="queryById3" resultMap="joinTeamResultMap2">
        select *
        from player p
                 inner join team t on p.teamId = t.teamId
        where playerId = #{id}
    </select>

<resultMap id="baseResultMap" type="Player">
        <id column="playerId" property="playerId"></id>
        <result column="playerName" property="playerName"></result>
        <result column="playerNum" property="playerNum"></result>
        <result column="teamId" property="teamId"></result>
    </resultMap>

    <!--这里指定属性为team2 javaType指定该属性的类型 resultMap指定关联对象的关系映射 这里的baseResultMap指定的是TeamMapper.xml中的baseResultMap-->
    <resultMap id="joinTeamResultMap2" type="Player" extends="baseResultMap">
        <association property="team2" javaType="Team"
                     resultMap="com.bifapang.mapper.TeamMapper.baseResultMap"/>
    </resultMap>

12.5、对一关系映射 方式三 直接引用关联对象的单独查询的方法(相当于执行两次查询)

要求:1、不需要两表的连接查询

​ 2、关联对象中已经存在被引用的查询方法

<!--方式三:使用关联对象单独的查询语句
    要求:不需要连接查询
    需要关联对象当中存在对应的查询语句
    -->
<select id="queryById4" resultMap="joinTeamResultMap3">
    select *
    from player
    where playerId = #{Id}
</select>

<resultMap id="baseResultMap" type="Player">
        <id column="playerId" property="playerId"></id>
        <result column="playerName" property="playerName"></result>
        <result column="playerNum" property="playerNum"></result>
        <result column="teamId" property="teamId"></result>
    </resultMap>


<!--findById需要一个id参数,那么这个参数一般就是两表连接的那个列,也就是外键列,就是teamId-->
<resultMap id="joinTeamResultMap3" type="Player" extends="baseResultMap">
    <association property="team3" javaType="Team" 
                 select="com.bifapang.mapper.TeamMapper.findById" column="teamId"/>
</resultMap>

13、分页插件

pom文件添加依赖

<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.10</version>
</dependency>

13.1、在Mybatis全局配置文件中添加插件配置

<plugins>
    <!--在5.0之前,使用的是pageHelper 5.0以后使用PageInterceptor -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="reasonable" value="true"/>  
        <!--配置分页合理性,例如查询页数小于1就默认查询第一页,查询页数大于最大页数,就查询最后页数,默认为false,
		方言可以省略,会根据连接数据的参数url自动推断-->
    </plugin>
</plugins>

13.2、插件的使用

 @Test
    public void test10(){
        // PageHelper.startPage 必须紧邻查询语句,而且只对第一条查询语句生效,如果底下再来一个query就不生效了
        PageHelper.startPage(15,5);
        List<Team> teams = teamMapper.queryByPage(0,5);
        teams.forEach(System.out::println);
        PageInfo<Team> pageInfo=new PageInfo<>(teams);
        System.out.println("获取分页的信息如下:");
        System.out.println("当前页:"+pageInfo.getPageNum());
        System.out.println("每页的数量:"+pageInfo.getPageSize());
        System.out.println("当前页的数量:"+pageInfo.getSize());   //当前页就是实际的页的数量
        System.out.println("总页数:"+pageInfo.getPages());
        System.out.println("上一页:"+pageInfo.getPrePage());
        System.out.println("下一页:"+pageInfo.getNextPage());
        //获得所有导航页号 [1, 2, 3, 4, 5, 6, 7]
        System.out.println("所有导航页号:"+ Arrays.toString(pageInfo.getNavigatepageNums()));    
    }

PageInfo.java的部分源码:

package com.github.pagehelper;
import java.util.Collection;
import java.util.List;
/**
* 对Page<E>结果进行包装
* <p/>
* 新增分页的多项属性,主要参考:http://bbs.csdn.net/topics/360010907
*
* @author liuzh/abel533/isea533
* @version 3.3.0
* @since 3.2.2
* 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class PageInfo<T> extends PageSerializable<T> {
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总页数
private int pages;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;

14、MyBatis缓存

14.1、缓存作用

缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。

将经常查询的数据存在缓存(内存)中,用户查询该数据的时候不需要从磁盘(关系型 数据库文件)上查询,而是直接从缓存中查询,提高查询效率,解决高并发问题。 MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。 Mybatis的缓存结构体系:

14.2、一级缓存:自动开启,SqlSession级别的缓存

在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不 影响的。

一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数 据将不再从数据库查询,从而提高查询效率。

当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。

Mybatis默认开启一级缓存,存在内存中(本地缓存)不能被关闭,可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。

14.2.1 一级缓存分析

工作原理图:image20220523195616622.png

当用户发起第一次查询team=1001的时候,先去缓存中查找是否有team=1001的对象;

如果没有,继续向数据中发送查询语句,查询成功之后会将teamId=1001的结果存入缓存 中;

当用户发起第2次查询team=1001的时候,先去缓存中查找是否有team=1001的对象,因为第一次查询成功之后已经存储到缓存中,此时可以直接从缓存中获取到该数据,意味 着不需要再去向数据库发送查询语句。

如果SqlSession执行了commit(有增删改的操作),此时该SqlSession对应的缓存区域被整个清空,目的避免脏读。

前提:SqlSession未关闭。

14.2.2、清空缓存的方式

1、 session.clearCache( ) ;

2、 execute update(增删改) ;

3、 session.close( );

4、 xml配置 flushCache="true" ;

5、 rollback;

6、 commit。

14.3、二级缓存:Mapper级别的缓存

多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession 的。

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace。

不同的sqlSession两次执行相同namespace下的sql语句参数相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获 取数据将不再从数据库查询,从而提高查询效率。

MyBatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。 如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

14.3.1、二级缓存的原理分析

image20220523161903903.png

14.3.2、二级缓存的使用步骤

首先需要去MyBatis全局配置文件中去配置

二级缓存是Mapper级别的,默认不启用

<!--配置二级缓存 cacheEnabled 默认为false-->
<setting name="cacheEnabled" value="true"/>

在需要二级缓存的Mapper中添加缓存标志

image20220523194617449.png

在需要缓存的实体类上添加 Serializable 接口

另外二级缓存仅在同一个Factory中有效,也就是,不同的Factory中缓存无法共享

简单来说,想要使用二级缓存,必要的配置就是:

全局配置文件开启了二级缓存;

TeamMapper.xml配置了缓存;

Team实体类实现了序列化接口

14.4.3、二级缓存的禁用

当我们在 Mapper 中添加了缓存的标志,那么里面只要使用了 select 标签的都会自动默认开启二级缓存

但是对于变化比较频繁SQL语句,我们可以禁用二级缓存,需要在select标签中添加属性useCache

image20220523201025861.png

14.4.4、缓存中的属性配置

<cache>
	<property name="eviction" value="LRU"/><!--回收策略为LRU-->
	<property name="flushInterval" value="60000"/><!--自动刷新时间间隔为60S-->	//也就是60秒后缓存清空	
	<property name="size" value="1024"/><!--最多缓存1024个引用对象-->
	<property name="readOnly" value="true"/><!--只读-->
</cache>
3.缓存会默认使用LRU(Least Recently Used)算法来收回;
3.1、LRU – 最近最少使用的:移除最长时间不被使用的对象。
3.2、FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3.3、SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
3.4、WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

如果想要在当前的Mapper中调用其他Mapper中的缓存配置,可以通过 cache-ref来指定

image20220523203346211.png

15、反向生成插件

15.1、插件的配置

在pom.xml中配置添加如下配置:

<!--反向生成插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <!--配置文件的路径-->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.5</version>
                    </dependency>
                </dependencies>
            </plugin>

创建插件配置文件:

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!-- 配置生成器 -->
<generatorConfiguration>
    <!--1、数据库驱动jar:添加自己的jar路径 -->
    <classPathEntry
            location="J:\repository\mysql\mysql-connector-java\8.0.23\mysql-connector-java-8.0.23.jar" />
    <context id="MyBatis" targetRuntime="MyBatis3">
        <!--去除注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--2、数据库连接 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"
                        userId="root"
                        password="123456">
        </jdbcConnection>
        <!--
       就是数据库类型转成java类型
        默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer;
        为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!--3、生成实体类 指定包名 以及生成的地址 (可以自定义地址,但是路径不存在不会自动创建
        使用Maven生成在target目录下,会自动创建) -->
        <javaModelGenerator targetPackage="com.kkb.pojo"
                            targetProject="src\main\java">
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!--4、生成SQLmapper.xml映射文件 -->
        <sqlMapGenerator targetPackage="com.kkb.mapper"
                         targetProject="src\main\resources">
        </sqlMapGenerator>
        <!--5、生成Dao(Mapper)接口文件,-->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.kkb.mapper"
                             targetProject="src\main\java">
        </javaClientGenerator>
        <!--6、要生成哪些表(更改tableName和domainObjectName就可以) -->
        <!-- tableName:要生成的表名
        enableCountByExample:Count语句中加入where条件查询,默认为true开启
        enableUpdateByExample:Update语句中加入where条件查询,默认为true开启
        enableDeleteByExample:Delete语句中加入where条件查询,默认为true开启
        enableSelectByExample:Select多条语句中加入where条件查询,默认为true开启
        selectByExampleQueryId:Select单个对象语句中加入where条件查询,默认为true开启
        -->
        <table tableName="Team"	<!--里面不添加参数的话,就会自动生成基本的增删改查的功能-->
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableUpdateByPrimaryKey="false"
               enableDeleteByExample="false"
               enableDeleteByPrimaryKey="false"
               enableSelectByExample="false"
               selectByExampleQueryId="false">
            <property name="useActualColumnNames" value="true"/>	<!-如果不添加这个参数的话,生成的实体类的属性就是小写的,因为mysql中列是不区分大小写的,直接转字段过来不符合java命名规范,就需要传入这个参数来进行调整,但是需要注意的是,如果表中的字段名带了下划线的话,就不需要使用该插件,带了下划线就可以自动生成符合规范的-->
        </table>
    </context>
</generatorConfiguration>

在使用插件生成的时候,如果连续生成两次就会出现追加的情况,该插件所使用的是追加而不是覆盖。

另外如果我们的其他数据库中也出现了同名的表,该插件也会跟着一起生成,所以需要去看下是不是生成对应的那个表

我们可以通过去Mapper文件中查看resultMap是否只有一个,如果是,那么就是正确生成的,如果出现了多个,那么就是重复生成了

15.2、反向生成插件的使用

1

评论区