概述
- 是什么
通过程序来操作数据库
是一个数据持久层框架(ORM框架,即对象关系映射),支持自定义SQL,存储过程以及映射(将关系型数据库中的数据与对象建⽴起映射关系,进⽽⾃动的完成数据与对象的互相转换)。
MyBatis通过简单的XML或者注解来配置和映射原始类型。
简单来说,MyBatis就是更简单完成程序和数据库交互的工具,即使程序操作数据库更加简单方便。
- 实现
- 数据库中的表 映射为 程序中的类
- 数据库中的记录(数据) 映射为 程序中的对象
- 数据库中的字段 映射为 程序中的属性
框架会将数据库的每张表都映射成一个Java类,而MyBatis就可以将数据库当做对象和类来进行操做
- ORM
ORM(Object Relational Mapping)即对象关系映射. 其中,对象指的是程序中的对象(Java对象),关系指的是数据库中的关系模型(以二维表结构来表示实体与实体之间的联系),对象关系映射就是在Java对象和数据库的关系模型之间建立一种对应关系。例如数据库中的User表,对应在程序中就是User类,其中User类中的属性和User表的字段是一一对应的。每当你new一个User对象,都会在数据库中对应一个该User的数据。例如下面:
实例
创建SpringBoot项目,在里面添加MyBatis框架
![在这里插入图片描述](https://img-blog.csdnimg.cn/e81cd644f982461d82fb74f8d6484194.png
初始化 链接MySQL
在application.properties 中链接数据库
#设置数据库相关连接配置
spring.datasource.url= jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=z0329
driver-class-name=com.mysql.cj.jdbc.Driver
#mybatis 配置信息 设置mybatis xml 存放路径和命名格式
mybatis.mapper-locations= classpath:mybatis/*Mapper.xml
# 配置 MyBatis 执行时打印 SQL
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.com.example.demo=debug
创建接口
里面用来声明增删改查方法,给其它类调用
创建xml
在mybatis目录里面创建对应的xml文件
xml文件用来配置映射
<!-- 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">
<!-- 注意此处namespace是用来标明当前xml实现或者映射的那个接口的-->
</mapper>
创建所需实体类
@Data
public class Userinfo {
private int id;
private String username;
private String password;
private String photo;
private Date createTime;
private Date updateTime;
//注意此处的属性要和数据库里面一样
}
增删改查操作
select
- 声明方法
- 映射配置文件(在UserMapper.xml中)
<select id="getAll" resultType="com.example.demo.model.Userinfo">
select * from Userinfo
</select>
- 测试(Generate -> Test)
@SpringBootTest //不能省略,告诉当前测试程序,当前项目是运行在Spring Boot 容器中
class UserMapperTest {
@Autowired //注入
private UserMapper userMapper;
@Test
void getAll() {
List<Userinfo> list = userMapper.getAll();
System.out.println(list);
}
}
注:若根据id查找是相似的,例如:
- 声明方法
@Mapper //数据持久层的标志
public interface UserMapper {
Userinfo getUserById(@Param("id") Integer id); //根据id查找
}
- 映射配置文件
<select id="getUserById" resultType="com.example.demo.model.Userinfo">
<!-- 下面花括号里面 id 是和@Param 一致的-->
<!-- @Param的作用就是给参数命名-->
select * from userinfo where id= ${id}
</select>
- 测试
@Test
void getUserById() {
Userinfo userinfo = userMapper.getUserById(1);
System.out.println(userinfo);
}
insert
- 声明方法
@Mapper //数据持久层的标志
public interface UserMapper {
int add(Userinfo userinfo); //添加数据
int insert(Userinfo userinfo); // 添加数据(id自增)
}
- 映射配置文件
<insert id="add">
insert into userinfo(username,password,photo) values(#{username},#{password},#{photo})
</insert>
第二种(设置id自增):
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
<!-- useGeneratedKeys: 是否开启自增-->
<!-- keyColumn: 自增哪个字段 Column(字段)-->
<!-- keyProperty: 将数据库中自增的id赋值给此属性(即后面使用userinfo.getId()方法获取该自增id)-->
insert into userinfo(username,password,photo) values(#{username},#{password},#{photo})
</insert>
- 测试
@Test
void insert() {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("发发");
userinfo.setPassword("123");
userinfo.setPhoto("");
int result = userMapper.insert(userinfo);
System.out.println("受影响行数: "+ result + " ID:" + userinfo.getId());
}
delete
- 声明方法
@Mapper //数据持久层的标志
public interface UserMapper {
int delByID(@Param("id") Integer id); //根据id删除数据
}
-映射配置文件
<delete id="delByID">
delete from userinfo where id=#{id}
</delete>
- 测试
@Test
void delByID() {
int id = 10;
int result = userMapper.delByID(id);
System.out.println("修改行数:" + result);
}
update
- 声明方法
@Mapper //数据持久层的标志
public interface UserMapper {
int update(Userinfo userinfo); //数据更改
}
- 映射配置文件
<update id="update">
update userinfo set username=#{username} where id=#{id}
</update>
- 测试
@Test
void update() {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("小帅");
userinfo.setId(1);
int result = userMapper.update(userinfo);
System.out.println("受影响行数: " + result);
}
动态SQL
概述
上述SQL都是固定死的,在注册页面时,有的数据(字段)是必填的,例如账户名,密码等,而有些字段不是必填的,例如性别,出生年月日等。这种情况下该如何判断用户填了什么字段,而对于程序实现来说,什么字段该有,什么字段该无,这都是灵活的,尽靠上述程序是无法实现的。于是动态SQL就出现了
动态SQl:使用不同标签来对字段有无进行自动判别,保证数据能正确传入到数据库中。换言之,就是对SQl语句中字段的数据或条件进行动态按需去除或添加
if标签
- 声明方法
@Mapper //数据持久层的标志
public interface UserMapper {
int add2(Userinfo userinfo); //if标签
}
- 映射文件配置
<insert id="add2">
insert into userinfo(username,password
<if test="photo != null">
,photo
</if>
) values (#{username},#{password}
<if test="photo != null">
,#{photo}
</if>
)
</insert>
其含义是对photo字段进行动态判别,若用户输入该字段就会将该字段数据添加到数据库中,反之就只会添加username,password字段数据
- 测试
void add2() { //if标签 动态插入数据
Userinfo userinfo = new Userinfo();
userinfo.setUsername("九九");
userinfo.setPassword("456");
//userinfo.setPhoto("www.cn");
int result = userMapper.add2(userinfo);
System.out.println("影响行数: " + result);
}
此时无photo字段,执行如下:
存在后:
这就是动态SQl
trim标签
- 声明方法
@Mapper
public interface UserMapper {
int add3(Userinfo userinfo); //trim标签
}
- 映射文件配置
<!-- 使用trim标签-->
<!-- 其中prefix表示给前面添加,suffix给后面添加,suffixOverrides表示当字段后面有 (,号) 时,会将 (,号) 删除,没有则任何事不做.prefixOverrides是删除字段前面-->
<insert id="add3">
insert into userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
<if test="photo != null">
photo,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
#{username},
</if>
<if test="password != null">
#{password},
</if>
<if test="photo != null">
#{photo},
</if>
</trim>
</insert>
- 测试
@Test
void add3(){ //trim标签 动态插入数据
Userinfo userinfo = new Userinfo();
userinfo.setUsername("小猴");
userinfo.setPassword("777");
//userinfo.setPhoto("www.cn");
int result = userMapper.add3(userinfo);
System.out.println("影响行数: " + result);
}
where标签
- 声明方法
@Mapper
public interface UserMapper {
List<Userinfo> getListByWhere(Userinfo userinfo); //where标签
}
- 映射文件配置
<!-- where标签-->
<!-- 只能用在where查询里面-->
<!-- 当有条件时才会生成where,当没有条件时就不会生成where语句(即就是普通查询)-->
<!-- where标签会自动删除and或者or。其中若不满足第一个if标签,那么直接就会和and拼接,就会出错,此时where就会自动删除and,就不会出错了-->
<select id="getListByWhere" resultType="com.example.demo.model.Userinfo">
select * from userinfo
<where>
<if test="id > 0">
id = #{id}
</if>
<if test="username != null">
or username = #{username}
</if>
<if test="password != null">
and password = #{password}
</if>
</where>
</select>
- 测试
@Test
void getListByWhere() { //where标签 根据id username password 进行条件查询
Userinfo userinfo = new Userinfo();
//userinfo.setId(12);
userinfo.setUsername("小猴");
List<Userinfo> list = userMapper.getListByWhere(userinfo);
System.out.println(list);
}
此时根据username查询:
@Test
void getListByWhere() { //where标签 根据id username password 进行条件查询
Userinfo userinfo = new Userinfo();
//userinfo.setId(12);
userinfo.setUsername("小猴");
userinfo.setPassword("123");
List<Userinfo> list = userMapper.getListByWhere(userinfo);
System.out.println(list);
}
此时根据username和password查询:
set标签
- 声明方法
@Mapper
public interface UserMapper {
int update2(Userinfo userinfo); //set标签
}
- 映射文件配置
<!-- set标签自动检测去除逗号-->
<update id="update2">
update userinfo
<set>
<if test="username != null">
username = #{username},
</if>
<if test="password != null">
password = #{password},
</if>
<if test="photo != null">
photo = #{photo},
</if>
</set>
where id = #{id}
</update>
- 测试
@Test
void update2() { //set标签 根据Id对数据库中信息修改更新
Userinfo userinfo = new Userinfo();
userinfo.setUsername("超级飞侠");
userinfo.setId(1);
int result = userMapper.update2(userinfo);
System.out.println("影响行数: " + result);
}
将SQL语句中password字段和photo字段动态去除
foreach标签
- 声明方法
@Mapper
public interface UserMapper {
int delByIds(List<Integer> ids); //foreach标签(批量删除)
}
- 映射文件配置
<!-- collection: UserMapper里面的值 绑定⽅法参数中的集合-->
<!-- separator:每次遍历之间间隔的字符串-->
<!-- item:遍历时的每⼀个对象-->
<delete id="delByIds">
delete from userinfo where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
- 测试
@Test
void delByIds() { //foreach标签 根据id批量删除
//批量删除id = 7,11,12的用户数据
List<Integer> list = new ArrayList<Integer>();
list.add(7);
list.add(11);
list.add(12);
int result = userMapper.delByIds(list);
System.out.println("收影响行数: " + result);
}
#{}与${}区别
$符: 直接替换(将花括号里面的id直接替换成用户输入的数值) 不安全,存在SQL注入
#号:预执行(将花括号里面的id替换成占位符?号) 安全,不存在SQL注入
SQL注入: 输入一个错误的用户密码或者信息,还能查出用户正确信息
示例
下面是 #{} :
resultMap
是结果映射标签。对实体类进行映射。
使用场景:当数据库中字段名与程序中属性名不一样的话,就可以使用resultMap来配置映射
示例
- 声明方法
@Mapper
public interface UserMapper {
List<Userinfo> getAll2(); //对于resultMap示例
}
- 映射文件配置
<resultMap id="map" type="com.example.demo.model.Userinfo">
<id column="id" property="id"></id>
<result column="username" property="name"></result>
<result column="password" property="password"></result>
</resultMap>
<select id="getAll2" resultMap="com.example.demo.mapper.UserMapper.map">
select * from userinfo
</select>
- 测试
@Test //对resultMap映射 测试
void getAll2() {
List<Userinfo> list = userMapper.getAll2();
System.out.println(list);
}