MyBatis 入门

目录

[一. MyBatis 概述](#一. MyBatis 概述)

[二. MyBatis 入门](#二. MyBatis 入门)

[1. 创建工程, 导入依赖](#1. 创建工程, 导入依赖)

[2. 数据准备](#2. 数据准备)

[3. 配置数据库相关信息](#3. 配置数据库相关信息)

[4. 编写持久层代码](#4. 编写持久层代码)

[5. 编写测试代码](#5. 编写测试代码)

[三. MyBatis基础操作.](#三. MyBatis基础操作.)

[1. 打印日志](#1. 打印日志)

[2. 参数传递](#2. 参数传递)

[3. 增 (Insert)](#3. 增 (Insert))

[4. 删 (Delete)](#4. 删 (Delete))

[5. 改 (Update)](#5. 改 (Update))

[6. 查 (Select)](#6. 查 (Select))

[(1) 起别名](#(1) 起别名)

[(2) 结果映射](#(2) 结果映射)

[(3) 开启驼峰命名与蛇形命名转换](#(3) 开启驼峰命名与蛇形命名转换)

[四. MyBatis XML配置文件](#四. MyBatis XML配置文件)

[(1) 配置连接字符串和MyBatis](#(1) 配置连接字符串和MyBatis)

[(2) 编写持久层代码](#(2) 编写持久层代码)

[① 添加Mapper接口](#① 添加Mapper接口)

[② 添加UserInfoXMLMapper.xml](#② 添加UserInfoXMLMapper.xml)

[③ 单元测试](#③ 单元测试)

[(3) 增删改查操作](#(3) 增删改查操作)

[① 增 (Insert)](#① 增 (Insert))

[② 删 (Delete)](#② 删 (Delete))

[③ 改 (Update)](#③ 改 (Update))

[④ 查 (Select)](#④ 查 (Select))


一. MyBatis 概述

前面我们学习过使用JDBC编程对数据库进行操作. 不过JDBC对于数据库的操作过于麻烦, 所以我们在Spring中引入MyBatis框架, 来帮助我们对数据库进行操作.

MyBatis 是一款优秀的持久层框架, 用于简化JDBC的开发.

持久层(Mapper/Dao), 就是用来操作数据库的.

简单来说, MyBatis 是一个能够更简单地完成程序和数据库交互的框架. 接下来,我们就通过一个入门程序,让大家感受一下通过Mybatis如何操作数据

二. MyBatis 入门

1. 创建工程, 导入依赖

MyBatis 是一个持久层框架, 具体的数据存储和数据操作还是在MySQL中进行的. 所以我们还需要添加MySQL驱动.

项目工程创建完成后, 我们就会发现: 在pom.xml文件中, 自动导入了Mybatis依赖和MySQL驱动依赖.

2. 数据准备

创建用户表:

sql 复制代码
-- 创建数据库 
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 ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

创建对应的实体类 UserInfo: (其中实体类的属性名 和表中的字段名 一一对应)

java 复制代码
package com.jrj.mybatis;

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;
}

3. 配置数据库相关信息

MyBatis中 要连接数据库 需要配置数据库的相关参数, 包括数据库URL, 用户名, 密码, MySQL驱动类. 这些配置信息均在配置文件中完成.

java 复制代码
# 数据库连接配置 
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
	username: root
	password: root
	driver-class-name: com.mysql.cj.jdbc.Driver

如上代码, mybatis_test 就是数据库的名称.

注意: 如果password是纯数字的话, 需要在数字的的外围用单引号括起来.

4. 编写持久层代码

java 复制代码
package com.jrj.mybatis.mapper;

import com.jrj.mybatis.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserInfoMapper {
    @Select("select * from userinfo")
    public List<UserInfo> selectAllUser();
}

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

@Select 注解: 表示Select查询 (代表注解中方法的具体实现)

注意: 像上述代码这样返回多条数据的接口, 一定要用List来接收数据; MyBatis的持久层接口一般都叫 "xxxMapper".

5. 编写测试代码

自动生成:

在想要测试的接口中点击右键, 点击生成, 选择想要测试的方法, 即可生成测试代码.

三. MyBatis基础操作.

1. 打印日志

在Mybatis当中, 我们可以借助日志, 查看到sql语句的执行, 执行传递的参数以及执行结果. 在配置文件中进行配置即可.

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

配置好之后, 重新运行程序, 即可看到SQL的执行内容.

2. 参数传递

如果我们现在需要查找 id=4 的用户, 对应的 sql语句 就是 select * from userinfo where id=4.

java 复制代码
@Select("select * form userinfo where id = 4")
public UserInfo selectUser();

但是这样的话, 就只能查询到 id为4 用户的数据. 所以我们不建议把sql语句写死 . 我们需要把 id 变为动态的数值.

解决办法就是在selectUser方法中添加一个参数(id), 将方法中的参数传递给sql语句 .使用**#{}**的方式在sql语句中获取方法中传递的参数.

java 复制代码
@Select("select * from userinfo where id = #{id}")
public UserInfo selectUser(Integer id);
java 复制代码
void selectUser() {
    System.out.println(userInfoMapper.selectUser(4));
}

注意:

  • 如果参数只有一个, #{ } 内的属性名称可以随便写.
  • 如果参数有多个 , 属性名和参数名必须保持一致; 或按照顺序对应(param1, param2...).

① 按名称对应

java 复制代码
@Select("select * from userinfo where username = #{name} and id = #{id}")
public UserInfo selectUser2(String name,Integer id);
java 复制代码
@Test
void selectUser2() {
    System.out.println(userInfoMapper.selectUser2("admin",1));
}

② 按顺序对应

java 复制代码
    @Select("select * from userinfo where username = #{param1} and id = #{param2}")
    public UserInfo selectUser3(String name,Integer id);
java 复制代码
    @Test
    void selectUser3() {
        System.out.println(userInfoMapper.selectUser3("admin",1));
    }

: 也可以通过@Param注解, 设置参数的别名. 如果使用@Param设置别名, **注解中的别名必须和sql中的属性名保持一致.**举例如下:

java 复制代码
@Select("select * from userinfo where id = #{ID}")
public UserInfo selectUser4(@Param("ID") Integer id);
java 复制代码
@Test
void selectUser4() {
    System.out.println(userInfoMapper.selectUser4(1));
}

3. 增 (Insert)

sql语句:

insert into userinfo (username, `password`, age, gender, phone) values ("zhaoliu","zhaoliu",19,1,"15585133024")

把SQL中的常量换为动态的参数.

Mapper接口:

java 复制代码
@Insert("insert into userinfo (id,username,password,age,gender,phone) values (" +
        "#{id},#{username},#{password},#{age},#{gender},#{phone})")
public Integer insertUser1(UserInfo userInfo);

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

java 复制代码
@Test
void insertUser1() {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(5);
    userInfo.setUsername("zhaoliu");
    userInfo.setPassword("zhaoliu");
    userInfo.setAge(19);
    userInfo.setGender(1);
    userInfo.setPhone("15585133024");
    userInfoMapper.insertUser1(userInfo);
}

如果设置了 @Param 属性, #{...} 需要使用 参数. 属性 来获取.

返回主键

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

但是有些情况下, 数据插入之后, 还需要有后续的关联操作, 需要获取到新插入的数据的id. 如果想要拿到自增id, 需要在Mapper接口的方法上添加一个Options的注解.

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

4. 删 (Delete)

sql语句:

delete from userinfo where id=6

把sql语句中的常量替换为动态参数.

Mapper接口:

java 复制代码
@Delete("delete from userinfo where id = #{id}")
public Integer deleteUser1(Integer id);
java 复制代码
@Test
void deleteUser1() {
    System.out.println(userInfoMapper.deleteUser1(6));
}

5. 改 (Update)

sql语句:

update userinfo set username="zhaoliu" where id=5

把SQL中的常量替换为动态的参数

Mapper接口:

java 复制代码
@Update("update userinfo set username = #{username} where id = #{id}")
public Integer updateUser(UserInfo userInfo);
java 复制代码
@Test
void updateUser() {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(5);
    userInfo.setUsername("tianqi");
    System.out.println(userInfoMapper.updateUser(userInfo));
    }

6. 查 (Select)

只有Java对象属性和数据库字段⼀模⼀样时,才会进行赋值.

当自动映射查询结果的时候, MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写). 这意味着如果发现了ID列和id属性, MyBatis会将 ID列的值 赋给 id属性. 但是我们的创建时间, 更新时间, 删除逻辑数字: 在数据库中是蛇形结构的名字, 而在Java类中是小驼峰的格式.

问题如上图, 那如何解决这个问题呢?

(1) 起别名

在sql语句中, 给列名起别名, 保持 别名实体类属性名一致.

java 复制代码
@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> selectAllUser2();

: 当sql语句太长的时候,我们可以使用 + 进行拼接.

(2) 结果映射

java 复制代码
@Results({
        @Result(column = "delete_flag",property = "deleteFlag"),
        @Result(column = "create_time",property = "createTime"),
        @Result(column = "update_time",property = "updateTime")
})
@Select("select * from userinfo")
public List<UserInfo> selectAllUser3();
  • @Results 注解 中可以用大括号括起多个@Result映射.
  • @Result前面的参数 是表的字段名 , 后面是Java类的属性 . (字段与属性一一映射)

如果其他sql也想复用 这一组映射, 可以给这一组 @Results映射 自定义一个名称 . 之后在想要复用这个sql映射的地方使用 @ResultMap(vlaue="映射名称") 来实现映射的复用.

java 复制代码
@Results(id = "resultMap1",value = {
         @Result(column = "delete_flag",property = "deleteFlag"),
         @Result(column = "create_time",property = "createTime"),
         @Result(column = "update_time",property = "updateTime")
 })
 @Select("select * from userinfo")
 public List<UserInfo> selectAllUser3();

 @ResultMap(value = "resultMap1")
 @Select("select * from userinfo where id = #{id}")
 public UserInfo selectUser5(Integer id);

@ResultMap注解 中的 value 的值和上面映射的 id 名字必须相同.

我们也可以在xml文件中使用 <resultMap> 和 <result> 标签来完成.

XML 复制代码
<resultMap id="BaseResultMap" type="com.jrj.forums.model.Article">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="board_id" jdbcType="BIGINT" property="boardId" />
    <result column="user_id" jdbcType="BIGINT" property="userId" />
    <result column="title" jdbcType="VARCHAR" property="title" />
    <result column="visit_count" jdbcType="INTEGER" property="visitCount" />
    <result column="reply_count" jdbcType="INTEGER" property="replyCount" />
    <result column="like_count" jdbcType="INTEGER" property="likeCount" />
    <result column="state" jdbcType="TINYINT" property="state" />
    <result column="delete_state" jdbcType="TINYINT" property="deleteState" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
 </resultMap>

指定好结果映射之后, 就可以在对应的sql标签之后使用 resultMap 来指定结果映射.

XML 复制代码
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
...
</select>

我们也可以使用 extends 来继承结果映射, 在继承过来的结果映射中添加还未存在的字段.

XML 复制代码
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.jrj.forums.model.Article">
  <result column="content" jdbcType="LONGVARCHAR" property="content" />
</resultMap>

(3) 开启驼峰命名与蛇形命名转换

通常++数据库列使用蛇形命名法进行命名(下划线分割各个单词), 而Java属性⼀般遵循驼峰命名法约定.++

为了在这两种命名方式之间启用自动映射 , 需要 **将mapUnderscoreToCamelCase设置为true .**我们需要在配置文件中配置.

XML 复制代码
mybatis: 
  configuration:
    map-underscore-to-camel-case: true #配置驼峰⾃动转换 

转换规则: abc_xyz --> abcXyz (蛇形转换为小驼峰).

而Java代码不用做任何处理:

java 复制代码
@Select("select * from userinfo")
public List<UserInfo> selectAllUser();

四. MyBatis XML配置文件

MyBatis 的开发方式有两种:

1. 注解

2. XML

使用Mybatis的注解方式, 主要是来完成一些简单的增删改查功能. 如果需要实现复杂的SQL功能, 建议使用XML来配置映射语句 (也就是将SQL语句写在XML配置文件中).

前面我们学习了注解方式, 下面我们来看XML方式:

这种开发方式大致分为两步:

(1) 配置 数据库连接字符串MyBatis.

(2) 编写持久层代码

(1) 配置连接字符串和MyBatis

XML 复制代码
# 数据库连接配置 
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?
    characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件 
mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml

mapper-locations 中的value值, classpath表示的是项目中的resource目录. mapper表示的是一个自定义目录. 一般我们使用xml操作数据库的代码都会单独放在一个mapper目录中, 之后**Mapper.xml表示的是以这个结尾的xml文件就是操作数据库的xml文件.

(2) 编写持久层代码

持久层代码分为两部分:

① 方法定义: Interface
② 方法实现: xxx.xml

① 添加Mapper接口

java 复制代码
package com.jrj.mybatis.mapper;

import com.jrj.mybatis.UserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

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

② 添加UserInfoXMLMapper.xml

java 复制代码
<?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.jrj.mybatis.mapper.UserInfoXMLMapper">
    
</mapper>

mapper标签 中加的是带有@Mapper注解接口的路径, 即想要通过MyBatis操作数据库的Mapper接口.

查询所有用户的具体实现:

java 复制代码
<?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.jrj.mybatis.mapper.UserInfoXMLMapper">
    <select id="selectAllUser1" resultType="com.jrj.mybatis.UserInfo">
        select * from userinfo
    </select>
</mapper>
  • 其中. select标签中 id="selectAllUser1" 代表的是Mapper中的方法名; resultType="com.jrj.mybatis.UserInfo" 代表的是sql查询之后返回的类型 (也就是我们开头定义的实体类)
  • 只有select类型的语句会有返回值的类型. 注意: 是sql查询之后返回的类型, 不是接口返回值的类型. (sql查询之后返回的是UserInfo类型, 而接口返回的是List类型.)
  • 标签中间写的是sql语句.

我们还可以安装一个插件, 叫做MyBatisX, 这个插件可以自动帮助我们生成xml标签. 我们只需要写sql语句就行了.

③ 单元测试

进行单元测试时, 我们可以在Mapper上点击右键 --> 生成 --> 测试 --> 勾选想要测试的方法, 就会在test目录下自动生成测试类和测试方法 , 其中测试方法的返回值必须为void.

java 复制代码
@Test
void selectAllUser1() {
     List<UserInfo> list = userInfoXMLMapper.selectAllUser1();
     for (UserInfo userInfo: list){
         System.out.println(userInfo);
     }
 }

(3) 增删改查操作

① 增 (Insert)

UserInfoMapper接口:

java 复制代码
public Integer insertUser(UserInfo userInfo);

XML:

java 复制代码
<insert id="insertUser">
    insert into userinfo (id,username,password,age,gender,phone) values (#{id},#{username},#{password},#{age},#{gender},#{phone})
</insert>

测试代码:

java 复制代码
@Test
void insertUser() {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(8);
    userInfo.setUsername("zhubajie");
    userInfo.setAge(22);
    userInfo.setPassword("6666666");
    userInfo.setGender(0);
    userInfo.setPhone("487362849326");
    userInfoXMLMapper.insertUser(userInfo);
}

与注解实现类似,要是在形参的前面加上 @Param注解 的话,在sql语句中的 #{ } 就必须使用对象名.属性名 来访问.

Mapper接口:

java 复制代码
public Integer insertUser(@Param("userinfo") UserInfo userInfo);

XML:

java 复制代码
<insert id="insertUser">
   insert into userinfo (id,username,password,age,gender,phone) values (#{userinfo.id},#{userinfo.username},#{userinfo.password},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})
</insert>

② 删 (Delete)

③ 改 (Update)

④ 查 (Select)

删, 改, 查和增是同样的道理, 我们不再赘述.

相关推荐
葫芦和十三5 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp6 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑6 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯7 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan9 小时前
多Agent之间的区别
后端
青石路11 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充11 小时前
1.面向对象设计思想
后端
IT_陈寒11 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro12 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗12 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端