一、注解操作
MyBatis是一个优秀的持久层框架,主要用于简化程序与数据库之间的交互操作。所谓的持久层,通常是指数据访问层(DAO),它负责与数据库进行交互,包括数据的增删改查等操作。MyBatis通过简化这些操作,使得开发者可以更加便捷地实现数据库操作,提高开发效率。简而言之,MyBatis就是一个简化数据库操作和读取的工具,让开发者能够更加轻松地处理数据库相关任务。
Mybatis操作数据库的步骤一般为以下四步:
- 准备工作(创建springboot⼯程、数据库表准备、实体类)
- 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
- 编写SQL语句(注解/XML)
- 测试
1、创建项目
创建springboot⼯程,并导入mybatis的起步依赖、mysql的驱动包
项目创建完成后,会自动在pom.xml文件中,导入Mybatis依赖和MySQL驱动依赖
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.0</version> </dependency>
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>
|
2、数据准备
这里以一个简单的用户信息表为例:
1 2 3 4 5 6 7 8 9 10 11 12
| 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 CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
|
根据这个表,创建对应的实体类UserInfo
,实体类的属性名与表中的字段名应该是相对应的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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中要连接数据库,需要数据库相关参数配置:
- MySQL驱动类
- 登录名
- 密码
- 数据库连接字符串
如果是application.yml
文件,则配置如下内容:
1 2 3 4 5 6
| spring: datasource: url: jdbc:mysql://localhost:3306/数据库名?characterEncoding=utf8&useSSL=false username: root password: 11111 driver-class-name: com.mysql.cj.jdbc.Driver
|
如果是application.properties
文件,则配置如下内容:
1 2 3 4 5 6
| spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/数据库名?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=11111
|
4、编写持久层代码
在项目的mapper层中,创建持久层接口UserInfoMapper
:
1 2 3 4 5 6 7 8 9 10 11
| package com.example.mybatisdemo.mapper; import com.example.mybatisdemo.model.UserInfo; import org.apache.ibatis.annotations.*; import java.util.List; @Mapper public interface UserInfoMap { @Select("select * from userinfo") List<UserInfo> selectAll(); @Select("select * from userinfo where id = #{id}") UserInfo selectById(Integer id); }
|
@Mapper
:这个注解是用来标记接口是一个MyBatis的映射器。当应用程序启动时,MyBatis会处理这个接口,并在运行时为其创建一个实现。这个实现将负责实际的数据库交互。public interface UserInfoMap
:这行代码声明了一个名为UserInfoMap
的公共接口。在MyBatis中,映射器通常定义为接口,接口中的每个方法都对应一个数据库操作。
@Select("select * from userinfo where id = #{id}")
:这个注解用来指定一个SQL查询语句。#{id}
占位符是一个参数,将会与方法selectById
中的参数id
绑定。MyBatis将会用实际传递给selectById
方法的值替换这个占位符。
5、进行单元测试
在需要写单元测试的类中,Generate,选择 Test
1 2 3 4 5 6 7 8 9
| @SpringBootTest class UserInfoMapTest { @Autowired private UserInfoMap userInfoMap; @Test void selectAll() { List<UserInfo> list = userInfoMap.selectAll(); } }
|
@SpringBootTest
:这个注解是用来标记测试类,它会告诉Spring Boot启动整个应用程序的上下文,包括所有的 beans 和配置。这样,测试就可以在一个接近真实运行环境的环境中执行。
@Autowired
:这个注解用于自动注入UserInfoMap类型的bean。Spring会自动查找UserInfoMap接口的实例,并将其注入到userInfoMap字段中。这样,在测试类中就可以直接使用userInfoMap来调用映射器接口中定义的方法。
@Test
:这个注解来自JUnit测试框架,用于标记一个方法作为测试方法。当运行测试时,所有标记有@Test的方法都会被执行。在这个例子中,selectAll方法就是一个测试方法。
二、基础操作
1、参数传递
1 2 3 4 5
| @Mapper public interface UserInfoMap { @Select("select * from userinfo where id = #{id}") UserInfo selectById(Integer id); }
|
使用 #{}
的方式获取方法中的参数,在selectById
方法中添加⼀个参数(id)
,将方法中的参数,传给SQL语句。
2、新增
Insert语句默认返回的是受影响的行数,新增语句写法如下:
1 2 3 4 5 6
| @Mapper public interface UserInfoMap { @Insert("insert into userinfo (username,password,age,gender,phone)"+ "values(#{username},#{password},#{age},#{gender},#{phone})") Integer insert(UserInfo userInfo); }
|
对应的测试代码:
1 2 3 4 5 6 7 8 9 10 11 12
| @Autowired private UserInfoMap userInfoMap; @Test void insert() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("zhangsan"); userInfo.setPassword("123456"); userInfo.setAge(20); userInfo.setPhone("12345678901"); userInfo.setGender(1); Integer result = userInfoMap.insert(userInfo); }
|
如何拿到自增 ID?在Mapper接口的方法上添加⼀个Options的注解。
1 2 3 4
| @Options(useGeneratedKeys = true,keyProperty = "id") @Insert("insert into userinfo (username,password,age,gender,phone)"+ "values(#{username},#{password},#{age},#{gender},#{phone})") Integer insert(UserInfo userInfo);
|
useGeneratedKeys
和 keyProperty
是 MyBatis 中用于处理数据库自动生成的主键的属性。
useGeneratedKeys
:这个属性用于通知 MyBatis 在执行插入操作后,是否需要使用 JDBC 的 getGeneratedKeys
方法来获取数据库自动生成的主键值。例如,在 MySQL 和 SQL Server 中,当你有一个自动递增的字段作为主键时,你可以设置这个属性为 true
,这样 MyBatis 就会在插入记录后自动获取这个生成的主键值。默认情况下,这个属性的值是 false
,也就是说,MyBatis 不会自动获取生成的主键值。
keyProperty
:这个属性用于指定哪个实体类属性的 field 将会被自动生成的主键值填充。当你设置了 useGeneratedKeys
为 true
时,你需要通过 keyProperty
告诉 MyBatis 实体类中哪个属性对应于数据库中的主键列。这样,MyBatis 就会在插入记录后,将自动生成的主键值赋给实体类中指定的属性。如果你没有设置 keyProperty
,MyBatis 就不会将获取到的主键值赋给任何属性。
3、删除
Mapper接口:
1 2 3 4 5
| @Mapper public interface UserInfoMap { @Delete("delete from userinfo where id = #{id}") Integer delete(Integer id); }
|
对应的测试代码:
1 2 3 4 5 6
| @Autowired private UserInfoMap userInfoMap; @Test void delete() { Integer result = userInfoMap.delete(10); }
|
4、更新
Mapper接口:
1 2 3 4 5
| @Mapper public interface UserInfoMap { @Update("update userinfo set age = #{age} where id = #{id}") Integer update(UserInfo userInfo); }
|
对应的测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Autowired private UserInfoMap userInfoMap; @Test void update() { UserInfo userInfo = new UserInfo(); userInfo.setId(9); userInfo.setAge(30); Integer result = userInfoMap.update(userInfo); if(result>0){ log.info("update:{}",result); }else{ log.info("update:更新失败"); } }
|
5、查找
在执行数据库查询操作时,MyBatis 默认情况下会根据 Java 对象的属性名与数据库字段名的对应关系进行自动映射。具体来说,MyBatis 会将查询结果集中每个字段的值赋给与之名称相同的 Java 对象属性。如果 Java 对象的属性名与数据库字段名不完全匹配,那么这些属性在查询结果中可能会显示为 null,因为没有找到与之对应的字段值。
1 2 3 4
| @Select("SELECT id, username, `password`, age, gender, phone, delete_flag, " + "create_time, update_time " + "FROM userinfo") List<UserInfo> selectAllUser();
|
尽管 SQL 语句中包含了 delete_flag、create_time 和 update_time 字段的查询,但由于这些字段名与 Java 对象的属性名不完全一致,导致查询结果中没有为这些属性赋值。
因为数据库字段名和 Java 对象属性名之间存在差异,例如大小写不同、下划线与驼峰命名法的差异等导致字段名与属性名不匹配。
Ⅰ、结果映射
1 2 3 4 5 6 7 8 9
| @Select("SELECT id, username, `password`, age, gender, phone, delete_flag, " + "create_time, update_time " + "FROM userinfo") @Results({ @Result(column = "delete_flag", property = "deleteFlag"), @Result(column = "create_time", property = "createTime"), @Result(column = "update_time", property = "updateTime") }) List<UserInfo> selectAllUser();
|
@Results({ ... })
:这个注解用于定义结果集映射,它包含了一个或多个 @Result 注解,每个 @Result 注解指定了一个数据库字段到 Java 对象属性的映射。@Result(column = "delete_flag", property = "deleteFlag")
:这个注解用于将查询结果中的 delete_flag 字段映射到 UserInfo 类中的 deleteFlag 属性。
如果希望其他的 SQL 查询也能复用这个映射关系,可以使用 MyBatis 的 @ResultMap 注解来定义一个结果映射,然后在其他的查询方法中引用这个结果映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Select("SELECT id, username, `password`, age, gender, phone, delete_flag, " + "create_time, update_time " + "FROM userinfo") @Results(id = "resultMap", value = { @Result(column = "delete_flag", property = "deleteFlag"), @Result(column = "create_time", property = "createTime"), @Result(column = "update_time", property = "updateTime") }) List<UserInfo> selectAllUser();
@Select("SELECT id, username, `password`, age, gender, phone, delete_flag, " + "create_time, update_time " + "FROM userinfo " + "WHERE id = #{userid}") @ResultMap("resultMap") UserInfo selectById(Integer id);
|
Ⅱ、开启驼峰命名
通常数据库列采用蛇形命名法(即使用下划线分隔各个单词),而Java对象的属性则遵循驼峰命名法(即首字母大写,其余单词首字母小写)。为了在这两种命名方式之间实现自动映射,需要在MyBatis配置中设置mapUnderscoreToCamelCase
属性为true
。开启之后,表中字段名:abc_xyz 会自动映射成类中属性名:abcXyz
在application.yml
文件中加入下方配置:
1 2 3
| mybatis: configuration: map-underscore-to-camel-case: true
|
三、XML方式
使⽤Mybatis的注解⽅式,主要是来完成⼀些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使⽤XML来配置映射语句,也就是将SQL语句写在XML配置⽂件中。MyBatis XML的方式需要以下两步:
- 配置数据库连接字符串
- 指明XML的路径
- 写持久层代码
1、配置数据库
和前面配置数据库的方法一样。
如果是application.yml
文件,则配置如下内容:
1 2 3 4 5 6
| spring: datasource: url: jdbc:mysql://localhost:3306/mybatis_test?useSSL=false&allowPublicKeyRetrieval=true username: root password: 11111 driver-class-name: com.mysql.cj.jdbc.Driver
|
如果是application.properties
文件,则配置如下内容:
1 2 3 4 5 6
| spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/数据库名?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=11111
|
2、指明XML路径
如果是application.yml
⽂件,配置内容如下:
1 2
| mybatis: mapper-locations: classpath:mapper/**Mapper.xml
|
如果是application.properties
⽂件,配置内容如下:
1 2
| mybatis.mapper-locations=classpath:mapper/**Mapper.xml
|
在mapper文件夹内,新建XXXMapper.xml
文件
在该文件内写入MyBatis 的固定 xml 格式:
1 2 3 4 5 6 7 8
| <?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.demo.mapper.UserInfoXMLMapper">
</mapper>
|
这里通常与 Mapper 接口的完全限定名相匹配。这样 MyBatis 就能够将接口中的方法与映射器文件中的 SQL 语句关联起来。
3、写持久层代码
持久层代码分两部分
- ⽅法定义 Interface
- ⽅法实现: XXXMapper.xml
新建一个接口:UserInfoXMlMapper
1 2 3 4
| @Mapper public interface UserInfoXMLMapper { List<UserInfo> selectAll(); }
|
新建UserInfoXMLMapper
用于实现该接口的方法。
1 2 3 4 5 6 7 8 9 10
| <?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.mybatisdemo.mapper.UserInfoXMLMapper">
<select id="selectAll" resultType="com.example.mybatisdemo.model.UserInfo"> select * from userinfo </select>
</mapper>
|
-
<mapper>
标签是 MyBatis 中映射器文件的基础,它定义了一个命名空间,用于隔离不同的 SQL 语句,防止 SQL ID 冲突。
-
namespace
属性是必须的,它通常设置为 Mapper 接口的全限定名,这样 MyBatis 可以将接口中的方法与映射器文件中的 SQL 语句关联起来。例如:<mapper namespace="com.example.mybatisdemo.mapper.UserInfoXMLMapper">
-
resultType
属性用于指定查询结果的类型,它应该是返回对象的完全限定类名或别名。MyBatis 会将查询结果映射到这个类型的实例中。
四、基础操作
1、新增
UserInfoXMLMapper
接口:
1 2 3 4
| @Mapper public interface UserInfoXMLMapper { Integer insertUser(UserInfo userInfo); }
|
UserInfoXMLMapper.xml
进行实现:
1 2 3 4
| <insert id="insertUser"> insert into userinfo (username, password, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone}) </insert>
|
对应的单元测试:
1 2 3 4 5 6 7 8 9 10 11 12
| @Autowired private UserInfoXMLMapper userInfoXMLMapper; @Test void insertUser() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("lisi"); userInfo.setPassword("123456"); userInfo.setAge(20); userInfo.setPhone("12345678901"); userInfo.setGender(1); Integer result = userInfoXMLMapper.insertUser(userInfo); }
|
获取插入数据的自增ID:接口定义不变,Mapper.xml
实现设置useGeneratedKeys 和keyProperty属性:
1 2 3 4
| <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> insert into userinfo (username, password, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone}) </insert>
|
2、删除
UserInfoXMLMapper
接口:
1 2 3 4
| @Mapper public interface UserInfoXMLMapper { Integer deleteUser(Integer id); }
|
UserInfoXMLMapper.xml
进行实现:
1 2 3
| <delete id="deleteUser"> delete from userinfo where id = #{id} </delete>
|
3、更新
UserInfoXMLMapper
接口:
1 2 3 4
| @Mapper public interface UserInfoXMLMapper { Integer updateUser(UserInfo userInfo); }
|
UserInfoXMLMapper.xml
进行实现:
1 2 3
| <update id="updateUser"> update userinfo set username=#{username} where id=#{id} </update>
|
4、查找
同样的, 使⽤XML 的⽅式进⾏查询, 也存在数据封装的问题。解决办法和注解类似:使用结果映射或者开启驼峰命名。
Ⅰ、开启驼峰命名
和前面一样。在application.yml
文件中加入下方配置:
1 2 3
| mybatis: configuration: map-underscore-to-camel-case: true
|
Ⅱ、结果映射
1 2 3 4 5 6 7 8 9 10 11 12
| <resultMap id="BaseMap" type="com.example.demo.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>
<select id="selectAllUser" resultMap="BaseMap"> select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo </select>
|
<resultMap>
标签:
<resultMap>
用于定义如何将查询结果映射到 Java 对象上。
id
属性是结果映射的唯一标识符。
type
属性指定了结果映射的 Java 类型,通常是实体类的全限定名。
<id>
标签:
<id>
标签用于指定主键列的映射。
column
属性表示数据库表中的列名。
property
属性表示 Java 对象中的属性名。
<result>
标签:
<result>
标签用于指定非主键列的映射。
column
属性表示数据库表中的列名。
property
属性表示 Java 对象中的属性名。