1.什么是MyBatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
简单说 MyBatis 是更简单完成程序和数据库交互的工具。
2.如何使用MyBatis
- 先添加MyBatis框架的支持
- 先来看看常见的项目开发框架
properties配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&userSSL=false
spring.datasource.username=root
spring.datasource.password=123456
# 如果使用 MySQL 是 5.x 之前的使用的是“com.mysql.jdbc.Driver”,
# 如果是大于 5.x 使用的是“com.mysql.cj.jdbc.Driver”。
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
实体类(model)
@Setter
@Getter
public class UserInfo {
private Integer id;
private String username;
private String password;
private String photo;
private Date createTime;
private Date updateTime;
private int state;
}
控制器层(controller)
负责跟用户交互,Service 需要注入到控制层的类中。
@Controller
@ResponseBody
public class UserController {
// 需要使用 Service,需要进行注入
@Autowired
private UserService userService;
@RequestMapping("/getall")
public List<UserInfo> getAll(){
return userService.getAll();
}
}
服务层(Service)
服务器是控制层和数据持久层之间的桥梁。需要将数据持久层的对象注入都 Service 的类中。注入标红不是错误,不影响。
@Service
public class UserService {
// Service 是进行数据组装的 不直接和数据库进行交互
@Autowired
private UserMapper userMapper;
public List<UserInfo> getAll(){
return userMapper.getAll();
}
}
数据持久层(mapper)
分为两部分:一个是接口,实现方法定义。一个是resources下的mapper实现方法的xml。
@Mapper
public interface UserMapper {
List<UserInfo> getAll();
}
数据持久层的实现,mybatis 的固定 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">
<!--namespace:xml 实现接口的完整路径-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<!--id:是和 Interface(接⼝)中定义的⽅法名称⼀样的,表示对接⼝的具体实现⽅法-->
<!--resultType:是返回的数据类型,也就是开头我们定义的实体类-->
<select id="getAll" resultType="com.example.demo.model.UserInfo">
<!--对应的sql-->
select * from userinfo
</select>
</mapper>
resultType 和 resultMap
resultType用来指定结果类型,用法简单,但是如果实体类中的属性名和数据库表中的字段名不一致那么将查询不到结果。不能省略,省略会报错。
resultMap 返回的是映射,但是它可以实现属性和字段不一致的映射,让查询结果能够正常。
我们发现使用resultType查询不到name的值。
我们使用 resultMap 来看看
<!--resultMap 就是做映射的 映射数据库表和实体类 type-->
<!--数据库字段 column 实体类字段 property-->
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<result column="id" property="id"></result>
<result column="username" property="name"></result>
</resultMap>
<select id="getAll" resultMap="BaseMap">
select * from userinfo
</select>
可以访问到全部数据。
模糊查询
<select id="findListByName" resultMap="BaseMap">
select * from userinfo where username like concat('%',#{name},'%')
</select>
多表查询
一对一的表映射
另一个实体类(文章信息)
@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 UserInfo user;
private List<ArticleInfo> artList;
}
mapper 下的 ArticleMapper
@Mapper
public interface ArticleMapper {
List<ArticleInfo> getAll();
List<ArticleInfo> getAll2();
}
resources -> mapper -> ArticleMapper.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">
<!--namespace:xml 实现接口的完整路径-->
<mapper namespace="com.example.demo.mapper.ArticleMapper">
<resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
<id column="id" property="id"></id>
<result column="title" property="title"></result>
<result column="content" property="content"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="uid" property="uid"></result>
<result column="rcount" property="rcount"></result>
<result column="state" property="state"></result>
<association property="user"
resultMap="com.example.demo.mapper.UserMapper.BaseMap"
columnPrefix="u_">
</association>
</resultMap>
<select id="getAll" resultMap="BaseMap">
<!--
select articleinfo.*,userinfo.* from articleinfo left join userinfo on articleinfo.uid = userinfo.id
-->
select a.*,u.* from articleinfo a left join userinfo u on a.uid = u.id
<!--User对象没有值-->
</select>
<select id="getAll2" resultMap="BaseMap">
select a.*,u.id u_id,u.username u_username,u.password u_password from articleinfo a
left join userinfo u on
a.uid=u.id
</select>
</mapper>
测试结果
多对多的表映射(用户表成为主表)
用户表的实体类
@Data
public class UserInfo {
private Integer id;
private String name;
private String password;
private String photo;
private Date createTime;
private Date updateTime;
private int state;
private List<ArticleInfo> artList;
}
用户表的xml
<resultMap id="BaseMap2" type="com.example.demo.model.UserInfo">
<id column="id" property="id"></id>
<result column="username" property="name"></result>
<result column="password" property="password"></result>
<result column="photo" property="photo"></result>
<result column="createtime" property="createTime"></result>
<result column="updatetime" property="updateTime"></result>
<result column="state" property="state"></result>
<!--多表查询-->
<collection property="artList"
resultMap="com.example.demo.mapper.ArticleMapper.BaseMap"
columnPrefix="a_">
</collection>
</resultMap>
<select id="getAll3" resultMap="BaseMap2">
select u.*,a.title a_title from userinfo u left join articleinfo a on u.id = a.uid;
</select>
三:增加操作
增加操作的两种需求:1.返回受影响行数。2.返回自增的 ID。
返回受影响的行数
controller
@RequestMapping("/add")
public int add(UserInfo userInfo){ // 使用对象来接受参数
// 控制层进行参数校验
if(userInfo==null
|| userInfo.getName()==null
|| userInfo.getPassword()==null
|| userInfo.getName().equals("")
|| userInfo.getPassword().equals(""))
{
return 0;
}
return userService.add(userInfo);
}
service
public int add(UserInfo userInfo) {
return userMapper.add(userInfo);
}
mapper
int add(UserInfo userInfo);
xml
<!--如何获取userinfo对象里的数据 #{}-->
<insert id="add">
insert into userinfo(username,password) value(#{name},#{password})
</insert>
返回自增的 ID
controller
@RequestMapping("/add2")
public int add2(UserInfo userInfo){ // 使用对象来接受参数
// 控制层进行参数校验
if(userInfo==null
|| userInfo.getName()==null
|| userInfo.getPassword()==null
|| userInfo.getName().equals("")
|| userInfo.getPassword().equals(""))
{
return 0;
}
userService.add2(userInfo);
return userInfo.getId();
}
service
public int add2(UserInfo userInfo) {
return userMapper.add2(userInfo);
}
mapper
int add2(UserInfo userInfo);
xml
<!--添加方法2 返回自增id-->
<!--
useGeneratedKeys:这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据
库内部⽣成的主键
keyProperty:指定能够唯⼀识别对象的属性
keyColumn="id": xml 会将自增的id插入到当前项目对象类中的指定的主键属性上
-->
<insert id="add2" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into userinfo(username,password) value(#{name},#{password})
</insert>
四:修改操作
controller
/**
* 修改密码
* */
@RequestMapping("/update")
public int updatePassword(UserInfo userInfo) {
// 参数校验
if (userInfo == null || userInfo.getId() <= 0
|| userInfo.getPassword()==null
|| userInfo.getPassword().equals(""))
{
return 0;
}
return userService.updatePassword(userInfo);
}
service
public int updatePassword(UserInfo userInfo) {
return userMapper.updatepassword(userInfo);
}
mapper
int updatepassword(UserInfo userInfo);
xml
<!-- 修改方法
-->
<update id="updatepassword">
update userinfo set password=#{password} where id=#{id}
</update>
五:删除操作
controller
@RequestMapping("/del")
public int del(Integer id){
// 参数校验
if(id==null || id<=0){
return 0;
}
return userService.del(id);
}
service
public int del(Integer id) {
return userMapper.del(id);
}
mapper
int del(Integer id);
xml
<delete id="del">
delete from userinfo where id=#{id}
</delete>
关于参数占位符 #{} 和 ${}
- #{}:预编译处理,相当于JDBC里面替换占位符的操作方式。可以防止SQL注入的问题。
- ${}:字符直接替换,用于SQL关键字的替换,不能防止SQL注入。
${}常用于排序等用于SQL关键字的替换的场景。
<select id="getAll2" resultMap="BaseMap">
<!--使用#{}本身是字符串类型 会报错 -->
select * from userinfo order by id ${order}
</select>