MyBatis 入门
目录
一. MyBatis 概述
二. MyBatis 入门
1. 创建工程, 导入依赖
2. 数据准备
3. 配置数据库相关信息
4. 编写持久层代码
5. 编写测试代码
三. MyBatis基础操作.
1. 打印日志
2. 参数传递
3. 增 (Insert)
4. 删 (Delete)
5. 改 (Update)
6. 查 (Select)
(1) 起别名
(2) 结果映射
(3) 开启驼峰命名与蛇形命名转换
四. MyBatis XML配置文件
(1) 配置连接字符串和MyBatis
(2) 编写持久层代码
① 添加Mapper接口
② 添加UserInfoXMLMapper.xml
③ 单元测试
(3) 增删改查操作
① 增 (Insert)
② 删 (Delete)
③ 改 (Update)
④ 查 (Select)
一. MyBatis 概述
前面我们学习过使用JDBC编程对数据库进行操作. 不过JDBC对于数据库的操作过于麻烦, 所以我们在Spring中引入MyBatis框架, 来帮助我们对数据库进行操作.
MyBatis 是一款优秀的持久层框架, 用于简化JDBC的开发.
持久层(Mapper/Dao), 就是用来操作数据库的.
简单来说, MyBatis 是一个能够更简单地完成程序和数据库交互的框架. 接下来,我们就通过一个入门程序,让大家感受一下通过Mybatis如何操作数据
二. MyBatis 入门
1. 创建工程, 导入依赖
MyBatis 是一个持久层框架, 具体的数据存储和数据操作还是在MySQL中进行的. 所以我们还需要添加MySQL驱动.
项目工程创建完成后, 我们就会发现: 在pom.xml文件中, 自动导入了Mybatis依赖和MySQL驱动依赖.
2. 数据准备
创建用户表:
-- 创建数据库
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: (其中实体类的属性名和表中的字段名 一一对应)
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驱动类. 这些配置信息均在配置文件中完成.
# 数据库连接配置
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. 编写持久层代码
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语句的执行, 执行传递的参数以及执行结果. 在配置文件中进行配置即可.
mybatis:
configuration: # 配置打印 MyBatis⽇志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
配置好之后, 重新运行程序, 即可看到SQL的执行内容.
2. 参数传递
如果我们现在需要查找 id=4 的用户, 对应的 sql语句 就是 select * from userinfo where id=4.
@Select("select * form userinfo where id = 4")
public UserInfo selectUser();
但是这样的话, 就只能查询到 id为4 用户的数据. 所以我们不建议把sql语句写死. 我们需要把 id 变为动态的数值.
解决办法就是在selectUser方法中添加一个参数(id), 将方法中的参数传递给sql语句.使用 #{} 的方式在sql语句中获取方法中传递的参数.
@Select("select * from userinfo where id = #{id}")
public UserInfo selectUser(Integer id);
void selectUser() {
System.out.println(userInfoMapper.selectUser(4));
}
[注意]:
- 如果参数只有一个, #{ } 内的属性名称可以随便写.
- 如果参数有多个, 属性名和参数名必须保持一致; 或按照顺序对应(param1, param2…).
① 按名称对应
@Select("select * from userinfo where username = #{name} and id = #{id}")
public UserInfo selectUser2(String name,Integer id);
@Test
void selectUser2() {
System.out.println(userInfoMapper.selectUser2("admin",1));
}
② 按顺序对应
@Select("select * from userinfo where username = #{param1} and id = #{param2}")
public UserInfo selectUser3(String name,Integer id);
@Test
void selectUser3() {
System.out.println(userInfoMapper.selectUser3("admin",1));
}
[注]: 也可以通过@Param注解, 设置参数的别名. 如果使用@Param设置别名, 注解中的别名必须和sql中的属性名保持一致. 举例如下:
@Select("select * from userinfo where id = #{ID}")
public UserInfo selectUser4(@Param("ID") Integer id);
@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接口:
@Insert("insert into userinfo (id,username,password,age,gender,phone) values (" +
"#{id},#{username},#{password},#{age},#{gender},#{phone})")
public Integer insertUser1(UserInfo userInfo);
这里我们可以直接使用UserInfo对象的属性来获取参数:
@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的注解.
@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接口:
@Delete("delete from userinfo where id = #{id}")
public Integer deleteUser1(Integer id);
@Test
void deleteUser1() {
System.out.println(userInfoMapper.deleteUser1(6));
}
5. 改 (Update)
sql语句:
update userinfo set username="zhaoliu" where id=5
把SQL中的常量替换为动态的参数
Mapper接口:
@Update("update userinfo set username = #{username} where id = #{id}")
public Integer updateUser(UserInfo userInfo);
@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语句中, 给列名起别名, 保持 别名 和 实体类属性名 一致.
@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) 结果映射
@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="映射名称") 来实现映射的复用.
@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> 标签来完成.
<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 来指定结果映射.
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
...
</select>
我们也可以使用 extends 来继承结果映射, 在继承过来的结果映射中添加还未存在的字段.
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.jrj.forums.model.Article">
<result column="content" jdbcType="LONGVARCHAR" property="content" />
</resultMap>
(3) 开启驼峰命名与蛇形命名转换
通常数据库列使用蛇形命名法进行命名(下划线分割各个单词), 而Java属性⼀般遵循驼峰命名法约定.
为了在这两种命名方式之间启用自动映射, 需要 将mapUnderscoreToCamelCase设置为true . 我们需要在配置文件中配置.
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰⾃动转换
转换规则: abc_xyz --> abcXyz (蛇形转换为小驼峰).
而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
# 数据库连接配置
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接口
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
<?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接口.
查询所有用户的具体实现:
<?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.
@Test
void selectAllUser1() {
List<UserInfo> list = userInfoXMLMapper.selectAllUser1();
for (UserInfo userInfo: list){
System.out.println(userInfo);
}
}
(3) 增删改查操作
① 增 (Insert)
UserInfoMapper接口:
public Integer insertUser(UserInfo userInfo);
XML:
<insert id="insertUser">
insert into userinfo (id,username,password,age,gender,phone) values (#{id},#{username},#{password},#{age},#{gender},#{phone})
</insert>
测试代码:
@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接口:
public Integer insertUser(@Param("userinfo") UserInfo userInfo);
XML:
<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)
删, 改, 查和增是同样的道理, 我们不再赘述.