目录
MyBatis 环境搭建
1.添加 Mybatis 框架支持
2.设置 MyBatis 配置信息
2.1.设置数据库连接的相关信息
2.2 Mybatis xml 保存路径和 xml命名格式
编辑 MyBatis 模式开发
Mybatis xml 模板
查询表内容
单元测试
以根据id,查询用户对象这个方法为例
获取动态参数的俩种方式 ${param} #{param}
${param}
#{param}
各种MyBatis单表操作
如果传递的参数为一个对象,如何获取对象里面的属性?
用户的修改
删除用户
添加用户
1.返回值为受影响行数
编辑2.返回受影响的行数和id
根据用户名模糊查询
返回字典映射 resultMap
什么是MyBatis?:更加简单的帮助程序实现数据库操作的工具
MyBatis 环境搭建
1.添加 Mybatis 框架支持
MyBatis项目不仅要添加MyBatis Framework的依赖,还要添加添加MySql Driver依赖(用于指定连接数据库的类型,这里我们使用MySql数据库,所以需要引入MySql Driver依赖,如果是别的类型数据库类型同理,比如使用MariaDB数据库,此处就可以引用MariaDB Driver依赖)
2.设置 MyBatis 配置信息
2.1.设置数据库连接的相关信息
在配置文件中配置如下信息
#设置数据库连接信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
usename: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
2.2 Mybatis xml 保存路径和 xml命名格式
mybatis:
mapper-locations: classpath:/mybatis/*Mapper.xml
这里交代了Mybatis xml的保存路径,在本地一个叫mybatis包底下,后缀为Mapper.xml的文件
MyBatis 模式开发
1. interface: 让其他层可以注入使用的接口
2.mybatis: xml 具体实现sql(它是上述interface的实现)
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">
<mapper namespace="com.example.demo.mapper.UserMapper">
</mapper>
注意这里的namespace表示当前xml实现的类是哪个类,这里就表示当前xml实现的类是UserMapper这个类
查询表内容
以实现查询操作为例,现在有一个mycnblog数据库,里面有一张用户表(userinfo)
表里的内容如下
.
这张表映射到Java中的UserEntity的实体类如下
@Data
public class UserEntity {
private Integer id;
private String username;
private String password;
private String photo;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private Integer state;
}
首先实现实现一个MyBatis的接口
@Mapper//表示这是一个Mybatis接口,需要和xml对应起来
public interface UserMapper {
List<UserEntity> getAll();
}
实现这个接口对应的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.entity.UserEntity">
select * from userinfo
</select>
</mapper>
此处我们需要进行查询操作,所以需要使用select标签,同理如果是添加操作.需要使用insert标签
此处的id,是我们要实现的这个类里面的某个方法的方法名
此处的resultType是我们要返回的数据类型
使用service层调用该接口
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<UserEntity> getAll(){
return userMapper.getAll();
}
}
使用controller调用service层的接口
package com.example.demo.controller;
import com.example.demo.entity.UserEntity;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("getAll")
public List<UserEntity> getAll(){
return userService.getAll();
}
}
我们此时在网页中访问这个getAll方法
上述测试getAll方法的过程比较繁琐,需要调用controller层,service层.且如果测试的是修改,添加等方法也会污染数据库.所以接下来我们使用单元测试的方法
单元测试
单元测试优点
1.可以非常简单,直观,快速的测试某一个功能是否正确
2.使用单元测试可以帮助我们在快速打包时发现一些问题,在打包之前,所有的单元测试必须通过,否则不能打包成功
3.使用单元测试在测试功能的时候,不污染连接的数据库,可以不对数据库进行任何改变的情况下,测试功能
4.可以跳过系统限制(登录校验,权限检验),直接测试想要测试的方法
SpringBoot项目会默认自带一个单元测试的框架(由JUnit实现),咱们直接用就好了
以根据id,查询用户对象这个方法为例
同样实现一个mapper接口
@Mapper//表示这是一个Mybatis接口,需要和xml对应起来
public interface UserMapper {
List<UserEntity> getAll();
//根据id 查询用户对象
UserEntity getUserById(@Param("id")Integer id);
}
这里的@Param("id"),意思是给这个叫做id的参数,重新起了个叫id的名字,之后在xml中,就以Param里面的名字为准
实现这个接口对应的xml
<select id="getUserById" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where id = ${id}
</select>
这里 id = ${id} 左边的"="号左边的id是数据库userinfo表里面的叫id的字段,右边的id就是我们之前在mapper接口传入的参数id,这里使用${}的方式是为了获取到属性中的id的值,如果直接写id=id,那么左边的id拿到的就是id这个字符串,而不是传入给id的值.
在我们想要测试的方法上右键生成
点击Test,勾中我们想要测试的方法
会在test底下和main一样的包路径下,生成一个Test的类,里面有我们要测试的方法
此时我们需要在这个自动生成的类 添加一些注解@SpringBootTest
@SpringBootTest // 表示当前单元测试的类是运行在 SpringBoot 环境中的
class UserMapperTest {
@Autowired
private UserMapper userMapper; //把我们需要测试的方法的类注入过来
@Test
void getUserById() {
UserEntity user = userMapper.getUserById(1);
System.out.println(user);
}
}
点击此处运行该方法
在控制台有我们输出的user打印信息,测试成功
获取动态参数的俩种方式 ${param} #{param}
${param}
把${param}直接替换成传过来的参数,所见即所得,比如如果我们需要的参数是字符类型,这种获取参数的方式默认是没有字符需要的引号的.且这种方式会出现sql注入的问题,容易出现安全性问题,适用于我们需要传递一些sql指令的场景(如传递排序方式),在使用时,我们需要保证我们输入的值可以被枚举(保证不被sql注入).
#{param}
把#{param}用一个占位符代替,再把参数传给这个占位符,是预处理
各种MyBatis单表操作
如果传递的参数为一个对象,如何获取对象里面的属性?
例如实现一个登录的方法
mapper接口
@Mapper//表示这是一个Mybatis接口,需要和xml对应起来
public interface UserMapper {
//根据名称查询用户对象
UserEntity login(UserEntity user);
}
xml
<select id="login" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username = #{username} and password = #{password}
</select>
这里我们没有使用user.getUserName的方式获取username,而是直接输入username,password原因是MyBatis已经帮我们映射好了.
进行单元测试
@Test
void getUserByName() {
UserEntity userLogin = new UserEntity();
userLogin.setUsername("admin");
userLogin.setPassword("admin");
UserEntity user = userMapper.login(userLogin);
System.out.println(user);
}
用户的修改
修改用户密码
mapper接口
//修改密码 返回值为受影响的行数
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>
update标签设置一个参数就可以了,不需要设置返回值,当使用upadate标签时,默认会返回一个受影响的行数,不需要再设置了
单元测试
@Transactional // 事务 加了此注解单元测试就不会污染数据库了
@Test
void updatePassword() {
int result = userMapper.updatePassword(1,"admin","123456");
UserEntity user = userMapper.getUserById(1);
System.out.println("修改 "+result);
System.out.println(user);
}
删除用户
mapper接口
//删除用户
int delById(@Param("id") Integer id);
xml
<delete id="delById">
delete from userinfo where id = #{id}
</delete>
使用delete标签来进行删除操作,和修改操作一样,delete标签不需要设置返回值,默认返回值是int,受影响的行数
单元测试
@Transactional
@Test
void delById() {
int result = userMapper.delById(1);
UserEntity user = userMapper.getUserById(1);
System.out.println("删除 "+result);
System.out.println(user);
}
添加用户
1.返回值为受影响行数
mapper接口
//添加用户
int addUser(UserEntity user);
xml
<insert id="addUser">
insert into userinfo(username,password) values(#{username},#{password})
</insert>
同样,当我们返回值是受影响行数时,不需要设置返回值
单元测试
@Test
void addUser() {
UserEntity user = new UserEntity();
user.setUsername("白杨");
user.setPassword("123456");
int result = userMapper.addUser(user);
UserEntity user2 = userMapper.getUserByName("白杨");
System.out.println(result);
System.out.println(user2);
}
2.返回受影响的行数和id
mapper接口
int addUserGetId(UserEntity user);
xml
<insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username,password) values(#{username},#{password})
</insert>
useGeneratedKeys为是否要拿到数据库自增的id,keyProperty = id 为把刚刚拿到的id赋值给userinfo对象中的id属性
单元测试
@Test
void addUserGetId() {
UserEntity user = new UserEntity();
user.setUsername("杰尼龟");
user.setPassword("123456");
int result = userMapper.addUserGetId(user);
UserEntity user2 = userMapper.getUserByName("杰尼龟");
System.out.println(result);
System.out.println(user2);
System.out.println(user.getId());
}
注意在单元测试代码中,我们并没有给user的id属性赋值,而执行流addUserGetId方法后,我们却能拿到Id,这就是 useGeneratedKeys和keyProperty的作用了
根据用户名模糊查询
mapper接口
//根据用户名模糊查询
List<UserEntity> getListByName(@Param("username")String username);
xml
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username like concat('%','#{username}','%')
</select>
使用concat字符串拼接,如果写成'%#{username}%',此时如果传入的username为"白",在数据库里面看到的会是:'%'白'%',占位符会自动添加双引号,所以我们这里用字符串拼接的方式
单元测试
@Test
void getListByName() {
String username = "白";
List<UserEntity> list = userMapper.getListByName(username);
list.stream().forEach(System.out::println);
}
返回字典映射 resultMap
使用场景:字段名称和程序中的属性名不同,可以使用 resultMap 配置映射
一对一和一对多关系可以使用 resultMap 映射并查询数据库
例如当我们程序中UserEntity这个类映射数据库userinfo这张表时,UserEntity的属性名为pwd,而userinfo的字段名为password
@Data
public class UserEntity {
private Integer id;
private String username;
private String pwd;
private String photo;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private Integer state;
}
userinfo表内容
同样以根据用户名模糊查询这个方法为例
mapper接口
//根据用户名模糊查询
List<UserEntity> getListByName(@Param("username")String username);
xml
<resultMap id="BaseMap" type="com.example.demo.entity.UserEntity">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="pwd" column="password"></result>
</resultMap>
<select id="getListByName" resultMap="BaseMap">
select * from userinfo where username like concat('%',#{username},'%')
</select>
id="BaseMap"表示给当前resultMap字典起了个叫BaseMap的名字,type表示当前这个resultMap这个字典,用来映射哪个实体类
<id></id>这里面放的是主键 property = "id",column = "id"意思是程序中的属性id对应数据库中的id字段
<result></result>放的是普通的字段 property = "pwd" column = "password"意思是程序中的属性 pwd,对应数据库中的password字段
我们想要映射几个属性和字段,就需要写几个
单元测试
@Test
void getListByName() {
String username = "白";
List<UserEntity> list = userMapper.getListByName(username);
list.stream().forEach(System.out::println);
}
当然上述方法可能有点繁琐,我们也可以通过调整sql语句实现这种功能(通过as重命名)
xml:
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select id,username,password as pwd from userinfo where username like concat('%',#{username},'%')
</select>