java 学习笔记指路
基础知识前端
【黑马程序员pink老师前端】HTML
【黑马程序员pink老师前端】JavaScript基础大总结
【黑马程序员pink老师前端】JavaScript函数与作用域
【黑马程序员pink老师前端】JavaScript对象数据库
SQL编程
Java 框架
spring框架学习
Mybatis-plus 快速入门
Maven基础
Zookeeper初认识
HTTP简介
Tomcat
Servlet
vue入门
目录
一、Mybatis 简介
JAVA三层架构包括持久层、业务层、表现层。Mybatis是持久层框架,简单说就是操作数据库的。
Mybatis-plus是对 Mybatis 的优化。主要学习 Service 和 Mapper 两个接口。
本篇文章学习:
通用Mapper(BaseMapper)、通用Service(IService)
通用接口方法无法满足需求时:自定义Mapper、自定义Service
常用注释:@TableName、@TableId、@TableField、@TableLogic
插件:分页插件,乐观锁插件
提供更新和查找方法的Wrapper家族
0. IService和BaseMapper的区别
两个接口都可以对数据库增删改查(CRUD),两者的区别?
对比两个接口提供的方法可以看出,IService 是对 BaseMapper 的补充,光看数据库增删改查(CRUD)可以发现除了名字不同其他的都类似。BaseMapper不支持批量操作,IBaseService支持批量操作。
既然IService是对BaseMapper的补充,为什么还需要BaseMapper?
Service虽然加入了数据库的操作,但还是以业务功能为主,而更加复杂的SQL查询,还是要靠Mapper对应的XML文件里去编写SQL语句。
1. Mybatis-plus基本工程搭建
1.1 步骤总览
1、创建数据库
2、创建SpringBoot工程
3、引入依赖(pom文件中配置各个框架版本)
4、修改配置文件(数据库连接配置)
5、创建实体类(实体类是一种属性类,一般就是数据库的表)
6、创建 mapper 接口
7、启动类添加注解(在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹)
8、测试
1.2 步骤展开
看完步骤之后就可以尝试用一用了,增删改查的指令每次用来找就好
1、创建数据库
2、创建SpringBoot工程
3、引入依赖
springboot配置(中的版本会限制下面中包的版本,这就是为什么中好多包没有人工加版本)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
其他包和版本设置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
4、修改配置文件
在 application.properties 配置文件中添加 MySQL 数据库的相关配置:
// mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
设置与MySQL版本有关:
driver-class-name(驱动)
- 8以上:com.mysql.cj.jdbc.Driver
- 8以下:com.mysql.jdbc.Driver(没有cj)
url:
- 8以上:
?serverTimezone=GMT%2B8
需要添加东八区时区,jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8
- 8以下:
jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=true
5、创建实体类
实体类是一种属性类,一般就是数据库的表
package com.atguigu.mybatisplus.entity;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
6、创建 IService / BaseMapper 接口
package com.atguigu.mybatisplus.mapper;
public interface UserMapper extends BaseMapper<User> {
}
package com.atguigu.mybatisplus.service;
public interface UserService extends IService<User> {
}
7、启动类添加注解
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹
package com.atguigu.mybatisplus;
@SpringBootApplication
@MapperScan("com.atguigu.mybatisplus.mapper")
public class MybatisPlusApplication {
......
}
8、测试
package com.atguigu.mybatisplus;
@SpringBootTest
class MybatisPlusApplicationTests {
//@Autowired //默认按类型装配。是spring的注解
@Resource //默认按名称装配,找不到与名称匹配的bean,则按照类型装配。是J2EE的注解
private UserMapper userMapper;
@Test
void testSelectList() {
//selectList()方法的参数:封装了查询条件
//null:无任何查询条件
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
补充、查看sql输出日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
2. BaseMapper CRUD 接口
2.1 增C(Insert)
@Test
public void testInsert(){
User user = new User();
user.setName("Helen");
user.setAge(18);
//不设置email属性,则生成的动态sql中不包括email字段
int result = userMapper.insert(user);
System.out.println("影响的行数:" + result); //影响的行数
System.out.println("id:" + user.getId()); //id自动回填
}
2.2 查R(Select)
@Test
public void testSelect(){
//按id查询
User user = userMapper.selectById(1);
System.out.println(user);
//按id列表查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
//按条件查询
Map<String, Object> map = new HashMap<>();
map.put("name", "Helen"); //注意此处是表中的列名,不是类中的属性名
map.put("age", 18);
List<User> users1 = userMapper.selectByMap(map);
users1.forEach(System.out::println);
}
2.3 改U(Update)
@Test
public void testUpdate(){
User user = new User();
user.setId(1L);
user.setAge(28);
//注意:update时生成的sql自动是动态sql
int result = userMapper.updateById(user);
System.out.println("影响的行数:" + result);
}
2.4 删D(Delete)
@Test
public void testDelete(){
int result = userMapper.deleteById(5);
System.out.println("影响的行数:" + result);
}
2.5 配置分页拦截器
//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.xxgg.blog.mapper")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
3. 通用Service(IService)
3.1 save(增)
// 插入一条记录(选择字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize);
3.2 SaveOrUpdate(增或改)
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
3.3 Remove(删)
// 根据 entity 条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList);
3.4 Update(改)
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereEntity 条件,更新记录
boolean update(T entity, Wrapper<T> updateWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);
3.5 Get(按条件查)
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
3.6 List(查)
// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
3.7 Page(分页查)
// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
3.8 Count(查记录数)
// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);
3.9 Chain(链式)
query(链式查询)
// 链式查询 普通
QueryChainWrapper<T> query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper<T> lambdaQuery();
// 示例:
query().eq("column", value).one();
lambdaQuery().eq(Entity::getId, value).list();
update(链式更改)
// 链式更改 普通
UpdateChainWrapper<T> update();
// 链式更改 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper<T> lambdaUpdate();
// 示例:
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);
4. 自定义Mapper
4.1 步骤
1.定义接口
2.配置xml文件,并在xml文件中配置SQL语句
其他的都和通用Mapper一样
4.2 详细步骤
接口方法定义
List<User> selectAllByName(String name);
创建xml文件
在resources目录中创建mapper目录,创建UserMapper.xml。mapper目录是持久层映射文件的默认目录,如果是其他目录,需要配置mapper-locations,例如:mybatis-plus.mapper-locations=classpath: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.atguigu.mybatisplus.mapper.UserMapper">
<sql id="Base_Column_List">
id, name, age, email
</sql>
<select id="selectAllByName" resultType="com.atguigu.mybatisplus.entity.User">
select
<include refid="Base_Column_List"/>
from user
where
name = #{name}
</select>
</mapper>
测试
在MapperTests中创建如下测试用例
@Test
public void testSelectAllByName(){
List<User> users = userMapper.selectAllByName("Helen");
users.forEach(System.out::println);
}
5. 自定义Service
5.1 步骤
1.定义接口
2.实现接口方法
其他的都和通用Service一样
5.2 详细步骤
接口方法定义
List<User> listAllByName(String name);
实现接口方法
@Override
public List<User> listAllByName(String name) {
// baseMapper对象指向当前业务的mapper对象
return baseMapper.selectAllByName("Helen");
}
测试
ServiceTests中添加测试方法
@Test
public void testListAllByName(){
List<User> users = userService.listAllByName("Helen");
users.forEach(System.out::println);
}
6. @Table注释
6.1 @TableName
指定连接的表名 t_user
@TableName(value = "t_user")
public class User {
}
6.2 @TableId
默认情况下数据库的 id 列使用的是基于雪花算法的策略生成.
为什么需要使用雪花算法?
所以的数据基本都是根据 id 来查询的,数据量很大的时候不会存在同一个表中。mysql数据库是根据聚簇索引进行查找的,而这种查找方法在 id 有顺序的时候查找速度更快。所以 id 的设置需要满足有序、方便扩展到其他分表、各个分表使用频率差不多(均衡)、id 不能重复。
雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。
- 长度共 64 bit(一个long型)。
- 首先是一个符号位,正数是 0,负数是 1。
- 41bit 时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于 69.73年。
- 10bit 作为机器的 ID,5个 bit 是数据中心(是哪个地区的数据中心),5个 bit 的机器ID(每个数据中心中的机器)。
- 12bit 作为毫秒内的流水号。
6.2.1 value 属性
表中的 id 只有设置为 id 的时候mybatis才会知道这个数据是id而采用雪花算法,如果数据库中 id 没有命名为 id 就需要 value 属性。
比如 id 在数据库中叫 uid。
@TableId(value = "uid")
private String id;
6.2.2 type属性
IdType.ASSIGN_ID:使用基于雪花算法的策略生成数据id
IdType.AUTO:使用数据库的自增策略
@TableId(type = IdType.ASSIGN_ID)
private Long id;
全局配置:要想影响所有实体的配置,可以设置全局主键配置
// 全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type = auto
6.3 @TableField
6.3.1 自动填充
照阿里的编写要求,每个数据库都要有 create_time 和 update_time ,也就是表创建的时间和表更新的时间。
在这种很多表都需要添加的时候才用自动填充,只改几个表的时候直接在实体类中标注就可以。
step1:添加fill属性
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
step2:实现元对象处理器接口 -> 创建handler包,创建MyMetaObjectHandler类
添加 @Component 注解
package com.atguigu.mybatisplus.handler;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
6.3.2 判断字段是否存在
@Override
public void insertFill(MetaObject metaObject) {
//其他代码
//判断是否具备author属性
boolean hasAuthor = metaObject.hasSetter("author");
if(hasAuthor){
log.info("start insert fill author....");
this.strictInsertFill(metaObject, "author", String.class, "Helen");
}
}
6.3.3 判断字段是否赋值
@TableField(fill = FieldFill.INSERT)
private Integer age;
@Override
public void insertFill(MetaObject metaObject) {
//其他代码
//判断age是否赋值
Object age = this.getFieldValByName("age", metaObject);
if(age == null){
log.info("start insert fill age....");
this.strictInsertFill(metaObject, "age", String.class, "18");
}
}
6.4 @TableLogic
逻辑删除(和物理删除不同,类似于放入回收站,是可以找回的)
step1:数据库中创建逻辑删除状态列
数据库中添加 is_deleted 列(是否删除,根据阿里编程准则,是否用is_命名),0是未逻辑删除,1是逻辑删除。
step2:实体类中添加逻辑删除属性
@TableLogic
@TableField(value = "is_deleted")
private Integer deleted;
step3:测试
测试删除:删除功能被转变为更新功能
-- 实际执行的SQL
update user set is_deleted=1 where id = 1 and is_deleted=0
测试查询:被逻辑删除的数据默认不会被查询
-- 实际执行的SQL
select id,name,is_deleted from user where is_deleted=0
7. 插件
7.1 分页插件
7.1.1 为什么需要分页?
同一业务的单表数据也会达到单台数据库服务器的处理瓶颈
7.1.2 单表数据拆分有两种方式
垂直分表和水平分表
7.2 乐观锁插件
8. Wrapper家族
3.1 QueryWrapper
Mybatis-plus 提供的动态条件查询(条件构造器)
常见用法:用来查询id等
3.2 获取和赋值
get 参数名 获取
set 参数名 赋值
3.3 AjaxResult
SpringBoot通用返回类AjaxResult用来返回状态码信息以及提示信息(AjaxResult),以便于浏览器端进行异步数据处理。其中,code表示请求的返回状态码,message表示请求返回的消息提示,data则表示请求返回的数据。
AjaxResult的使用方法非常简单,只需要在Controller中将需要返回的数据,封装到AjaxResult对象中即可。下面是一段例子代码:
@RequestMapping("/demo")
@ResponseBody
public AjaxResult demo() {
User user = new User();
user.setName("Test");
user.setAge(18);
return AjaxResult.success(user);
}
3.4 ServiceImpl
ServiceImpl 类是我们进行SQL操作中非常重要的一个类,通过MybatisPlus生成的各个实体类的 XXXImpl 都会继承 ServiceImpl 类那里继承全部的方法。
3.5 ****接口
两种保存方式:save,batchsave