MyBatis(一)

目录

MyBatis是什么?

MyBatis入门

创建工程

数据准备

连接数据库

编写代码

单元测试

日志

参数传递

MyBatis的增删改查

增加(Insert)

删除(Delete)

修改(Update)

查询(Select)

XML配置文件

增加(Insert)

删除(Delete)

修改(Update)

查询(Select)


MyBatis是什么?

MyBatis 是一个持久层框架,它是用 Java 编程语言编写的,用于管理数据库访问代码,帮助开发者在 Java 应用程序中轻松地与数据库进行交互。

MyBatis 的核心思想是将 SQL 语句从 Java 代码中分离出来,使得 SQL 语句和 Java 代码可以分开维护和管理。通过 XML 文件或 注解 来配置 SQL 映射关系,使得开发者可以更灵活地编写和管理 SQL 查询语句。

web应用程序一般分为三层:Controller、Service 和 Dao

浏览器发起请求,先请求Controller,Controller接收到请求后,调用Service进行业务逻辑处理,Service再调用Dao从数据库中获取数据

Controller(表现层):负责接收来自用户的请求并调用相应的业务逻辑进行处理

Service(业务层):负责实现应用程序的业务逻辑,包括对业务规则的处理、数据的验证和计算等

Dao(持久层):负责将应用程序的数据持久化到数据库中,并提供对数据库的访问操作

MyBatis 是持久层框架,其目的是为了更加简单的完成程序和数据库交互,是一个更简单的操作和读取数据库的工具

简单了解了什么是MyBatis后,接下来,我们就来学习如何使用MyBatis

MyBatis入门

要使用MyBatis,我们首先要引入MyBatis相关依赖,配置MyBatis(数据库连接信息)

因此,我们从创建工程开始:

创建工程

创建 SpringBoot 工程,并引入MySQL的驱动包 和 MyBatis的起步依赖:

MyBatis是持久层框架,是帮助我们更简单地操作和读取数据库的工具,其具体数据存储和数据操作还是在MySQL中进行的,因此,需要添加MySQL驱动

项目创建完成后,会在pom.xml中导入MyBatis依赖 和 MySQL驱动依赖:

其中 MyBatis 依赖包的版本会随着SpringBoot版本发生变化:

可参考:mybatis-spring-boot-autoconfigure – Introduction

创建好项目后,我们需要创建数据表及实体类,为数据库操作做好准备

数据准备

创建用户表 userinfo:

-- 若数据库存在,则删除
DROP DATABASE IF EXISTS mybatis_test;
-- 创建数据库 
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;

 -- 使用数据库
USE mybatis_test;

