Mybatis- 简单整合

Mybatis- 简单整合

原生方式整合

参考官方档中章节:mybatis - MyBatis 3 | Getting started

基础模块

直接在 IDEA 中新建模块

会创建一个标准的 Maven 构建的 JavaSE 项目的模块结构

假设根包名为 xyz.xiashuo,我们在根包名下创建 domain 包,并创建 User

public class User {

    private Integer id;

    private String username;

    private String password;

    private Integer age;

    private String gender;

    private String email;

    // 省略属性的getter、setter方法,有参构造和无参构造、toString()方法
    .... 

}

同时在 MySql 数据库创建中创建模式 MyBatis,并在其中创建表 t_user,建表语句如下

create table t_user
(
    id       int auto_increment,
    username varchar(20) null,
    password varchar(20) null,
    age      int         null,
    gender   char        null,
    email    varchar(20) null,
    constraint t_user_pk
        primary key (id)
)
    comment '用户表';

开始整合

在 POM 文件中添加依赖

<dependencies>
    <!-- Mybatis核心 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.11</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.9.1</version>
        <scope>test</scope>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.31</version>
    </dependency>
</dependencies>

其中核心的 jar 包就一个,就是 mybatis,此外注意,MySQL 驱动版本要跟 MySQL 数据库匹配,比如我当前的 MySQL 数据库的版本是 8,那么 mysql-connector-java 的版本最好也是 8

然后开始创建核心配置文件,核心配置文件主要用于配置连接数据库的环境以及 MyBatis 的全局配置信息

框架的本质,就是 jar 包加上配置文件

MyBatis 的配置文件分为两种

src/main/resources 下创建 mybatis-config.xml,

没有默认名字,习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合 Spring 之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴

<?xml version="1.0" encoding="UTF-8" ?>
<!-- !DOCTYPE 后面的单词,必定是xml文件的根标签-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--设置连接数据库的环境-->
    <environments default="development">
        <!-- 可配置多种环境下的数据库配置 -->
        <environment id="development">
            <!-- 事务管理器-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- Mysql新版驱动类-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!-- 我自己的个人服务器上的Mysql数据库-->
                <property name="url" value="jdbc:mysql://xiashuo.xyz:3306/MyBatis"/>
                <property name="username" value="mysqlXS"/>
                <property name="password" value="mySql@327541"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入SQL映射文件-->
    <mappers>
        <!-- resource 路径的起点是类路径 -->
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>

接下来创建 Mapper 接口,MyBatis 中的 mapper 接口相当于以前的 dao。但是区别在于,mapper 仅仅是接口,我们不需要提供实现类。

创建 xyz.xiashuo.mapper,然后创建 UserMapper 接口,对应 domain 包下的 User,对应 Mysql 中的 t_user

// 对应domain 包下的 User 
// 对应 Mysql 中的 t_user 表
public interface UserMapper {

    /**
     * * 添加用户信息
     */
    int insertUser();

}

接下来开始创建 MyBatis 的映射文件,MyBatis 映射文件用于编写 SQL,访问以及操作表中的数据,MyBatis 映射文件存放的位置是 src/main/resources/mappers 目录下,当然你可以放在别的路径下,注意映射文件的路径要在核心配置文件中的 <mappers> 下的 <mapper> 标签中进行配置,否则映射文件无法生效

首先我们看看 ORM 框架的含义

ORM(Object Relationship Mapping)对象关系映射。其中:

Java 概念 数据库概念
属性 字段/列
对象 记录/行

映射文件有两点需要注意:

  1. 映射文件的命名规则:

    表所对应的实体类的类名+Mapper.xml。例如:表 t_user,映射的实体类为 User,所对应的映射文件为 UserMapper.xml

    因此一个映射文件对应一个实体类,对应一张表的操作

  2. MyBatis 中可以面向接口操作数据,要保证两个一致:

    • mapper 接口的全类名和 xml 映射文件的命名空间(<mapper> 跟标签的 namespace 属性)保持一致

    • mapper 接口中方法的方法名和 xml 映射文件中编写 SQL 的标签的 id 属性保持一致

