目录
- MyBatis
- 创建MyBatis环境搭建
- MyBatis模式开发
- MyBatis 获取动态参数(查询操作)
- ${} 直接替换
- #{} 占位符模式替换
- like查询(模糊查询)
- 多表查询
- 一对一的表映射
- 一对多的表映射
- 增、删、改操作
- 改操作
- 删除操作
- 增加操作
- 添加用户
- 添加用户并获取用户id
- 返回类型与返回字典映射
- 返回类型 resultType
- 返回字典映射 resultMap
- 动态SQL
- \<if\>标签
- \<trim\>标签
- \<where\>标签
- \<set\>标签
- \<foreach\>标签
MyBatis是优秀的持久层框架,支持自定义SQL,存储过程以及高级映射。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
使用MyBatis是更简单完成程序和数据库交互的工具,可以更简单的操作和读取数据库工具。
MyBatis官网
MyBatis
MyBatis是基于JDBC的,在JDBC上又封装了一层
创建MyBatis环境搭建
和maven项目不同的两步:
- 添加MyBatis框架支持 (老项目 / 新项目)
- 设置MyBatis的配置信息
- 设置数据库链接的相关信息
- 配置 MyBatis xml 的保存路径和 xml 命名模式
# 数据库的连接字符串设置
spring.datasource.url=jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=111
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 设置MyBatis
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
添加MyBatis框架支持
注意:第一次创建好了MyBatis项目之后,启动会报错。
需要在application.properties下设置数据库的连接字符串设置和 MyBatis 的 XML ⽂件配置。
# 数据库的连接字符串设置
spring.datasource.url=jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=111
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 设置MyBatis
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
MyBatis模式开发
MyBatis模式开发由两部分组成
- interface :让其他层可以注入使用的接口
- MyBatis xml:具体实现sql【他是上面interface的实现】
MyBatis 获取动态参数(查询操作)
${} 直接替换
MyBatis 获取动态参数有两种实现:
- ${paramName} 直接替换
<select id="getUserById" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where id=${uid}
<!-- 如果param和形参中的值不一样,需要使用@Param中的参数 -->
</select>
#{} 占位符模式替换
- #{paramName} 占位符模式
是一种预执行,可以有效的排除SQL的安全问题
<select id="getUserById" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where id=#{uid}
</select>
@Test
void login() {
String username = "admin";
String password = "' or 1='1";
UserEntity inputUser = new UserEntity();
inputUser.setUsername(username);
inputUser.setPassword(password);
UserEntity user = userMapper.login(inputUser);
System.out.println(user);
}
<select id="login" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username='${username}' and password='${password}'
</select>
没有输入密码,但是可以构造,得到用户数据
$:直接替换
只要or前后有一个条件满足即可
select * from userinfo where username='${username}' and password='' or 1='1'
#是预执行处理,填充的东西只看做一个字符串,不会查到用户数据
<select id="login" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username=#{username} and password=#{password}
</select>
$:直接替换,当需要传递一个SQL关键字的时候比较适合使用
比如需要对数据进行倒序或者正序排序时
//UserMapper.java
List<UserEntity> getAllByIdOrder(@Param("ord") String ord);
//UserMapper.xml
<select id="getAllByIdOrder" resultType="com.example.demo.entity.UserEntity">
select * from userinfo order by id ${ord}
</select>
//UserMapperTest
@Test
void getAllByIdOrder() {
List<UserEntity> list = userMapper.getAllByIdOrder("desc");
System.out.println(list.size());
}
like查询(模糊查询)
//根据用户名模糊查询
List<UserEntity> getListByName(@Param("username") String username);
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username like concat('%', #{username},'%')
</select>
@Test
void getListByName() {
String username = "oo";
List<UserEntity> list = userMapper.getListByName(username);
list.stream().forEach(System.out::println);
}
多表查询
一对一的表映射
(企业中几乎不用)
一对多的表映射
增、删、改操作
改操作
修改密码
//UserMapper.java
//修改密码
int updatePassword(@Param("id")Integer id,
@Param("password") String password,
@Param("newPassword") String newPassword);
<!-- UserMapper.xml -->
<!-- select 要求必须设置两个属性,update设置一个参数就行,update默认会返回一个受影响的行数,返回类型默认是int -->
<update id="updatePassword">
update userinfo set password=#{newPassword}
where id=#{id} and password=#{password}
</update>
在UserMapper.java下右键生成updatePassword的Test
//UserMapperTest.java
@Transactional //事务(执行前新建事务,执行完进行回滚) 该注解让操作不污染数据库,可以加在类上,也可以加在方法上。
@Test
void updatePassword() {
int result = userMapper.updatePassword(1, "123456", "aaaaa");
System.out.println("修改:" + result);
}
删除操作
//UserMapper.java
//删除用户
int delById(@Param("id") Integer id);
<!-- UserMapper.xml -->
<!-- 删除 -->
<delete id="delById">
delete from userinfo where id=#{id}
</delete>
//UserMapperTest.java
@Transactional
@Test
void delById() {
int id = 1;
int result = userMapper.delById(id);
System.out.println("删除结果:" + result);
}
增加操作
添加用户
//UserMapper.java
//添加操作
int addUser(UserEntity user);
<!-- UserMapper.xml -->
<!-- 添加 返回影响行数-->
<insert id="addUser">
insert into userinfo(username, password) values(#{username}, #{password})
</insert>
//UserMapperTest.java
@Test
void addUser() {
UserEntity user = new UserEntity();
user.setUsername("root");
user.setPassword("123456");
int result = userMapper.addUser(user);
System.out.println("添加用户" + result);
}
添加用户并获取用户id
//UserMapper.java
int addUserGetId(UserEntity user);
<!-- UserMapper.xml -->
<!-- 数据库中要给id设置自增主键,不然这些会报错 -->
<!-- useGeneratedKeys="true" 表示是否自动生成id(主键) 默认为false -->
<!-- 开启自动生成主键后,keyProperty表示将生成的主键赋值给哪个属性字段上,放在id字段 -->
<!-- 添加 返回影响行数 和 id-->
<insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username, password) values(#{username}, #{password})
</insert>
//UserMapperTest.java
@Test
void addUserGetId() {
UserEntity user = new UserEntity();
user.setUsername("root");
user.setPassword("123456");
int result = userMapper.addUserGetId(user);
System.out.println("添加结果:" + result);
System.out.println("ID:" + user.getId());
}
返回类型与返回字典映射
返回类型 resultType
大多数的场景都可以使用resultType进行返回。使用方便,直接定义实体类即可
返回字典映射 resultMap
resultMap的使用场景
- 字段名称和程序中的属性名不同,使用resultMap配置映射
- 一对一和一对多关系可以使用resultMap映射并查询数据
动态SQL
动态SQL是MyBatis的强大特性之一,能够描述不同条件下不同SQL的拼接。允许在XML中写逻辑判断
保证了数据的一致性
<if>标签
有时候有必填字段和⾮必填字段,如果在添加的时候有不确定的字段传⼊,需要使用动态标签 <if> 判断
int addUser2(UserEntity user);
<insert id="addUser2">
insert into userinfo(username, password
<if test="photo != null and photo !=''"> <!-- 这里test里面判断的key是属性,不是数据库字段 -->
,photo <!-- 数据库字段 -->
</if>
) values(#{username}, #{pwd}
<if test="photo != null and photo !=''">
,#{photo} <!-- 这里是属性,不是数据库字段 (特殊字符 #{} 等,里面的都属于程序里面的) -->
</if>
)
</insert>
@Transactional
@Test
void addUser2() {
String username = "ss";
String password = "1008611";
String photo = "this is a photo";
UserEntity user = new UserEntity();
user.setUsername(username);
user.setPwd(password);
user.setPhoto(photo);
int result = userMapper.addUser2(user);
System.out.println("添加:" + result);
}
<trim>标签
如果所有的字段都是非必填的情况下,只使用if标签就无法解决问题了。
就要使用<trim>
标签结合<if>
标签,对多个字段都采取动态生成的方式。
<trim>
标签属性:
- prefix:整个语句块,以prefix的值为前缀
- suffix:整个语句块,以suffix的值作为后缀
- prefixOverrides:表示整个语句块要去除的前缀,有则去除,没有也不会报错
- suffixOverrides:表示整个语句块要去除的后缀
<trim>
标签的处理方法:
- 基于
prefix
配置,在开始部分加上(
- 基于
suffix
配置,在结束部分加)
- 在多个
<if>
组织的语句后都以,
结尾,在最后拼接好的字符串还会以,
结尾,会基于suffixOverrides配置去掉最后一个,
<if test="createTime!=null">
中的 createTime 是传入对象的属性
使用 trim 什么都不传的话会报错
select * from articleinfo
where
<trim 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>
当MyBatis中多个都是非必传参数的解决方案:
① 1=1
<!--除非在最前面加上 1=1 -->
<!-- 这样是可行的 -->
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>
② 在trim中使用where作为前缀和and作为整个语句块要去除的后缀
当trim中生成了代码,才会添加<trim>
中的前缀和后缀,如果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>
③ where标签
<where>标签
where标签是专门用来解决多个都是非必传参数的。
<where>
会自动帮你取出最前面的 and 关键字,但要注意,使用where标签不会自动帮你去除最后的and标签。
where 会自动检测 <where>
内是否有数据传入,如果没有就不会生成where语句
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>
<set>标签
<set>
会自动取出最后面的关键字。
<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>
<if test="sex != null">
sex=#{sex},
</if>
</set>
where id=#{id}
</update>
<foreach>标签
对集合进行遍历
常用属性:
- collection:传递过来的集合的名称,List,set,map或数组
- item:集合中遍历的每一个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的分隔符
使用foreach 标签最常见的使用:批量删除