-- 创建用户表
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (
 `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
 `username` VARCHAR ( 127 ) NOT NULL,
 `password` VARCHAR ( 127 ) NOT NULL,
 `age` TINYINT ( 4 ) NOT NULL,
 `gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1:男 2:女 0:默认',
 `phone` VARCHAR ( 15 ) DEFAULT NULL,
 `delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0:正常, 1:删除',
 `create_time` DATETIME DEFAULT now(),
 `update_time` DATETIME DEFAULT now(),
 PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;

添加信息:

-- 添加信息 
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '12312312311' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 19, 1, '12312312312' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 20, 2, '12312312313' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhaoliu', 'zhaoliu', 21, 2, '12312312314' );

此时,就已创建好了用户表:

接下来,我们创建对应的实体类 UserInfo:

import lombok.Data;
import java.util.Date;

@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

接下来,我们将 MyBatis 连接到数据库

连接数据库

MyBatis中要连接数据库,需要数据库相关参数配置:

MySQL驱动类

登录名

密码

数据库连接字符串

.properties 配置内容:

#驱动类名称 
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url 
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=false
#连接数据库的⽤⼾名 
spring.datasource.username=root
#连接数据库的密码 
spring.datasource.password=123123

.yml 配置内容:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: 123123
    driver-class-name: com.mysql.cj.jdbc.Driver

注:若使用的MySQL 是 5.X 之前的,使用 "com.mysql.jdbc.Driver",若 大于 5.X 使用的是 "com.mysql.cj.jdbc.Driver"

接下来,我们来编写持久层代码

编写代码

我们首先创建持久层接口 UserInfoMapper 

MyBatis的持久层接口规范一般叫 xxxMapper

接下来,我们进行简单的查询:

import com.example.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface UserInfoMapper {
    @Select("select id, username, password, age, gender phone from userinfo")
    public List<UserInfo> selectAllUser();
}

@Mapper注解:表示是MyBatis中的Mapper接口

 程序运行时,框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理     @Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容

写好代码之后,我们对代码进行测试

单元测试

我们可以在 test 目录下进行测试:

import com.example.mybatis.mapper.UserInfoMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MybatisApplicationTests {
	@Autowired
	private UserInfoMapper userInfoMapper;
	
	@Test
	void contextLoads() {
		System.out.println(userInfoMapper.selectAllUser());
	}
}
MybatisApplicationTests添加了注解 @SpringBootTest,因此该测试类在运行时,会自动加载Spring的运行环境

我们只需要通过 @Autowired 注入我们想要测试的类,就可以进行测试了

运行代码,进行测试:

从结果中可以看到,SQL语句中查询的列对应的数据被赋值 

此外,我们也可以使用 Idea 自动生成测试类:

我们只想要在需要测试的Mapper 接口中,鼠标右键,选择 Generate

再选择 Test 

勾选我们想要测试的方法

点击 OK后,就能够在 Test 目录下生成对应的测试类:

同样的,我们进行测试:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.jupiter.api.Assertions.*;

class UserInfoMapperTest {

    @Autowired
    private UserInfoMapper userInfoMapper;
    @Test
    void selectAllUser() {
        System.out.println(userInfoMapper.selectAllUser());
    }
}

运行代码:

然而此时程序报错:

报错信息显示:userInfoMapper 为空

这说明 userInfoMapper 未成功创建,这是因为我们未在测试类上添加注解 @SpringBootTest,因此,该测试类在运行时不会自动加载Spring运行环境,我们想要通过 @Autowired 注解注入我们要测试的类,自然会失败

我们在测试类上加上 @SpringBootTest,再次运行:

此时结果正确

在使用MyBatis的过程中,我们可以借助日志,来观察 SQL语句的执行、传递的参数 和 执行结果,因此,我们来学习MyBatis日志打印

日志

我们只需要在配置文件中进行配置:

.properties

#指定mybatis输出⽇志的位置(控制台) 
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

.yml

mybatis:
  configuration: # 配置打印 MyBatis⽇志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

此时我们再次运行程序,就可以看到SQL的执行内容  传递的参数 和 执行结果

在上述操作中,我们并未传递参数,若我们想要查询 id = 2 的用户信息,此时应该如何编写代码呢?

参数传递

查询 id = 2 的用户信息:

    @Select("select id, username, password, age, gender, phone from userinfo where id = 2")
    public UserInfo selectById();

 进行测试:

    @Test
    void selectById() {
        System.out.println(userInfoMapper.selectById());
    }

测试结果:

但此时 id 为固定的数值2,因此只能查询 id = 2 的数据,若我们想要变为动态的数值,应该如何实现呢?

我们可以在 selectById方法中添加参数 id,将方法中的参数传给SQL语句

    @Select("select id, username, password, age, gender, phone from userinfo where id = #{id}")
    public UserInfo selectById(Integer id);

使用 #{} 获取方法中的参数

测试:

    @Test
    void selectById() {
        System.out.println(userInfoMapper.selectById(3));
    }

结果:

此时,就将方法中的参数传给SQL语句进行查询

也可以通过 @Param ,设置参数的别名

    @Select("select id, username, password, age, gender, phone from userinfo where id = #{userId}")
    public UserInfo selectById(@Param("userId") Integer id);

#{}中的属性名必须与 @Param 设置的相同

在学习了MyBatis的使用后,我们来进一步学习 MyBatis 的 增 删 改 查 操作

MyBatis的增删改查

增加(Insert)

若我们使用 SQL语句进行增加:

insert into userinfo (suername, password, age, gender, phone) values ("aaa", "aaa", 22, 2, "135234522")

 我们使用 @Insert 注解来实现增加

此时,我们只需要将要添加的数据替换为动态的参数

    @Insert("insert into userinfo (username, password, age, gender, phone) values (#{username}, #{password}, #{age}, #{gender}, #{phone})")
    public Integer insert(UserInfo userInfo);

在这里,我们可以直接使用 UserInfo对象的属性名来获取参数

接下来,我们进行测试:

    @Test
    void insert() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("aaa");
        userInfo.setPassword("aaa");
        userInfo.setAge(22);
        userInfo.setGender(1);
        userInfo.setPhone("135234522");
        System.out.println(userInfoMapper.insert(userInfo));
    }

运行结果:

此时新增了一条数据

 Insert语句的默认返回值是 受影响的行数

但有时,我们在插入数据后,需要获取到新插入数据的id

若想要拿到自增 id,需要在方法上添加一个Options注解:

    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into userinfo (username, password, age, gender, phone) values (#{username}, #{password}, #{age}, #{gender}, #{phone})")
    public Integer insert(UserInfo userInfo);

useGeneratedKeys:支持自动生成记录主键的数据库(如 MySQL、SQL Server) ,若将其设置为 true,则在添加记录后可以获取到数据库自动生成的主键id(其默认值为false)

keyProperty:指定能够唯⼀识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)

再次进行测试: 

    @Test
    void insert() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("ccc");
        userInfo.setPassword("ccc");
        userInfo.setAge(24);
        userInfo.setGender(1);
        userInfo.setPhone("135234522");
        Integer count = userInfoMapper.insert(userInfo);
        System.out.println("新增数据条数:" + count + "新增数据id:" + userInfo.getId());
    }

设置 useGeneratedKeys = true 后,方法返回值仍为 受影响的行数,自增 id 设置在 keyProperty 指定的属性中

删除(Delete)

 若我们使用 SQL语句进行删除:

delete from userinfo while id = 4

我们使用 @Delete 注解来实现增加

此时,我们只需要将要添加的数据替换为动态的参数

    @Delete("delete from userinfo where id = #{id}")
    public Integer deleteById(Integer id);

同样的,我们进行测试:

    @Test
    void deleteById() {
        System.out.println(userInfoMapper.deleteById(4));

    }

测试结果:

修改(Update)

 使用 SQL语句进行修改:

update userinfo set username = "admin" where id = 2

我们使用 @Update 注解来实现修改

    @Update("update userinfo set username = #{username} where id = #{id}")
    public Integer update(UserInfo userInfo);

进行测试:

    @Test
    void update() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setUsername("admin");
        System.out.println(userInfoMapper.update(userInfo));
    }

查询(Select)

我们查询 id = 2 的数据是否更新成功:

    @Select("select * from userinfo where id = #{id}")
    public UserInfo selectById(Integer id);

测试: 

    @Test
    void selectById() {
        System.out.println(userInfoMapper.selectById(2));
    }

 结果:

更新成功

但根据结果我们可以发现:其中的 deleteFlag、createTime 和 updateTime 都为 null,未被赋值

而在SQL语句中 查询了 delete_flag、create_time 和 update_time,但这几个属性没有赋值

即 Java对象属性 与 数据库字段 相同的行被赋值

当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java类 中查找 相同名字的属性(忽略大小写)。这意味着如果发现了 ID 列和 id 属性,MyBatis会将列ID的值赋给id属性

因此,当返回的列名未在Java类中找到相同名字的属性,则不会赋值

如何解决这个问题呢?

1. 起别名

我们可以想到我们在学习MySQL时,可以给列名起别名,因此,我们可以通过起别名的方式,让别名 和 实体类属性名相同

    @Select("select id, username, password, age, gender, phone, " +
            "delete_flag as deleteFlag, create_time as createTime, update_time as updateTime " +
            "from userinfo")
    public List<UserInfo> selectAllUser();

进行测试:

    @Test
    void selectAllUser() {
        System.out.println(userInfoMapper.selectAllUser());
    }

测试结果:

当使用起别名的方式时,每当 实体属性名 与 数据库字段名不同时,都需要起别名,比较麻烦

除了起别名,我们还可以使用 结果映射

2. 结果映射

    @Select("select * from userinfo where id = #{id}")
    @Results({
            @Result(column = "delete_flag", property = "deleteFlag"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime")
    })
    public UserInfo selectById(Integer id);

测试:

    @Test
    void selectById() {
        System.out.println(userInfoMapper.selectById(2));
    }

运行结果:

其他SQL也可以复用这个映射关系,但需要为这个Results定义一个名称:

    @Select("select * from userinfo")
    @ResultMap(value = "resultMap")
    public List<UserInfo> selectAllUser();

    @Select("select * from userinfo where id = #{id}")
    @Results(id = "resultMap", value = {
            @Result(column = "delete_flag", property = "deleteFlag"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime")
    })
    public UserInfo selectById(Integer id);

使用 id 属性为 Results 定义别名,使用 @ResultMap 注解来复用其他定义的ResultMap

测试 selectAllUser() 方法:

此外,我们还可以开启驼峰命名

3. 开启驼峰命名 

一般数据库字段使用 蛇形命名法(单词之间使用下划线分割)进行命名 ,而Java属性使用驼峰命名法进行命名

因此,为了在这两种命名方式之间启用自动映射,我们可以配置驼峰自动转换、

.properties

mybatis.configuration.map-underscore-to-camel-case=true #配置驼峰⾃动转换

.yml

mybatis:
  configuration:
    map-underscore-to-camel-case: true #配置驼峰⾃动转换

此时就能够将表中字段名:xxx_xxx 转换为 类中属性名 xxxXXX 

此时,我们再次进行测试:

    @Select("select * from userinfo where id = #{id}")
    public UserInfo selectById(Integer id);

测试结果

字段正确赋值

在上述操作中,我们使用注解的方式来进行增删改查,在MyBatis中,也可以使用XML的方式来进行操作

接下来,我们就来学习使用XML

XML配置文件

要想使用 XML,首先我们需要配置数据库连接字符串和MyBatis

.properties

# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
mybatis.mapper-locations=classpath:mapper/**Mapper.xml

.yml

    mybatis:
      # 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
      mapper-locations: classpath:mapper/**Mapper.xml

配置好后,开始编写代码

此时的代码分为两部分:

1. 方法定义(Interface)

2. 方法实现(XXX.xml)

我们首先添加 mapper接口:

import java.util.List;

@Mapper
public interface UserInfoXMLMapper {
    List<UserInfo> selectAllUser();
}

接下来添加 UserInfoXMLMapper.xml

UserInfoXMLMapper.xml的路径参考配置的路径:

其中 MyBatis的固定 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">
<mapper namespace="com.example.mybatis.mapper.UserInfoXMLMapper">


</mapper>

 其中<mapper>标签中需要指定 namespace 属性,表示命名空间,值为 mapper 接口的全限定名

接下来,我们使用 <select>查询标签来进行查询操作:

<?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.example.mybatis.mapper.UserInfoXMLMapper">

    <select id="selectAllUser" resultType="com.example.mybatis.model.UserInfo">
        select username, password, age, gender, phone from userinfo
    </select>

</mapper>

<select> 中的 id 表示对接口的具体实现方法,值为 mapper接口中的定义的方法名

resultType 表示返回的数据类型,也就是我们定义的实体类

接下来,我们进行测试:

同样的,我们生成测试类:

进行单元测试:

@SpringBootTest
class UserInfoXMLMapperTest {

    @Autowired
    private UserInfoXMLMapper userInfoXMLMapper;

    @Test
    void selectAllUser() {
        System.out.println(userInfoXMLMapper.selectAllUser());
    }
}

测试结果:

接下来,我们学习通过xml的方式实现 增删改查

增加(Insert)

UserInfoXMLMapper接口:

Integer insert(UserInfo userInfo);

UserInofXMLMapper.xml实现:

    <insert id="insert">
        insert into userinfo(username, password, age, gender) values (#{username}, #{password}, #{age}, #{gender})
    </insert>

同样的,使用 #{} 获取方法中的参数,使用 @Param设置参数名称

进行测试:

    @Test
    void insert() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("hhh");
        userInfo.setPassword("hhh");
        userInfo.setAge(13);
        userInfo.setGender(2);
        System.out.println(userInfoXMLMapper.insert(userInfo));
    }

测试结果:

返回自增 id

同样的,我们设置 useGeneratedKeys 和 keyProperty属性 

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into userinfo(username, password, age, gender) values (#{username}, #{password}, #{age}, #{gender})
    </insert>

再次进行测试:

删除(Delete)

UserInfoXMLMapper接口:

    Integer delete(Integer id);

UserInofXMLMapper.xml实现:

    <delete id="delete">
        delete from userinfo where id = #{id}
    </delete>

测试:

修改(Update)

UserInfoXMLMapper接口:

Integer update(UserInfo userInfo);

UserInofXMLMapper.xml实现:

    <update id="update">
        update userinfo set age = #{age} where id = #{id}
    </update>

测试:

修改成功

查询(Select)

同样的,使用xml的方式查询,也存在有属性没有赋值的问题

    <select id="selectAllUser" resultType="com.example.mybatis.model.UserInfo">
        select * from userinfo
    </select>

其解决方法与注解相似:

1. 起别名

2. 结果映射

3. 开启驼峰命名

其中 起别名 和 开启驼峰命名 与注解相同,在这里就不再演示了,我们重点学习 xml 中如何实现结果映射

    <resultMap id="BaseMap" type="com.example.mybatis.model.UserInfo">
        <id column="id" property="id"></id>
        <result column="delete_flag" property="deleteFlag"></result>
        <result column="create_time" property="createTime"></result>
        <result column="update_time" property="updateTime"></result>
    </resultMap>

其中,

id:resultMap的别名

type:要映射的实体类

id:主键

result:普通字段和属性

column:数据库字段名

property:Java对象属性名  

评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

楠枬

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

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

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

打赏作者

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

抵扣说明:

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

余额充值