本篇进行MyBatis学习
文章目录
概念学习
MyBatis查询数据库
MyBatis是什么?
MyBaits是基于JDBC的
MyBatis 是⼀款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis 去除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式Java 对象)为数据库中的记录。
简单来说 MyBatis 是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库工具。
我们可以查看MyBatis的官方文档MyBatis官网
为什么要学习 MyBatis?
对于后端开发来说,程序是由以下两个重要的部分组成的:
- 后端程序
- 数据库
简单来说:JDBC太难,
MyBatis 也是⼀个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射。在面向对象编程语言中,将关系型数据库中的数据与对象建立起映射关系,进而自动的完成数据与对象的互相转换:
- 将输⼊数据(即传⼊对象)+SQL 映射成原生 SQL
- 将结果集映射为返回对象,即输出对象
ORM 把数据库映射为对象:
数据库表(table)–> 类(class)
记录(record,行数据)–> 对象(object)
字段(field) --> 对象的属性(attribute)
⼀般的 ORM 框架,会将数据库模型的每张表都映射为一个 Java 类。
也就是说使用MyBatis 可以像操作对象⼀样来操作数据库中的表,可以实现对象和数据库表之间的转换
MyBatis使用
MyBatis 学习只分为两部分:
- 配置 MyBatis 开发环境;
- 使用 MyBatis 模式和语法操作数据库
配置 MyBatis 开发环境
MyBatis环境搭建:
1.添加MyBatis框架支持
新建项目添加依赖
注意创建好了MyBatis项目之后,启动会报错!!!
2.设置MyBatis配置信息
设置数据库连接的相关信息
数据库连接代码,在配置文件中添加
# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
MyBatis xml保存路径和xml的命名格式
配置 mybatis xml 的文件路径,创建所有表的 xml 文件,在配置文件中添加
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml//存储mabits的目录和文件名
MyBatis模式开发
由两部分组成:
- interface:让其他层可以注入使用的接口
- mybaitis:xml ->具体实现sql【它是上面interface的“实现”】
首先在resources中添加mybatis(设置的文件)中的xml文件
然后创建实体类
在userMapper中进行接口声明
@Mapper//注意添加此注解
public interface UserMapper {
List<UserEntity> getAll();
}
在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">//这里是对应的Mapper类,实现的接口(包名+接口名)
<select id="getAll" resultType="com.example.demo.entity.UserEntity">//mapper里面的方法和返回类型,id与接口方法名相同
select * from userinfo//sql语句
</select>
</mapper>
在服务层(servlce),将映射进行服务层
代码:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<UserEntity> getAll(){
return userMapper.getAll();
}
}
服务层进入控制层
RequestMapping("/user")
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/getall")
public List<UserEntity> getAll(){
return userService.getAll();
}
}
以上为基础开发
根据id查找对象
//根据id查询用户对象
UserEntity getUserById(@Param("id") Integer id);
此处使用注解@Param,代表了传递过来的内容被重命名,在数据库查询时,需要属于@Param修改的名字
Mabatis获取动态参数
**1、${paramName},**直接替换,可以在日志中进行查看
首先添加配置文件
#打印MyBatis执行SQL
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.com.example.demo=debug
日志将打印出所执行的sql语句,系统默认的日志打印时info,这里进行配置改为debug级别才能进行查看。
select * from userinfo where id=${id}
通过日志可以看到是直接替换
2、#{paramName} 占位符
select * from userinfo where id=#{id}
使用占位符可以有效避免sql注入的问题
直接替换和使用占位符的区别
直接替换在处理非数值类型的查询时,会出现错误
占位符预处理,则不会
使用占位符可以有效避免sql注入的问题,但在使用sql关键字处理时就会出现问题,这个时候需要使用直接替换
使⽤ ${sort} 可以实现排序查询,⽽使⽤ #{sort} 就不能实现排序查询了,因为当使⽤ #{sort} 查询时,
如果传递的值为 String 则会加单引号,就会导致 sql 错误。
单表的增删改
改操作
interface代码
int updatePassword(@Param("id")Integer id,
@Param("password") String password,
@Param("newPassword") String newPassword);
xml代码
<update id="updatePassword">
update userinfo set password=#{newPassword}
where id=#{id} and password=#{password}
</update>
删除操作
对于删除事务
interface代码
@Transactional
@Test
void delById() {
int id = 1;
int result = userMapper.delById(id);
System.out.println("删除"+result);
}
xml代码
<delete id="delById">
delete from userinfo where id=#{id}
</delete>
添加操作
interface代码
//添加用户
int addUser(UserEntity user);
//添加用户方法2
int addUserGetId(UserEntity user);
xml代码:
<insert id="addUser"><!--添加返回受影响的行数-->
insert into userinfo(username,password) values(#{username},#{password})
</insert>
<insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id"><!--是否自动生成id,返回影响行数和id-->
insert into userinfo(username,password) values(#{username},#{password})
</insert>
like查询
模糊查询
interface代码:
//根据名称模糊查询
List<UserEntity> getListByName(@Param("username") String username);
xml代码(使用concat)
<!--模糊查询-->
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username like concat('%',#{username},'%')<!--模糊查询,查询任意字符,使用字符串拼接-->
</select>
返回类型的区别resultType和resultMap
返回字典映射
resultMap 使用场景:
字段名称和程序中的属性名不同的情况,可使用resultMap 配置映射;
一对一和一对多关系可以使用 resultMap 映射并查询数据。
如出现字段名与属性名不相同的情况
<!--resultMap的使用-->
<resultMap id="BaseMap" type="com.example.demo.entity.UserEntity">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="createtime" column="createtime"></result>
</resultMap>
<select id="getListByName" resultMap="BaseMap">
select * from userinfo where username like concat('%',#{username},'%')<!--使用字符串拼接-->
</select>
多表操作
多表查询
第一种方式:
mapper代码:
xml代码
值得注意的是由于lombok的使用,在继承了别的类的类中添加lombok的注解如@Data,并不会让父类的属性进行tostring操作,所有并不会显示父类信息,所以要自己tostring类
勾选父类同样tostring
一个查询多个表并打印:
mapper代码
List<ArticleInfoVO> getListByUid(@Param("uid") Integer uid);
xml代码:
<!--根据uid查找作者-->
<select id="getListByUid" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select a.*,u.username from articleinfo a
left join userinfo u on u.id=a.uid
where a.uid=#{uid}
</select>
单元测试代码:
@Test
void getListByUid() {
Integer uid = 1;
List<ArticleInfoVO> list = articleMapper.getListByUid(uid);
/*for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}*/
list.stream().parallel().forEach(System.out::println);//parallel()并行执行
}
动态sql的使用
动态 sql 是Mybatis的强大特性之⼀,能够完成不同条件下不同的 sql 拼接
标签
必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传⼊,程序应该如何实现呢?
如果用户设置值就会用用户设置的值,如果没有就系统默认的值,保证数据的一致性
使用:
进行判断
<insert id="addUserGetId2">
insert into userinfo(username,password
<if test="photo!=null">//test判断的是key的属性(不是数据库中的字段)
,photo//数据库的信息
</if>
)values(#{username},#{password}
<if test="photo!=null">
,#{photo}//类里面的属性
</if>
)
</insert>
标签
当如果所有字段都是⾮必填项,就考虑使用标签结合标签,对多个字段都采取动态生成的方式。
标签中有如下属性:
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀
MyBatis多个参数都是非必传参数的解决方案
第一种方法:一加一方案
xml文件:
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
where 1=1
<trim prefixOverrides="and">
<if test="id!=null and id>0">
and id=#{id}
</if>
<if test="title!=null and title!=' '">
and title like concat ('%' ,#{title},'%')
</if>
</trim>
</select>
第二种方式:trim方式,
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
<trim prefix="where" suffixOverrides="and">
<if test="id!=null and id>0">
id=#{id} and
</if>
<if test="title!=null and title!=' '">
title like concat ('%' ,#{title},'%')
</if>
</trim>
</select>
当trim中生成了代码,那么才会添加< trim >里面的前缀和后缀,如果trim中没有生成代码,那么前缀和后缀就会省略(不生成)
第三种方式:< where >标签
where只能去掉前缀and关键字,并不能帮你去除后面的and关键字
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
<where>
<if test = "id!=null and id>0">
id=#{id}
</if>
<if test="title!=null and title!=''">
and title like concat('%',#{title},'%')
</if>
</where>
</select>
以上标签也可以使⽤ <trim prefix="where" prefixOverrides="and">
< set >标签
根据传入的用户对象属性来更新用户数据,可以使用< set >标签来指定动态内容。
UserMapper 接口中修改用户方法:根据传入的用户id 属性,修改其他不为 null 的属性:
<update id="updateById" parameterType="org.example.model.User">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
</set>
where id=#{id}
</update>
以上< set >标签也可以使⽤ <trim prefix="set" suffixOverrides=",">
替换。
< foreach >标签
对集合机械能遍历时可以使用该标签。标签有如下属性:
collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
item:遍历时的每⼀个对象
open:语句块开头的字符串
close:语句块结束的字符串
separator:每次遍历之间间隔的字符串
示例:根据多个文章 id 来删除文章数据。
ArticleMapper 中新增接口方法:
批量删除操作
<delete id="delById">
delete from articleinfo
where id in
<foreach collection="idList" item="aid" open="(" close=")" separator=",">
#{aid}
</foreach>
</delete>