<?xml version="1.0" encoding="UTF-8" ?>
<!-- !DOCTYPE 后面的单词,必定是xml文件的根标签-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 属性保存的是此映射文件对应的mapper接口  -->
<mapper namespace="xyz.xiashuo.mapper.UserMapper">

    <!-- id属性为此映射文件对应的mapper接口中的方法名   -->
    <insert id="insertUser">
        insert into t_user
        values (null, '张三', '123', 23, '女', '[email protected]')
    </insert>

</mapper>

注意,IDEA 安装 MyBatisX 插件之后,可以很方便地在 mapper 接口和映射文件之间进行跳转

在映射文件中

在 mapper 接口中

甚至 mapper 接口和映射文件的图标也是特殊图标

简单测试

public class UserMapperTest {

    SqlSession sqlSession = null;

    /**
     * 方便多个测试方法,直接将获取 SqlSession 的内容提取出来
     */
    @BeforeEach
    public void getSqlSession() throws IOException {
        //1. 读取MyBatis的核心配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //2. 创建SqlSessionFactoryBuilder对象,通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        //3. 创建SqlSession对象,
        // openSession方法参数为空时,通过SqlSession对象所操作的sql都必须手动提交或回滚事务
        // SqlSession sqlSession = sqlSessionFactory.openSession();
        // openSession方法参数为true时,通过SqlSession对象所操作的sql都会自动提交
        sqlSession = sqlSessionFactory.openSession(true);
    }

    @Test
    public void testInsertUser() throws IOException {
        if (sqlSession == null) {
            return;
        }
        //通过代理模式创建UserMapper接口的代理实现类对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
        int result = userMapper.insertUser();
        //sqlSession.commit()
        System.out.println("结果:" + result);

    }
}

执行日志:

结果:1

简单分析一下 Mybatis 原生的使用过程:

  1. 读取 MyBatis 的核心配置文件

  2. 创建 SqlSessionFactoryBuilder 对象,通过核心配置文件所对应的字节输入流创建工厂类 SqlSessionFactory,生产 SqlSession 对象

  3. 创建 SqlSession 对象

    • openSession 方法参数为空时,通过 SqlSession 对象所操作的 sql 都必须手动提交或回滚事务

    • openSession 方法参数为 true 时,通过 SqlSession 对象所操作的 sql 都会自动提交

    openSession 方法应该使用了 Mybatis 配置文件中的数据库配置,这个以后有时间再去研究,TODO

  4. 调用 getMapper 方法,通过代理模式创建 XXXMapper 接口的代理实现类对象

    getMapper 方法应该有 xml 配置文件的查找过程,这个以后有时间再去研究,TODO

  5. 调用 XXXMapper 接口中的方法

这个测试类中,记录的,实际上是Mybatis 原生的使用方法,其中最终要的操作对象是SqlSession代表 Java 程序和数据库之间的(连接)会话。(HttpSession 是 Java 程序和浏览器之间的会话),在获取 SqlSession 对象的时候,我们可以指定四个方面的信息

SqlSessionFactory 的源码:

public interface SqlSessionFactory {
  // 默认 
  SqlSession openSession();

  // 设置是否自动提交   
  SqlSession openSession(boolean autoCommit);

  // 直接通过 Connection 连接创建会话 
  SqlSession openSession(Connection connection);

  // 指定事务的隔离级别,重要
  SqlSession openSession(TransactionIsolationLevel level);

  // 指定执行器的类型,再配合上面三个条件 
  // 执行器有三种:SimpleExecutor、ReuseExecutor、BatchExecutor   
  SqlSession openSession(ExecutorType execType);

  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}

这里,我们最常见的,其实是事务是否自动提交的问题,默认情况下,通过 openSession() 获取的 SqlSession 在获取 mapper 接口的实例之后,执行完 mapper 接口中的方法,是需要手动提交的,但是通过 openSession(true) 获取的 SqlSession 在获取 mapper 接口的实例之后,执行完 mapper 接口中的方法,会自动提交。

一般情况下,简单的增删改查的 SQL,手动提交太麻烦,我们都会设置自动提交,而一些业务复杂,设置到回滚的事务操作,我们最好手动提交和回滚

