MyBatis(1) 基本使用
2023-10-30 15:00:43 # Backend # MyBatis

1. 快速入门

img

导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>

定义 mapper 接口和xml

1
2
3
public interface EmployeeMapper {
Employee selectEmployee(Integer empId);
}
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.mapper.EmployeeMapper">
<select id="selectEmployee" resultType="com.atguigu.pojo.Employee">
<!-- #{empId}代表动态传入的参数,并且进行赋值!后面详细讲解 -->
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where emp_id = #{empId}
</select>
</mapper>
  • 方法名和 SQL 的 id 一致
  • 方法返回值和 resultType 一致
  • 方法的参数和 SQL 的参数一致
  • 接口的全类名和映射配置文件的名称空间一致

准备 MyBatis 配置文件

习惯上命名为 mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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>

<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>

<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->

<!-- 可以直接指定其所在的包 -->
<!-- <package name="mappers"/> -->
<mapper resource="mappers/EmployeeMapper.xml"/>
</mappers>

</configuration>

运行和测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void testSelectEmployee() throws IOException {
// 1.创建SqlSessionFactory对象
// ①声明Mybatis全局配置文件的路径
String mybatisConfigFilePath = "mybatis-config.xml";
// ②以输入流的形式加载Mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream(mybatisConfigFilePath);
// ③基于读取Mybatis配置文件的输入流创建SqlSessionFactory对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 2.使用SqlSessionFactory对象开启一个会话
SqlSession session = sessionFactory.openSession();

// 3.根据EmployeeMapper接口的Class对象获取Mapper接口类型的对象(动态代理技术)
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);

// 4. 调用代理类方法既可以触发对应的SQL语句
Employee employee = employeeMapper.selectEmployee(1);
System.out.println("employee = " + employee);

// 5.关闭SqlSession
session.commit(); //提交事务 [DQL不需要,其他需要]
session.close(); //关闭会话
}

2. SQL语句传参

2.1 日志输出配置

MyBatis 配置文件设计标签和顶层结构如下:

可以在配置文件使用settings标签设置,输出运过程SQL日志!

设置名 描述 有效值 默认值
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 image-20231023152901677 未设置
1
2
3
4
<settings>
<!-- SLF4J 选择slf4j输出! -->
<setting name="logImpl" value="LOG4J2"/>
</settings>

2.2 #{}, ${}

#{}

image-20231023154722087

  • MyBatis 会将 SQL 语句中的 #{} 转换为问号占位符。

${}

image-20231023154524366

${}形式传参,底层MyBatis做的是字符串拼接操作, 通常不会使用,有 SQL 注入的风险

一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用${}

1
2
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

3. 数据输入

3.1 单个简单类型参数

单个简单类型参数,在#{}中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名

3.2 多个简单类型参数

当有多个简单类型参数时,MyBatis无法识别抽象方法参数的自定义名称,需要使用 @Param 标记

或者通过 arg0, arg1; param1, param2 表示

3.3 Map类型参数

若mapper接口方法的参数有多个时,可以手动将这些参数放在一个map中存储

  • 只需要通过#{}${}的方式访问值即可,但是需要注意后者的单引号问题
  • 如果有很多零散的参数需要传递,但是没有对应的实体类类型可以使用,封装到Map中会很简洁

3.4 实体类类型参数

以属性的方式访问值即可

MyBatis会根据 #{} 中传入的数据,加工成 getXxx() 方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到 #{} 解析后的问号占位符的位置

4. 数据输出

数据输出总体上有两种形式:

  • 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
  • 查询操作的查询结果,需要我们指定输出数据类型

以及插入场景下,实现主键数据回显

4.1 返回单个简单类型

1
2
3
<select id="selectEmpCount" resultType="int">
select count(*) from t_emp
</select>

select标签,通过resultType指定查询返回值类型!

resultType = “全限定符|别名|如果是返回集合类型,写范型类型即可”

Mybatis 内部给常用的数据类型设定了很多别名。

以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等

别名问题

4.2 返回实体类对象

通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来

1
2
3
4
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
<!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
select emp_id empId, emp_name empName, emp_salary empSalary from t_emp where emp_id=#{maomi}
</select>

或者通过增加全局配置自动识别对应关系

1
2
3
4
5
6
7
8
9
10
11
<!-- 在全局范围内对Mybatis进行配置 -->
<settings>

<!-- 具体配置 -->
<!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
<!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
<!-- 规则要求数据库表字段命名方式:单词_单词 -->
<!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>

</settings>

4.3 返回Map类型

若查询出的数据只有一条

1
Map<String, Object> getUserByIdToMap(@Param("id") Integer id);
1
2
3
<select id="getUserByIdToMap" resultType="map">
select * from t_user where id = #{id}
</select>

返回的结果中key为属性名

image-20231024190557526

若查询出多条数据

1
2
3
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
  1. 可以通过map类型的list集合接收
1
List<Map<String, Object>> getAllUserToMap();

image-20231024190838306

  1. 可以在mapper接口的方法上添加 @MapKey 注解,此时就可以将每条数据转换的map集合作为值,以某个字段的值作为键,放在同一个map集合中
1
2
@MapKey("id")
Map<String, Object> getAllUserToMap();

image-20231024191038218

4.4 返回List类型

不需要任何特殊处理,在resultType属性中还是设置实体类类型即可

4.5 返回主键值

自增长类型主键

1
2
3
4
5
6
<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId">
insert into t_emp(emp_name, emp_salary) values(#{empName}, #{empSalary})
</insert>

MyBatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回

非自增长类型主键

对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素。selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用

使用 selectKey 帮助插入UUID作为字符串类型主键示例:

1
2
3
4
5
6
7
8
9
10
11
12
<insert id="insertUser" parameterType="User">
<selectKey keyProperty="id" resultType="java.lang.String"
order="BEFORE">
SELECT UUID() as id
</selectKey>
INSERT INTO user (id, username, password)
VALUES (
#{id},
#{username},
#{password}
)
</insert>

通过 keyProperty 属性来指定查询到的 UUID 赋值给对象中的 id 属性,而 resultType 属性指定了 UUID 的类型为 java.lang.String

需要注意的是,selectKey 在插入语句的前面,这是因为 MySQL 在 insert 语句中只支持一个 select 子句,而 selectKey 中查询 UUID 的语句就是一个 select 子句,因此我们需要将其放在前面。

最后,在将 User 对象插入到 user 表中时,我们直接使用对象中的 id 属性来插入主键值。

4.6 实体类属性和数据库字段对应关系

别名对应

  • SQL语句中手动转换

全局配置自动识别驼峰式命名规则

  • SQL语句中可以不使用别名

resultMap

使用 resultMap 标签定义对应关系,再在后面的SQL语句中引用这个对应关系

此处简单介绍一下,涉及到一对多多对一映射的在后面部分展开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">

<!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
<!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
<id column="emp_id" property="empId"/>

<!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
<result column="emp_name" property="empName"/>

<result column="emp_salary" property="empSalary"/>

</resultMap>

<!-- Employee selectEmployeeByRM(Integer empId); -->
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>

5. Mapper标签总结

select 标签

1
2
3
<select id="selectPerson" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>

select 元素允许配置很多属性来配置每条语句的行为细节:

属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
resultType 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。

insert, update 和 delete 标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<insert
id="insertAuthor"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">

<update
id="updateAuthor"
statementType="PREPARED"
timeout="20">

<delete
id="deleteAuthor"
statementType="PREPARED"
timeout="20">
属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。