欢迎关注个人主页:逸狼
创造不易,可以点点赞吗
如有错误,欢迎指出~
目录
前⾔
在应⽤分层学习时,我们了解到web应⽤程序⼀般分为三层,即:Controller、Service、Dao. 之前的案例中,请求流程如下:浏览器发起请求,先请求Controller,Controller接收到请求之后,调⽤ Service进⾏业务逻辑处理,Service再调⽤Dao,但是Dao层的数据是Mock的,真实的数据应该从数据库 中读取
对于JDBC来说,整个操作⾮常的繁琐,我们不但要拼接每⼀个参 数,⽽且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关闭连接 等,⽽所有的这些操作步骤都需要在每个⽅法中重复书写.那有没有⼀种⽅法,可以更简单、更⽅便的 操作数据库呢?
什么是MyBatis?
• MyBatis是⼀款优秀的持久层框架,⽤于简化JDBC的开发。
MyBatis本是Apache的⼀个开源项⽬iBatis,2010年这个项⽬由apache迁移到了googlecode,并 且改名为MyBatis。2013年11⽉迁移到Github.
官⽹:MyBatis中⽂⽹
在上⾯我们提到⼀个词:持久层 • 持久层:指的就是持久化操作的层,通常指数据访问层(dao),是⽤来操作数据库的.
简单来说MyBatis是更简单完成程序和数据库交互的框架,也就是更简单的操作和读取数据库⼯具 接下来,我们就通过⼀个⼊⻔程序,让⼤家感受⼀下通过Mybatis如何来操作数据库
数据准备
创建⽤⼾表,并创建对应的实体类UserInfo
先在MySQL中或者navicat中创建一个 名为mybatis_test的mysql数据库.
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
4-- 使⽤数据数据
USE mybatis_test;
-- 创建表[⽤⼾表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`username` VARCHAR ( 127 ) NOT NULL,
`password` VARCHAR ( 127 ) NOT NULL,
`age` TINYINT ( 4 ) NOT NULL,
`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-⼥ 0-默认',
`phone` VARCHAR ( 15 ) DEFAULT NULL,
`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
-- 添加⽤⼾信息
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );
创建对应的实体类UserInfo
实体类的属性名与表中的字段名⼀⼀对应
package com.example.demo.model;
import lombok.Data;
import java.util.Date;
@Data
//创建mybatis_test数据库对应的类
public class UserInfo {
private Integer id;
private String username;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
配置properties文件
配置数据库连接字符串 Mybatis中要连接数据库,需要数据库相关参数配置 • MySQL驱动类 • 登录名 • 密码 • 数据库连接字符串
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=false
#连接数据库的⽤⼾名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password="111111"
注意事项: 如果使⽤MySQL是5.x之前的使⽤的是"com.mysql.jdbc.Driver",如果是⼤于5.x使⽤的 是“com.mysql.cj.jdbc.Driver”.
持久层代码Mapper
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper//将对象交给spring管理
public interface UserInfoMapper {
//查询所有用户信息
@Select("select * from user_info")
List<UserInfo> selectAll();
}
Mybatis的持久层接⼝规范⼀般都叫XxxMapper @Mapper注解:表⽰是MyBatis中的Mapper接⼝ • 程序运⾏时,框架会⾃动⽣成接⼝的实现类对象(代理对象),并给交Spring的IOC容器管理 • @Select注解:代表的就是select查询,也就是注解对应⽅法的具体实现内容.
测试调用
在创建出来的SpringBoot⼯程中,在src下的test⽬录下,已经⾃动帮我们创建好了测试类,我们可以 直接使⽤这个测试类来进⾏测试.
package com.example.demo;
import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class Demo6ApplicationTests {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void contextLoads() {
// System.out.println(userInfoMapper.selectAll().toString());//调用查询方法
//使用lambda表达式
userInfoMapper.selectAll().forEach(x -> System.out.println(x));
//List<UserInfo> userInfos = userInfoMapper.selectAll();
//userInfos.forEach(x -> System.out.println(x));
// List<UserInfo> userInfos = userInfoMapper.selectAll();
// for(UserInfo userInfo : userInfos){
// System.out.println(userInfo.toString());
// }
}
}
测试类上添加了注解@SpringBootTest,该测试类在运⾏时,就会⾃动加载Spring的运⾏环境. 我们通过@Autowired这个注解,注⼊我们要测试的类,就可以开始进⾏测试了
手动生成测试类
使⽤Idea⾃动⽣成测试类 除此之外,也可以使⽤Idea⾃动⽣成测试类 1. 在需要测试的Mapper接⼝中,右键->Generate->Test
package com.example.demo.mapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class UserInfoMapperTest {
@BeforeEach
void setUp() {
System.out.println("BeforeEach");
}
@AfterEach
void tearDown() {
System.out.println("AfterEach");
}
@Test
void selectAll() {
System.out.println("selectAll111");
}
@Test
void SelectAll222() {
System.out.println("SelectAll222");
}
}
将查询内容输出到浏览器里
UserController类
package com.example.demo.Controller;
import com.example.demo.Service.UserService;
import com.example.demo.model.UserInfo;
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;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/v1")
public List<UserInfo> v1(){
return userService.selectUserList();
}
}
UserSevice类
package com.example.demo.Service;
import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
public List<UserInfo> selectUserList() {
return userInfoMapper.selectAll();
}
}
MyBatis的基础操作
上⾯我们学习了Mybatis的查询操作,接下来我们学习MyBatis的增,删,改操作
在学习这些操作之前,我们先来学习MyBatis⽇志打印
打印⽇志
在Mybatis当中我们可以借助⽇志,查看到sql语句的执⾏、执⾏传递的参数以及执⾏结果
在配置⽂件中进⾏配置
#指定mybatis输出⽇志的位置, 输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
重新运行测试
①:查询语句 ②:传递参数及类型 ③:SQL执⾏结果
参数传递
需求:查找id=4的⽤⼾,对应的SQL就是:select*fromuser_infowhereid=4
@Select("select * from user_info where id = 4;")
List<UserInfo> selectById();
但是这样就写死了,所以SQL语句中的id值不能写成固定数值,需要变为动态的数值 解决⽅案:在queryById⽅法中添加⼀个参数(id),将⽅法中的参数,传给SQL语句 使⽤#{} 的⽅式获取⽅法中的参数
修改后
@Select("select * from user_info where id = #{id};")
List<UserInfo> selectById(Integer id);
如果mapper接⼝⽅法形参只有⼀个普通类型的参数,#{…}⾥⾯的属性名可以随便写,如:#{id}、# {value}。建议和参数名保持⼀致
@Test
void selectById(){
userInfoMapper.selectById(3).forEach(x -> System.out.println(x));
}
也可以通过 @Param ,设置参数的别名,如果使⽤ @Param 设置别名,#{...}⾥⾯的属性名必须和 @Param 设置的⼀样
@Select("select username, `password`, age, gender, phone from user_info where
id= #{userid} ")
UserInfo queryById(@Param("userid") Integer id);
MyBatis会根据⽅法的返回结果进⾏赋值.
- ⽅法⽤对象UserInfo接收返回结果,MySQL查询出来数据为⼀条,就会⾃动赋值给对象.
- ⽅法⽤List接收返回结果,MySQL查询出来数据为⼀条或多条时,也会⾃动赋值给List. 但如果MySQL查询返回多条,但是⽅法使⽤UserInfo接收,MyBatis执⾏就会报错.
增(insert)
接口方法
@Insert("insert into user_info (username, `password`, age, gender, phone) values(#{username}, #{password}, #{age}, #{gender}, #{phone})")
Integer insert(UserInfo userInfo);
测试
@Test
void insert(){
UserInfo userInfo = new UserInfo();
userInfo.setUsername("zhao");
userInfo.setPassword("111");
userInfo.setAge(12);
userInfo.setGender(1);
userInfo.setPhone("1008611");
userInfoMapper.insert(userInfo);
}
如果设置了 @Param 属性,#{...}需要使⽤参数.属性来获取
@Insert("insert into user_info (username, `password`, age, gender, phone)
values (#{userInfo.username},#{userInfo.password},#{userInfo.age},#
{userInfo.gender},#{userInfo.phone})")
Integer insert(@Param("userInfo") UserInfo userInfo);
如果直接写属性,会有下面报错
返回主键
Insert语句默认返回的是受影响的⾏数 但有些情况下,数据插⼊之后,还需要有后续的关联操作,需要获取到新插⼊数据的id ⽐如订单系统 当我们下完订单之后,需要通知物流系统,库存系统,结算系统等,这时候就需要拿到订单ID 如果想要拿到⾃增id,需要在Mapper接⼝的⽅法上添加⼀个Options的注解
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info (username, `password`, age, gender, phone) values(#{username}, #{password}, #{age}, #{gender}, #{phone})")
Integer insert(@Param("userInfo") UserInfo userInfo);
- useGeneratedKeys:这会令MyBatis使⽤JDBC的getGeneratedKeys⽅法来取出由数据库内 部⽣成的主键(⽐如:像MySQL和SQLServer这样的关系型数据库管理系统的⾃动递增字 段),默认值:false
- keyProperty:指定能够唯⼀识别对象的属性,MyBatis会使⽤getGeneratedKeys的返回值或 insert语句的selectKey⼦元素设置它的值,默认值:未设置(unset)
测试
//拿到自增id
@Test
void testInsert() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("zhaolll");
userInfo.setPassword("111");
userInfo.setAge(12);
userInfo.setGender(1);
userInfo.setPhone("1008611");
Integer count = userInfoMapper.insert(userInfo);
System.out.println("增加的数据条数: " + count + " id: " + userInfo.getId());
}
结果
删(Delete)
接口方法
@Delete("Delete from user_info where id = #{id}")
Integer delete(Integer id);
测试
@Test
void delete() {
System.out.println(userInfoMapper.delete(15));
}
改(Update)
接口方法
@Update("update user_info set username = #{username} where id = #{id}")
Integer update(UserInfo userInfo);
@Update("update user_info set username = #{username} where id = #{id}")
Integer update1(String username, Integer id);
测试
@Test
void update() {
UserInfo userInfo = new UserInfo();
userInfo.setId(18);
userInfo.setUsername("zzzz");
System.out.println(userInfoMapper.update(userInfo));
}
@Test
void testUpdate() {
System.out.println(userInfoMapper.update1("haha",1));
}
查(Select)
我们在上⾯查询时发现,有⼏个字段是没有赋值的,只有Java对象属性和数据库字段⼀模⼀样时,才会进 ⾏赋值
原因分析: 当⾃动映射查询结果时,MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略 ⼤⼩写)。这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性
解决办法: 1. 起别名 2. 结果映射 3. 开启驼峰命名
起别名
在SQL语句中,给列名起别名,保持别名和实体类属性名⼀样
@Select("select id, username, `password`, age, gender, phone, delete_flag as deleteFlag, create_time as createTime, update_time as updateTime from user_info")
List<UserInfo> select1();
结果映射
//;结果映射
@Select("select id, username, `password`, age, gender, phone, delete_flag, \n" +
"create_time, update_time from user_info")
@Results({
@Result(column = "delete_flag",property = "deleteFlag"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time",property = "updateTime")
})
List<UserInfo> select2();
如果其他SQL,也希望可以复⽤这个映射关系,可以给这个Results定义⼀个名称id
在其他sql使用注解@ResultMap
@Select("select id, username, `password`, age, gender, phone, delete_flag, \n" +
"create_time, update_time from user_info")
@Results(id = "resultMap",value = {
@Result(column = "delete_flag",property = "deleteFlag"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time",property = "updateTime")
})
List<UserInfo> select2();
@Select("select * from user_info where id = #{id};")
@ResultMap("resultMap")
List<UserInfo> selectById(@Param("id") Integer userId);
开启驼峰命名(推荐)
通常数据库列使⽤蛇形命名法进⾏命名(下划线分割各个单词),⽽Java属性⼀般遵循驼峰命名法约定. 为了在这两种命名⽅式之间启⽤⾃动映射,只需要将mapUnderscoreToCamelCase 设置为true。Java代码不做任何处理
proporties文件配置
#配置驼峰⾃动转换
mybatis.configuration.map-underscore-to-camel-case=true
- 驼峰命名规则:abc_xyz=>abcXyz
- 表中字段名:abc_xyz • 类中属性名:abcXyz