MyBatis 初级
一、MyBatis是什么?
MyBatis是一款优秀的持久层框架,用于简化JDBC的开发。
持久层:指的就是持久化操作的层,通常指数据访问层(dao),是用来操作数据库。
简单来说MyBatis是更简单完成程序和数据库交互的框架。
二、Mybatis入门
2.1 准备工作
2.1.1 创建工程
创建springboot工程,并导入mybatis的起步依赖、mysql的驱动包
这种方式pom.xml文件中会自动导入 MyBatis 和 MySQL
也可以通过手动导入MyBatis和MySQL
<!-- mysql -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
2.1.2 数据库的准备
创建用户表,并创建对应的实体类User
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 使⽤数据数据
USE mybatis_test;
-- 创建表[⽤⼾表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info`
(
`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.user_info(username, `password`, age, gender, phone)
VALUES ('admin', 'admin', 18, 1, '18612340001');
INSERT INTO mybatis_test.user_info(username, `password`, age, gender, phone)
VALUES ('zhangsan', 'zhangsan', 18, 1, '18612340002');
INSERT INTO mybatis_test.user_info(username, `password`, age, gender, phone)
VALUES ('lisi', 'lisi', 18, 1, '18612340003');
INSERT INTO mybatis_test.user_info(username, `password`, age, gender, phone)
VALUES ('wangwu', 'wangwu', 18, 1, '18612340004');
查询user_info表的数据
select * from user_info;
创建对应的实体类UserInfo
注意:实体类的属性名与表中的字段名⼀⼀对应
@Data
@NoArgsConstructor
@AllArgsConstructor
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;
}
2.2 配置数据库连接字符串
Mybatis中要连接数据库,需要数据库相关参数配置
application.yml文件,配置内容如下:
#数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
application.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=123456
2.3 书写持久层代码
在项目中,创建持久层接口UserInfoMapper
@Mapper
public interface UserInfoMapper {
@Select("select * from user_info where username =#{userName}")
List<UserInfo> queryAllUsers(String userName);
}
2.4 单元测试
使用Idea自动生成测试类
1. 在需要测试的Mapper接口中,右键->Generate->Test
2、书写测试代码
在test包下会生成相应的测试类
@Slf4j //日志注解
@SpringBootTest //表示是测试类
class UserInfoMapperTest {
@Autowired //自动注入
private UserInfoMapper userInfoMapper;
@Test
//表示是测试方法
void queryAllUsers() {
List<UserInfo> queryAllUsers = userInfoMapper.queryAllUsers();
for (UserInfo queryAllUser : queryAllUsers) {
System.out.println(queryAllUser.toString());
}
}
}
3、在测试类测试
运行结果如下:
注意 deleteFlag createTime updateTime 三个字段为null 是因为 UserInfo类中的字段与MySQL字段名称不一致所造成的。 解决方法是添加yml文件中添加驼峰转换
mybatis:
configuration:
map-underscore-to-camel-case: true
再次运行结果如下: deleteFlag createTime updateTime 三个字段此时不为null
三. MyBatis的基础操作
3.1 打印日志
在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果
在配置文件中进行配置即可.
application.yml,配置内容如下
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
application.properties, 配置内容如下
#指定mybatis输出日志的位置,输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdoutImpl
重新运行程序,可以看到SQL执行内容,以及传递参数和执行结果
3.2 参数传递
SQL语句中的id值不能写成固定数值,需要变为动态的数值解决方案:在queryByld方法中添加一个参数(id),将方法中的参数,传给SQL语句使用#{}的方式获取方法中的参数
@Select("select * from user_info where id =id")
UserInfo queryById(Integer id);
如果mapper接口方法形参只有⼀个普通类型的参数,#{…}里面的属性名可以随便写,如:#{id}、# {value}。建议和参数名保持⼀致.
添加测试用例
@Test
void queryById() {
UserInfo userInfo = userInfoMapper.queryById(2);
System.out.println(userInfo.toString());
}
运行结果:
也可以通过@Param,设置参数的别名,如果使用@Param设置别名,#..}里面的属性名必须和
@Param设置的一样
@Select("select * from user_info where id =#{userId}")
UserInfo queryById(@Param("userId") Integer id);
3.3 增(Insert)
使用UserInfo对象的属性名来获取参数
@Insert("insert into user_info (username, `password`, age, gender, phone) values (#{username},#{password},#{age},#{gender},#{phone})")
nteger addUser(UserInfo userInfo);
测试代码:
@Test
void addUser() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("zhaoliu");
userInfo.setPassword("123456");
userInfo.setAge(20);
userInfo.setGender(1);
userInfo.setPhone("12345678901");
userInfoMapper.addUser(userInfo);
}
运行后,观察数据库执行结果
如果设置了@Param属性,#.{.}需要使用 参数.属性 来获取
@Insert("insert into user_info (username, `password`, age, gender, phone) values (#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")
Integer addUser(@Param("userInfo") UserInfo userInfo);
返回主键
但有些情况下,数据插入之后,还需要有后续的关联操作,需要获取到新插入数据的id
比如订单系统 当我们下完订单之后,需要通知物流系统,库存系统,结算系统等,这时候就需要拿到订单ID
如果想要拿到自增id,需要在Mapper接接的方法上添加⼀个Options的注解
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info (username, `password`, age, gender, phone) values " +
"(#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")
Integer addUser(@Param("userInfo") UserInfo userInfo);
测试数据:
@Test
void addUser() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("tianqi");
userInfo.setPassword("123456");
userInfo.setAge(22);
userInfo.setGender(2);
userInfo.setPhone("12345678966");
Integer count = userInfoMapper.addUser(userInfo);
System.out.println("添加数据条数:" + count + ",数据ID:" + userInfo.getId());
}
}
运行结果
注意:设置useGeneratedKeys=true之后,方法返回值依然是受影响的行数,自增id会设置在上
述keyProperty指定的属性中.
3.4 删(Delete)
Mapper接口
@Delete("delete from user_info where id =#{id}")
void delete(Integer id);
测试接口
@Test
void delete() {
userInfoMapper.delete(1);
}
运行结果
3.5 改(Update)
Mapper接口
@Update("update user_info set username=#{username} where id =#{id}")
void update(UserInfo userInfo);
测试接口:
将id为1的用户名修改为 ADMIN
@Test
void update() {
UserInfo userInfo = new UserInfo();
userInfo.setId(1);
userInfo.setUsername("ADMIN");
userInfoMapper.update(userInfo);
}
运行结果:
3.6 查(Select)
我们在上⾯查询时发现,有几个字段是没有赋值的,只有Java对象属性和数据库字段⼀模⼀样时,才会进行赋值
Mapper接口:
@Select("select * from user_info")
List<UserInfo> queryAllUsers();
测试代码:
@Test
//表示是测试方法
void queryAllUsers() {
List<UserInfo> queryAllUsers = userInfoMapper.queryAllUsers();
for (UserInfo queryAllUser : queryAllUsers) {
System.out.println(queryAllUser.toString());
}
}
运行结果:
从运行结果上可以看到,我们SQL语句中,查询了delete_flag,create_time,update_time,但是这几个 属性却没有赋值.
原因分析:
当自动映射查询结果时,MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写)。这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性。
解决策略:
3.6.1 起别名
@Select("select id, username,`password`,age, gender, phone, delete_flag as deleteFlag," +
"create_time as createTime, update_time as updateTime from user_info")
List<UserInfo> queryAllUsers();
测试:
delete_flag,create_time,update_time 三个字端非null
3.6.2 结果映射
@Select("select id, username,`password`,age, gender, phone, delete_flag," +
"create_time , update_time from user_info")
@Results({
@Result(column = "delete_flag",property = "deleteFlag"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUsers();
测试:
如果其他SQL,也希望可以复用这个映射关系,可以给这个Results定义⼀个名称
@Select("select id, username,`password`,age, gender, phone, delete_flag," +
"create_time , update_time from user_info")
@Results(id = "resultMap", value = {
@Result(column = "delete_flag", property = "deleteFlag"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
List<UserInfo> queryAllUsers();
@Select("select * from user_info where id =#{userId}")
@ResultMap("resultMap")
UserInfo queryById(@Param("userId") Integer id);
使用id属性给该Results定义别名,使用@ResultMap注解来复用其他定义的ResultMap
3.6.3 开启驼峰命名(推荐)
通常数据库列使用蛇形命名法进行命名(下划线分割各个单词),而Java属性一般遵循驼峰命名法约定为了在这两种命名方式之间启用自动映射,需要将mapUnderscoreToCamelCase设置为true。
驼峰命名规则:abc_xyz => abcXyz
表中字段名:abc_xyz
类中属性名:abcXyz
在application.yml 中配置如下代码
mybatis:
configuration:
map-underscore-to-camel-case: true #开启驼峰转换
Mapper接口:
@Select("select * from user_info")
List<UserInfo> queryAllUsers();
测试类:
@Test//表示是测试方法
void queryAllUsers() {
List<UserInfo> queryAllUsers = userInfoMapper.queryAllUsers();
for (UserInfo queryAllUser : queryAllUsers) {
System.out.println(queryAllUser.toString());
}
}
运行结果:
字段全部赋值成功