目录
1.MyBatis是什么?
2.为什么要学习 MyBatis?
3.怎么学MyBatis?
4.第⼀个MyBatis查询
4.1创建数据库表
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使⽤数据数据
use mycnblog;
-- 创建表[⽤户表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
) default charset 'utf8mb4';
-- 创建⽂章表
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime datetime default now(),
updatetime datetime default now(),
uid int not null,
rcount int not null default 1,
`state` int default 1
)default charset 'utf8mb4';
-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
vid int primary key,
`title` varchar(250),
`url` varchar(1000),
createtime datetime default now(),
updatetime datetime default now(),
uid int
)default charset 'utf8mb4';
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`,
`createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1)
;
insert into articleinfo(title,content,uid)
values('Java','Java正⽂',1);
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://ww
w.baidu.com',1);
select * from userinfo;
select * from userinfo where username='admin' and password= '' or 1='1';
select * from userinfo ORDER BY id DESC;
select * from userinfo where username like CONCAT('%','m','%');
4.2 添加MyBatis框架⽀持
在创建新项目时,在添加Spring框架的基础上,加入MyBatis FrameWork和MySQL Driver框架支持。
4.3配置连接字符串和MyBatis
4.3.1配置连接字符串
在application.yml文件中添加以下内容:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
username: root
password: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
4.3.2 配置 MyBatis 中的 XML 路径
MyBatis 的 XML 中保存是查询数据库的具体操作 SQL,配置如下:
#数据库配置
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4.4添加业务代码
4.4.1 添加实体类
先添加用户的实体类,使用Data注解可以自己生成一些方法:
@Data
public class User {
private Integer id;
private String name;
private String pwd;
private String photo;
private Date createtime;
private Date updatetime;
private Integer state;
}
4.4.2添加Mapper接口
@Mapper
public interface UserMapper {
/**
* 查询所有数据
* @return
*/
List<User> queryAll();
}
4.4.3 添加 UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybati
s.org/dtd/mybatis-3-mapper.dtd">
<!--实现的哪个接口-->
<mapper namespace="com.example.springmybatisdemo.mapper.UserMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybati
s.org/dtd/mybatis-3-mapper.dtd">
<!--实现的哪个接口-->
<mapper namespace="com.example.springmybatisdemo.mapper.UserMapper">
<!-- 实现的是接口里的哪个方法-->
<select id="queryAll" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo
</select>
</mapper>
4.4.4 添加 Service
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> selectAllUser() {
return userMapper.queryAll();
}
}
4.4.5 添加 Controller
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/selectAll")
public List<User> selectAllUser(){
return userService.selectAllUser();
}
}
4.4.6 使⽤ postman 测试
4.4.7带有参数的查询
/**
* 根据id查询
* 当只有一个参数时,可以不用加注解,参数名可以随便写,可以不一样
*/
User queryById(@Param("uid") Integer id);
当只有一个参数时,接口内的参数名可以随便写,不用加注解,有多个参数时,需要添加@Param注解,注解内的value值与xml中的要一致。
<select id="queryById" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where id = ${uid}
</select>
把xml文件中的id改成uid,可以执行成功。
5.增、删、改操作
5.1增加操作
mapper接口:
Integer insert(User user);
xml实现:
<insert id="insert">
<!-- mybatis会自动帮我们生成以属性名命名的变量-->
insert into userinfo (username,password,photo) values(#{username},#{password},#{photo})
</insert>
使用Test方法实现结果:
如果设置了Param注解,就必须要使用Param注解的命名
Integer insert2(@Param("userinfo") User user);
<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
insert into userinfo (username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
</insert>
想要拿到自增id,可以在xml文件的insert标签里面添加以下内容:
<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
insert into userinfo (username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
</insert>
5.2 修改⽤户操作
mapper接口:
void update(User user);
<update id="update">
update userinfo set username=#{username},password=#{password} where id=#{id}
</update>
5.3 删除⽤户操作
mapper接口:
/**
* 删除数据
*/
void delete(Integer id);
mapper.xml 实现代码:
<delete id="delete">
delete from userinfo where id=#{id}
</delete>
6.查询操作
6.1 单表查询
上面我们已经实现了简单的单表查询操作,但我们的SQL语句属性都使用的是#,如果换成$会有什么结果呢?
根据id查询:
User queryById(@Param("uid") Integer id);
<select id="queryById" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where id = #{uid}
</select>
使用#查询结果:
使用$查询结果:
<select id="queryById" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where id = ${uid}
</select>
根据name查询:
User queryByName(String username);
使用#:
<select id="queryByName" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where username= #{username}
</select>
使用$:
<select id="queryByName" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where username= ${username}
</select>
为什么使用$就会出现异常呢?
6.1.1 参数占位符 #{} 和 ${}
6.1.2SQL注入问题
User queryByNameAndPassword(@Param("username") String username,@Param("password") String password);
<select id="queryByNameAndPassword" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where username="${username}" and password = "${password}"
</select>
@Test
void queryByNameAndPassword() {
String username="admin";
// String password="admin";
String password="' or 1='1";
User user=userMapper.queryByNameAndPassword(username,password);
log.info(user==null?null:user.toString());
}
可以从上述的结果中发现,我们给密码赋值为“' or 1='1” ,即使不需要密码,也可以查出全部信息,这就会造成很严重的安全问题。
6.1.3${} 优点
当我们使用#{}进行排序操作时:
<select id="queryByOrder" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo order by id #{order}
</select>
通过结果可以发现,当我们使用#操作时,会将传入排序的方法认为是想要查询的值,程序查不出来就会报错。
使用${}:
<select id="queryByOrder" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo order by id ${order}
</select>
当我们使用${},程序就可以正常的查询出来。
所以排序时,只能使用${},为了防止SQL注入的问题,我们再前端让用户只能点击来排序,参数由后端来拼接,后端在查询之前,对参数进行校验,只能传入两个值:desc,asc。
6.1.4 like 查询
使用#{}:
<select id="queryByLike" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where username like '%#{name}%'
</select>
当使用#{}时,程序会报错;
使用${}:
<select id="queryByLike" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where username like '%${name}%'
</select>
使用${}不会报错,但${}存在SQL注入问题,该怎么解决呢?
使用mysql的内置函数concat:
<select id="queryByLike" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where username like concat('%',#{name},'%')
</select>
6.2多表查询
然⽽即使是最简单查询⽤户的名称也要设置返回的类型,否则会出现如下错误:
6.2.1 返回类型:resultType
<select id="queryByLike" resultType="com.example.springmybatisdemo.model.User">
select * from userinfo where username like concat('%',#{name},'%')
</select>
6.2.2返回字典映射:resultMap
<resultMap id="BaseMap" type="com.example.springmybatisdemo.model.User">
<id property="id" column="id"></id>
<result property="name" column="username"></result>
<result property="pwd" column="password"></result>
<result property="createtime" column="cteatetime"></result>
<result property="updatetime" column="updatetime"></result>
</resultMap>
<select id="queryByMap" resultMap="BaseMap">
select * from userinfo
</select>
一个xml文件中可以存在多个resultMap,只要id不同就可以。
如果程序中的属性和SQL字段名不一致,就会查不出来,但使用resultMap就可以解决这个问题。
6.2.3 多表查询
@Data
public class ArticleInfo {
private Integer id;
private String title;
private String content;
private Date createtime;
private Date updatetime;
private Integer uid;
private Integer rcount;
private Integer state;
// //作者相关信息
private User user;
private String username;
}
此时我们就需要使⽤特殊的⼿段来实现联表查询了。
6.2.3.1 ⼀对⼀的表映射
⼀对⼀映射要使⽤ <association> 标签,具体实现如下(⼀篇⽂章只对应⼀个作者):
<resultMap id="BaseMap" type="com.example.springmybatisdemo.model.ArticleInfo">
<id property="id" column="id"></id>
<result property="title" column="title"></result>
<result property="content" column="content"></result>
<result property="createtime" column="createtime"></result>
<result property="updatetime" column="updatetime"></result>
<association property="user" resultMap="com.example.springmybatisdemo.mapper.UserMapper.BaseMap">
</association>
</resultMap>
引入其他xml的resultMap,路径是:xml的namespace+resultMap的id
常见使用写法:
<select id="queryArticle2" resultMap="BaseMap2">
select ta.*,
tb.id as userid,
tb.username as username
ta.id as id,
ta.title as title,
ta.content as content,
ta.createtime as createtime ,
ta.updatetime as updatetime
from articleinfo ta left join userinfo tb on ta.uid=tb.id
</select>
<resultMap id="BaseMap2" type="com.example.springmybatisdemo.model.ArticleInfo">
<id property="id" column="id"></id>
<result property="title" column="title"></result>
<result property="content" column="content"></result>
<result property="createtime" column="createtime"></result>
<result property="updatetime" column="updatetime"></result>
<result property="uid" column="userid"></result>
<result property="username" column="username"></result>
</resultMap>
7.复杂情况:动态SQL使⽤
7.1 <if>标签
<insert id="insertByCondition">
insert into articleinfo(
<if test="title!=null">
title
</if>
<if test="content!=null">
,content
</if>
<if test="uid!=null">
,uid
</if>
<if test="state!=null">
,state
</if>
)
values
(
<if test="title!=null">
#{title}
</if>
<if test="content!=null">
,#{content}
</if>
<if test="uid!=null">
,#{uid}
</if>
<if test="state!=null">
,#{state}
</if>
)
</insert>
但当我们给第一个属性不赋值时,会出现错误:
这时候就可以使用trim标签:
7.2 <trim>标签
<insert id="insertByCondition">
insert into articleinfo
<trim prefix="(" suffix=")" suffixOverrides="," prefixOverrides=",">
<if test="title!=null">
title
</if>
<if test="content!=null">
,content
</if>
<if test="uid!=null">
,uid
</if>
<if test="state!=null">
,state
</if>
</trim>
values
<trim prefix="(" suffix=")" prefixOverrides="," suffixOverrides=",">
<if test="title!=null">
#{title}
</if>
<if test="content!=null">
,#{content}
</if>
<if test="uid!=null">
,#{uid}
</if>
<if test="state!=null">
,#{state}
</if>
</trim>
</insert>
7.3 <where>标签
List<ArticleInfo>queryByCondition(@Param("uid") Integer uid,@Param("state") Integer state);
<select id="queryByCondition" resultType="com.example.springmybatisdemo.model.ArticleInfo">
select *
from articleinfo
<if test="uid!=null">
and uid=#{uid}
</if>
<if test="state!=null">
and state=#{state}
</if>
</select>
我们传入两个参数:
但当我们只传入一个参数时:
报错了,where后面直接跟的and,第一个参数不传,结果where和and中间的数据没有了,造成sql语句报错,该怎么解决呢?
1.在where后面加1=1
<select id="queryByCondition" resultType="com.example.springmybatisdemo.model.ArticleInfo">
select *
from articleinfo where 1=1
<if test="uid!=null">
and uid=#{uid}
</if>
<if test="state!=null">
and state=#{state}
</if>
</select>
2.使用where标签:
<select id="queryByCondition" resultType="com.example.springmybatisdemo.model.ArticleInfo">
select *
from articleinfo
<where>
<if test="uid!=null">
and uid=#{uid}
</if>
<if test="state!=null">
and state=#{state}
</if>
</where>
</select>
不传入第一条数据:
一条数据都不传入:
7.4 <set>标签
根据传⼊的⽤户对象属性来更新⽤户数据,可以使⽤<set>标签来指定动态内容。
当我们更新多个字段时,也会出现与上面一样的问题,我们就可以通过set标签来解决:
<update id="updateByCondition">
update articleinfo
<set>
<if test="uid!=null">
state=#{state},
</if>
<if test="state!=null">
uid=#{state};
</if>
</set>
</update>
7.5 <foreach>标签
void batchDelete(@Param("ids") List<Integer>ids);
<delete id="batchDelete">
delete from articleinfo where id in
<foreach collection="ids" open="(" close=")" separator="," item="deleteId">
#{deleteId}
</foreach>
</delete>
8.补充
MyBatis的实现有两种方式:
1.xml方式,也就是我们上面所用到的方式
2.注解方式
@Mapper
public interface UserMapper2 {
@Select("select * from userinfo")
List<User> queryAll();
@Select("select * from userinfo where id= #{id}")
List<User> queryById(Integer id);
}
对于比较简单的SQL语句,注解的方式写起来很简单,但是负责一点的SQL语句写起来会非常复杂
字符串的拼接很繁琐,而且没有错误提示,比较不适合新手,所以新手学习MyBatis框架,建议先使用xml方式再学习注解的方式。