其实通过查看 SqlSession 接口的方法,发现SqlSession 不仅可以通过 getMapper 方法获取 mapper 接口实例,还可以直接执行 sql 语句,非常的方便和牛逼。这个以后有时间再去研究,TODO

添加日志 - log4j2

参考官方文档:mybatis - MyBatis 3 | Logging

在 pom 中添加依赖

<!--  日志相关-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.17.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.17.2</version>
</dependency>

src/main/resources 中添加配置文件,log4j2.xml,此配置会将日志同时 sql 的执行日志同时输出到工作目录和控制台之中

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" monitorInterval="30">
    <properties>
        <property name="LOG_HOME">${sys:user.dir}\logs</property>
    </properties>

    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        <File name="SqlFile" fileName="${LOG_HOME}/sqlFile.log" append="true">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </File>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
        <!-- name 为 mapper类所在的包-->
        <Logger name="xyz.xiashuo.mapper" level="debug" additivity="false">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="SqlFile"/>
        </Logger>
    </Loggers>
</Configuration>

输出日志:

2022-10-28 12:10:56.102 [main] DEBUG xyz.xiashuo.mapper.UserMapper.insertUser - ==>  Preparing: insert into t_user values (null, '张三', '123', 23, '女', '[email protected]')
2022-10-28 12:10:56.120 [main] DEBUG xyz.xiashuo.mapper.UserMapper.insertUser - ==> Parameters: 
2022-10-28 12:10:56.191 [main] DEBUG xyz.xiashuo.mapper.UserMapper.insertUser - <==    Updates: 1

工作目录:

如果是在 IDEA 中,工作目录就是当前模块所在项目的根路径

如果是打成 jar 包,那么工作目录就是执行 java -jar 的目录

具体请看《Java 中的常见路径和资源获取.md

重点:

log4j 相关知识请查看《log4j2_Document.docx》

添加更新和删除的方法

UserMapper 接口中添加

int updateUser();
int deleteUser();

UserMapper.xml 映射文件中添加

<update id="updateUser">
    update t_user set username = 'xiashuo' where id = 3
</update>

<delete id="deleteUser">
    delete from t_user where id = 4
</delete>

测试类中添加

@Test
public void testUpdateUser() throws IOException {
    if (sqlSession == null) {
        return;
    }
    //4. 通过代理模式创建UserMapper接口的代理实现类对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
    int result = userMapper.updateUser();
    //sqlSession.commit()
    System.out.println("结果:" + result);

}
@Test
public void testDeleteUser() throws IOException {
    if (sqlSession == null) {
        return;
    }
    //4. 通过代理模式创建UserMapper接口的代理实现类对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
    int result = userMapper.deleteUser();
    //sqlSession.commit()
    System.out.println("结果:" + result);

}

查询功能

跟更新和删除的区别就是,查询时必定有返回值的

映射文件中的 SQL 语句的标签中的 resultType 或者 resultMap 属性的功能时指导 Mybatis 处理 SQL 语句的结果集,<select> 标签必须设置其中一个标签

简单使用一下 resultType

UserMapper 接口中添加

User getUser();
List<User> getUsers();

UserMapper.xml 映射文件中添加

<select id="getUser" resultType="xyz.xiashuo.domain.User">
    select *
    from t_user
    where id = 5
</select>

<select id="getUsers" resultType="xyz.xiashuo.domain.User">
    select *
    from t_user
</select>

测试类中添加

@Test
public void testSelectUser() throws IOException {
    if (sqlSession == null) {
        return;
    }
    //4. 通过代理模式创建UserMapper接口的代理实现类对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
    User result = userMapper.getUser();
    System.out.println("结果:" + result);
}

@Test
public void testSelectUsers() throws IOException {
    if (sqlSession == null) {
        return;
    }
    //4. 通过代理模式创建UserMapper接口的代理实现类对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
    List<User> result = userMapper.getUsers();
    System.out.println("结果:");
    result.forEachprintln;

}

在 SpringBoot 中的整合

以后有时间再增加,TODO。