1. 什么是MyBatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。 ? MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。 ? MyBatis 可以通过简单的 XML 或 注解 来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
用一句简单的话来概述: MyBatis 是更简单完成程序和数据库交互的工具或 MyBatis 是更简单的操作和读取数据库工具
2. 配置 MyBatis 的开发环境
2.1 准备工作, 创建数据库和表
create database if not exists MyBlogSystem;
use MyBlogSystem;
drop table if exists blog;
create table blog (
blogId int primary key auto_increment,
title varchar(1024),
content mediumtext,
postTime datetime,
userId int
);
drop table if exists user;
create table user (
userId int primary key auto_increment,
username varchar(128) unique,
password varchar(128)
);
2.2 添加 MyBatis 依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.3 配置连接字符串和 MyBatis
2.3.1 配置数据库连接信息
spring.datasource.url=jdbc:mysql://localhost:3306/MyBlogSystem?characterEncoding=utf8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2.3.2 配置 MyBatis 中的 XML 路径
配置 mybatis 的文件路径, 在 resources/mapper 创建所有表的xml文件
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
这里必须要一样
2.4 添加代码
2.4.1 添加实体类
添加用户的实体类
@Getter
@Setter
@ToString
public class User {
public int userId;
public String username;
public String password;
}
2.4.2 添加 mapper 接口
@Mapper
public interface UserMapper {
List<User> getAll();
}
2.4.3 添加 UserMapper.xml
固定的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.example.demo.mapper.UserMapper">
</mapper>
这里的 UserMapper.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.example.demo.mapper.UserMapper">
<select id="getAll" resultType="com.example.demo.model.User">
select * from user;
</select>
</mapper>
2.4.4 添加 Service
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public List<User> getAll() {
return userMapper.getAll();
}
}
2.4.5 添加 Controller
@RestController
@RequestMapping("/user")
public class UserController3 {
@Autowired
private UserService userService;
@RequestMapping("/getall")
public List<User> getAll() {
return userService.getAll();
}
}
2.4.6 测试代码
2.5 注意事项
- 注意配置文件中的mapper配置和mapper文件夹的对应
- UserMapper.xml 这里需要一些路径对应, 以及id格式对应 , 这里的resultType是返回的类型.
- 按照业务规定流程写代码
3. MyBatis 的增删改查操作.
3.1 增加操作
步骤:
- 添加 实体类(这里有了就不需要添加了)
- 添加 controller
- 添加 service
- 添加 mapper 接口
- 添加 xml
① 添加 controller 实现代码
@RestController
@RequestMapping("/user")
public class UserController3 {
@Autowired
private UserService userService;
@RequestMapping("/insert")
public Integer insert(User user) {
return userService.insert(user);
}
}
② 添加 service 实现代码
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public Integer insert(User user) {
return userMapper.insert();
}
}
③ 添加 mapper 接口
@Mapper
public interface UserMapper {
Integer insert();
}
④ 添加 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.example.demo.mapper.UserMapper">
<insert id="insert">
insert into user values (null, #{username},#{password});
</insert>
</mapper>
⑤ 测试代码
3.2 查找操作
步骤:
- 添加 实体类(这里有了就不需要添加了)
- 添加 controller
- 添加 service
- 添加 mapper 接口
- 添加 xml
① 添加 controller
@RequestMapping("/getall")
public List<User> getAll() {
return userService.getAll();
}
② 添加 service
@RequestMapping("/getall")
public List<User> getAll() {
return userService.getAll();
}
③ 添加 mapper 接口
public List<User> getAll() {
return userMapper.getAll();
}
④ 添加 xml
<select id="getAll" resultType="com.example.demo.model.User">
select * from user;
</select>
⑤ 测试代码
3.3 修改操作
步骤:
- 添加 实体类(这里有了就不需要添加了)
- 添加 controller
- 添加 service
- 添加 mapper 接口
- 添加 xml
① 添加controller
@RequestMapping("/update")
public Integer update(Integer userId, String password) {
return userService.update(userId,password);
}
② 添加 service
public Integer update(Integer userId, String password) {
return userMapper.update(userId,password);
}
③ 添加 mapper 接口
Integer update(Integer userId, String password);
④ 添加 xml
<update id="update">
update user set password=#{password} where userId=#{userId}
</update>
⑤ 测试代码
3.4 删除操作
步骤:
- 添加 实体类(这里有了就不需要添加了)
- 添加 controller
- 添加 service
- 添加 mapper 接口
- 添加 xml
① 添加 controller
@RequestMapping("/delete")
public Integer delete(Integer userId) {
return userService.delete(userId);
}
② 3.4.2 添加 service
public Integer delete(Integer userId) {
return userMapper.delete(userId);
}
③ 添加 mapper 接口
Integer delete(Integer userId);
④ 添加 xml
<delete id="delete">
delete from user where userId=#{userId}
</delete>
⑤ 测试代码
4. MyBatis 的进阶操作
4.1 返回自增id
这里用增加操作来进行演示
4.1.1 添加 controller 实现代码
@RequestMapping("/insert2")
public Integer insert2(User user) {
if (user == null || user.getUsername() == null
|| user.getPassword() == null
|| user.getUsername().equals("")
|| user.getPassword().equals("")
) {
return 0;
}
userService.insert2(user);
return user.getUserId();
}
4.1.2 添加 service 实现代码
public void insert2(User user) {
userMapper.insert2(user);
}
4.1.3 添加 mapper 接口
void insert2(User user);
4.1.4 添加 xml
注意: 如果要返回自增id, 就不能像普通增加那样写xml 正确写法:
<insert id="insert2" useGeneratedKeys="true" keyProperty="userId" keyColumn="userId">
insert into user values (null,#{username}, #{password});
</insert>
- useGeneratedKeys: 这个会取出数据库内部生成的主键, 默认值是false
- keyColumn: 设置生成键值在表中的列名, 在某些数据库中, 当主键列不是表中第一列的时候, 是必须设置的.如果生成列不止一个, 可以用逗号分隔多个属性名称
- keyProperty: 指定能够唯一标识对象的属性, 默认值: 未设置(unset), .如果生成列不止一个, 可以用逗号分隔多个属性名称
4.1.5 测试代码
4.2 一对一的表映射
4.2.1 xml中具体的实现
一对一的表映射要使用 <association> 标签, 具体实现
<resultMap id="baseMap" type="com.example.demo.model.User">
<id column="userId" property="userId"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
</resultMap>
<resultMap id="BaseMap" type="com.example.demo.model.Blog">
<id column="blogId" property="blogId"></id>
<result column="title" property="title"></result>
<result column="content" property="content"></result>
<result column="postTime" property="postTime"></result>
<result column="userId" property="userId"></result>
<association property="user"
resultMap="com.example.demo.mapper.UserMapper.baseMap"
columnPrefix="u_">
</association>
</resultMap>
<select id="getAll" resultMap="BaseMap">
select b.*,u.userId u_userId,u.username u_username,u.password u_password from blog b left join user u on u.userId=b.userId
</select>
注意事项:
property 属性: 指定 Blog 表中对应的属性名, 与实体类一致, 这里是userresultMap 属性: 指定关联的结果集映射, 将基于该映射配置来组织用户数据columnPrefix 属性: 绑定一对一对象时, 多表前缀, 作用防止多张表出现相同字段查询的问题
4.2.2 Blog 实体类
@Data
public class Blog {
public int blogId;
public String title;
public String content;
public Timestamp postTime;
public int userId;
public User user;
}
4.2.3 添加 controll
@RequestMapping("/getall")
public List<Blog> getAll() {
return userService.getAll();
}
4.2.4 添加 service
public List<Blog> getAll() {
return userMapper.getAll();
}
4.2.5 添加 mapper
List<Blog> getAll();
4.2.6 测试运行
4.3 一对多的表映射
4.3.1 xml中具体的实现
一对多使用 <collection> 标签, 用法和 <association> 相同
<resultMap id="BaseMap2" type="com.example.demo.model.Blog">
<id column="blogId" property="blogId"></id>
<result column="title" property="title"></result>
<result column="content" property="content"></result>
<result column="postTime" property="postTime"></result>
<result column="userId" property="userId"></result>
<collection property="user"
resultMap="com.example.demo.mapper.UserMapper.baseMap"
columnPrefix="u_">
</collection>
</resultMap>
<select id="getUserById" resultMap="BaseMap2">
select b.*,u.username u_username,u.userId u_userId,u.password u_password from blog b left join user u on b.userId=u.userId where u.userId=#{userId}
</select>
4.3.2 添加 controller
@RequestMapping("/getUserById")
public List<Blog> getUserById(Integer userId) {
return userService.getUserById(userId);
}
4.3.3 添加 service
public List<Blog> getUserById(Integer userId) {
return userMapper.getUserById(userId);
}
4.3.4 添加 mapper接口
List<Blog> getUserById(Integer userId);
4.3.5 测试运行
5. MyBatis 的一些注意事项
5.1 参数占位符 #{} 和 ${}
编译预处理: MyBatis 在处理 #{} 时, 会将SQL 中的 #{} 替换为 ? 号, 使用 PreparedStatement 的 set 方法来赋值. 字符直接替换: MyBatis 在处理 ${} 时, 就是把 ${} 替换成变量的值.(缺点SQL注入)
注意: 当使用 #{} 的时候, 传递值为 String 则会加单引号. 使用${} 的时候, 就不会加单引号
5.1.1 ${} 的优点
在对一些商品进行排序的时候, 使用${}就可以, 而使用#{}就不行
<select id="getall2" resultType="com.example.demo.model.User">
select * from user order by userId ${sort};
</select>
5.2 SQL 注入问题
<select id="getAll3" resultType="com.example.demo.model.User">
select * from user where username= '${username}' and password='${password}'
</select>
当注入 'or 1='1 时, 所有的信息就暴露了
需要用户传递的就尽量使用 #{} , 程序员自己传递的就可以使用 ${}
5.3 like 查询
like 使用 #{} 会报错 例如一段sql代码 select * from user where username like '%#{username}%' ; 当username=admin 相当于 select * from user where username like '%'admin'%'
这里也不能直接使用 ${} .
可以使用mysql内置函数 concat() 来处理
<select id="findLike" resultType="com.example.demo.model.User">
select * from user where username like concat('%',#{username},'%')
</select>
5.4 实体类中的字段和mysql中的字段不匹配
例如: 这里就查询不到 name了
除了把名字改过来, 还可以使用 resultMap
<resultMap id="baseMap" type="com.example.demo.model.User">
<id column="userId" property="userId"></id>
<result column="username" property="name"></result>
<result column="password" property="password"></result>
</resultMap>
<select id="getAll" resultMap="baseMap">
select * from user
</select>
注意这里的resultMap的写法
5.5 想要在控制台查看 mybatis 的日志
在配置文件中添加代码
logging.level.com.example.demo=debug
mybaits.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
通过这样配置就能查看日志了
5.6 编写 MyBatis 更方便的插件
搜索 MyBatisX
下载之后 重启Idea 有这种鸟就是安装好了
6. 动态 SQL
6.1 if 标签
在填写表单的时候 经常会有一些字段必须要填写, 一些字段不必要填写, 那么这种情况就需要用的动态SQL的if标签
xml的演示
<insert id="insert4" useGeneratedKeys="true" keyColumn="blogId" keyProperty="blogId">
insert into blog(
title,
<if test="content != null">
content,
</if>
postTime,userId) values (#{title},
<if test="content != null">
#{content},
</if>
#{postTime},#{userId})
</insert>
注意这里的 if标签后面的test中的content是传入的属性,不是数据库中的字段.
6.2 trim 标签
当所有字段都是非必填项, 此时使用 if 标签就满足不了了
如 title是必填项, 后面都是非必填项, 此时sql拼装出来 就会变成 insert into blog(title,) values (title,) 这里的逗号就会导致sql报错.
<trim> 标签中有如下属性:
prefix : 表示整个语句块, 以prefix 的值作为前缀suffix : 表示整个语句块, 以suffix 的值作为后缀prefixOverrides : prefixOverrides 的值表示整个语句块要去掉的前缀suffixOverrides : suffixOverrides 的值表示整个语句块要去掉的后缀
<insert id="insert5" useGeneratedKeys="true" keyProperty="userId" keyColumn="userId">
insert into blog
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="title != null">
title,
</if>
<if test="content != null">
content,
</if>
<if test="postTime != null">
postTime,
</if>
<if test="userId != null">
userId,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="title != null">
#{title},
</if>
<if test="content != null">
#{content},
</if>
<if test="postTime != null">
#{postTime},
</if>
<if test="userId != null">
#{userId},
</if>
</trim>
</insert>
6.3 where 标签
如代码 select * from blog where userId=#{userId} 如果这里的 userId 是非必填项, 为空, 那么sql也会报错
下面的<where> 标签也可以使用<trim prefix="where" prefixOverrides="and"> 替换。
<select id="selectAll" resultType="com.example.demo.model.User">
select * from blog
<where>
<if test="blogId != null">
blogId = #{blogId}
</if>
<if test="userId != null">
and userId = #{userId}
</if>
</where>
</select>
<where> 标签, 如果有查询条件就会生成where, 如果没有查询条件就不会生成where- where会判断第一个条件前面有没有and, 如果有就会去掉.
6.4 set 标签
更新的时候也会出现问题, 使用 <set> 标签来解决 set标签和where标签差不多
<update id="testUpdate">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
</set>
where userId=#{userId}
</update>
6.5 foreach 标签
<foreach> 标签有如下属性:
- collection:绑定?法参数中的集合,如 List,Set,Map或数组对象
- item:遍历时的每?个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的字符串
<delete id="testDe">
delete from user where userId in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
|