Mybatis plus
介绍
-
如果写一个数据库表的crud接口,正常流程:编写实体类-》编写Controller-》编写Service-》编写DAO-》-》编写XML文件
-
特别是管理后台,多数都是简单的CRUD,用普通的mybatis有的鸡肋
-
介绍
- 官网https://baomidou.com/
- 是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生
- 是怎么增强的呢?已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,类似JPA但优于JPA
-
更多特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
准备数据
xd_shop
CREATE TABLE `address` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`default_status` int(1) DEFAULT NULL COMMENT '是否默认收货地址:0->否;1->是',
`receive_name` varchar(64) DEFAULT NULL COMMENT '收发货人姓名',
`phone` varchar(64) DEFAULT NULL COMMENT '收货人电话',
`province` varchar(64) DEFAULT NULL COMMENT '省/直辖市',
`city` varchar(64) DEFAULT NULL COMMENT '市',
`region` varchar(64) DEFAULT NULL COMMENT '区',
`detail_address` varchar(200) DEFAULT NULL COMMENT '详细地址',
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='电商-公司收发货地址表';
CREATE TABLE `banner` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`img` varchar(524) DEFAULT NULL COMMENT '图片',
`url` varchar(524) DEFAULT NULL COMMENT '跳转地址',
`weight` int(11) DEFAULT NULL COMMENT '权重',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `banner` (`id`, `img`, `url`, `weight`)
VALUES
(1, 'https://file.xdclass.net/video/2020/alibabacloud/zx-lbt.jpeg', 'https://m.xdclass.net/#/member', 1),
(2, 'https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/20%E5%B9%B4%E5%8F%8C11%E9%98%BF%E9%87%8C%E4%BA%91/fc-lbt.jpeg', 'https://www.aliyun.com/1111/pintuan-share?ptCode=MTcwMTY3MzEyMjc5MDU2MHx8MTE0fDE%3D&userCode=r5saexap', 3),
(3, 'https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/20%E5%B9%B4%E5%8F%8C11%E9%98%BF%E9%87%8C%E4%BA%91/FAN-lbu-vip.jpeg', 'https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/Nginx.jpeg', 2);
CREATE TABLE `coupon` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`category` varchar(11) DEFAULT NULL COMMENT '优惠卷类型[NEW_USER注册赠券,TASK任务卷,PROMOTION促销劵]',
`publish` varchar(11) DEFAULT NULL COMMENT '发布状态, PUBLISH发布,DRAFT草稿,OFFLINE下线',
`coupon_img` varchar(524) DEFAULT NULL COMMENT '优惠券图片',
`coupon_title` varchar(128) DEFAULT NULL COMMENT '优惠券标题',
`price` decimal(16,2) DEFAULT NULL COMMENT '抵扣价格',
`user_limit` int(11) DEFAULT NULL COMMENT '每人限制张数',
`start_time` datetime DEFAULT NULL COMMENT '优惠券开始有效时间',
`end_time` datetime DEFAULT NULL COMMENT '优惠券失效时间',
`publish_count` int(11) DEFAULT NULL COMMENT '优惠券总量',
`stock` int(11) DEFAULT '0' COMMENT '库存',
`add_one` int(11) DEFAULT NULL COMMENT '是否叠加0是不行,1是可以',
`create_time` datetime DEFAULT NULL,
`condition_price` decimal(16,2) DEFAULT NULL COMMENT '满多少才可以使用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `product` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(128) DEFAULT NULL COMMENT '标题',
`cover_img` varchar(128) DEFAULT NULL COMMENT '封面图',
`detail` varchar(256) DEFAULT '' COMMENT '详情',
`old_price` decimal(16,2) DEFAULT NULL COMMENT '老价格',
`price` decimal(16,2) DEFAULT NULL COMMENT '新价格',
`stock` int(11) DEFAULT NULL COMMENT '库存',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`lock_stock` int(11) DEFAULT '0' COMMENT '锁定库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `product_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',
`state` varchar(11) DEFAULT NULL COMMENT 'NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单',
`create_time` datetime DEFAULT NULL COMMENT '订单生成时间',
`total_fee` decimal(16,2) DEFAULT NULL COMMENT '订单总金额',
`pay_fee` decimal(16,2) DEFAULT NULL COMMENT '订单实际支付价格',
`pay_type` varchar(64) DEFAULT NULL COMMENT '支付类型,微信-银行-支付宝',
`nickname` varchar(64) DEFAULT NULL COMMENT '昵称',
`head_img` varchar(524) DEFAULT NULL COMMENT '头像',
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`del` int(5) DEFAULT '0' COMMENT '0表示未删除,1表示已经删除',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`order_type` varchar(32) DEFAULT NULL COMMENT '订单类型 DAILY普通单,PROMOTION促销订单',
`receiver_address` varchar(1024) DEFAULT NULL COMMENT '收货地址 json存储',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `product_order_item` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`product_order_id` bigint(11) DEFAULT NULL COMMENT '订单号',
`out_trade_no` varchar(32) DEFAULT NULL,
`product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
`product_name` varchar(128) DEFAULT NULL COMMENT '商品名称',
`product_img` varchar(524) DEFAULT NULL COMMENT '商品图片',
`buy_num` int(11) DEFAULT NULL COMMENT '购买数量',
`create_time` datetime DEFAULT NULL,
`total_fee` decimal(16,2) DEFAULT NULL COMMENT '购物项商品总价格',
`pay_fee` decimal(16,0) DEFAULT NULL COMMENT '购物项商品支付总价格',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `user` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(128) DEFAULT NULL COMMENT '昵称',
`pwd` varchar(124) DEFAULT NULL COMMENT '密码',
`head_img` varchar(524) DEFAULT NULL COMMENT '头像',
`slogan` varchar(524) DEFAULT NULL COMMENT '用户签名',
`sex` tinyint(2) DEFAULT '1' COMMENT '0表示女,1表示男',
`points` int(10) DEFAULT '0' COMMENT '积分',
`create_time` datetime DEFAULT NULL,
`mail` varchar(64) DEFAULT NULL COMMENT '邮箱',
`secret` varchar(12) DEFAULT NULL COMMENT '盐,用于个人敏感信息处理',
PRIMARY KEY (`id`),
UNIQUE KEY `mail_idx` (`mail`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SpringBoot2.X整合MybatisPlus+Lombok
添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.xdclass</groupId>
<artifactId>1024shop-manager</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>1024shop-manager</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis plus和springboot整合-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 代码自动生成依赖 begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!-- 代码自动生成依赖 end-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
</plugins>
</build>
<!-- 代码库 -->
<repositories>
<repository>
<id>maven-ali</id>
<url>http://maven.aliyun.com/nexus/content/groups/public//</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
增加数据库配置 application.properties
server.port=8081
#==============================数据库相关配置========================================
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.31.101:50000/xd_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username =root
spring.datasource.password =123456
包扫描:添加@MapperScan注解
package net.xdclass.shopmanager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("net.xdclass.shopmanager.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
快速上手
基础准备
- 统一接口返回协议-JsonData
package net.xdclass.shopmanager.util;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor //会生成一个包含所有变量
@NoArgsConstructor //生成一个无参数的构造方法
public class JsonData {
/**
* 状态码 0 表示成功,1表示处理中,-1表示失败
*/
private Integer code;
/**
* 数据
*/
private Object data;
/**
* 描述
*/
private String msg;
// 成功,传入数据
public static JsonData buildSuccess() {
return new JsonData(0, null, null);
}
// 成功,传入数据
public static JsonData buildSuccess(Object data) {
return new JsonData(0, data, null);
}
// 失败,传入描述信息
public static JsonData buildError(String msg) {
return new JsonData(-1, null, msg);
}
// 失败,传入描述信息,状态码
public static JsonData buildError(String msg, Integer code) {
return new JsonData(code, null, msg);
}
}
BannerDO类编写
@Data
@TableName("banner")//表名映射
public class BannerDO {
private Integer id;
private String img;
private String url;
private Integer weight;
}
Controller
@RestController
@RequestMapping("/api/banner/v1")
public class BannerController {
//注入有红色告警可以忽略,idea识别问题
@Autowired
private BannerService bannerService;
@RequestMapping("list")
public JsonData list(){
return JsonData.buildSuccess(bannerService.list());
}
}
Service
public interface BannerService {
List<BannerDO> list();
}
ServiceImpl
@Service
public class BannerServiceImpl implements BannerService {
@Autowired
private BannerMapper bannerMapper;
@Override
public List<BannerDO> list() {
//查询全部
List<BannerDO> list = bannerMapper.selectList(new QueryWrapper<BannerDO>());
return list;
}
}
Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.xdclass.shopmanager.model.BannerDO;
public interface BannerMapper extends BaseMapper<BannerDO> {
}
测试
// http://localhost:8081/api/banner/v1/list
{
"code": 0,
"data": [
{
"id": 1,
"img": "https://file.xdclass.net/video/2020/alibabacloud/zx-lbt.jpeg",
"url": "https://m.xdclass.net/#/member",
"weight": 1
},
{
"id": 2,
"img": "https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/20%E5%B9%B4%E5%8F%8C11%E9%98%BF%E9%87%8C%E4%BA%91/fc-lbt.jpeg",
"url": "https://www.aliyun.com/1111/pintuan-share?ptCode=MTcwMTY3MzEyMjc5MDU2MHx8MTE0fDE%3D&userCode=r5saexap",
"weight": 3
},
{
"id": 3,
"img": "https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/20%E5%B9%B4%E5%8F%8C11%E9%98%BF%E9%87%8C%E4%BA%91/FAN-lbu-vip.jpeg",
"url": "https://file.xdclass.net/video/%E5%AE%98%E7%BD%91%E8%BD%AE%E6%92%AD%E5%9B%BE/Nginx.jpeg",
"weight": 2
}
],
"msg": null
}
单元测试、控制台数据sql
Spring Boot Test 是在Spring Test之上的再次封装, 使用@SpringBootTest后,Spring将加载所有被管理的bean,等同于启动了整个服务
项目添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
Mybatis plus配置控制台打印日志
#配置mybatis plus打印sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
项目新建测试
package net.xdclass.shopmanager;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.shopmanager.model.BannerDO;
import net.xdclass.shopmanager.service.BannerService;
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(classes = Application.class)
@Slf4j
class ApplicationTests {
@Autowired
private BannerService bannerService;
@Test
public void testBannerList(){
List<BannerDO> list = bannerService.list();
log.info("轮播图列表:{}",list);
}
}
核心类BaseMapper
核心类介绍: Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
- 方法很多:记住常用的几个就行
/**
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
* <p>这个 Mapper 支持 id 泛型</p>
*
* @author hubin
* @since 2016-01-23
*/
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 根据 ID 修改
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 查询(根据 columnMap 条件)
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
QueryWrapper介绍
- 查询包装类,可以封装多数查询条件,泛型指定返回的实体类
List<BannerDO> list = bannerMapper.selectList(new QueryWrapper<BannerDO>());
Mybatis plus常用注解
- @TableName 用于定义表名
- @TableId 用于定义表的主键
- 属性
value 用于定义主键字段名
type 用于定义主键类型(主键策略 IdType)
- 主键策略
IdType.AUTO 主键自增,系统分配,不需要手动输入
IdType.NONE 未设置主键
IdType.INPUT 需要自己输入 主键值
IdType.ASSIGN_ID 系统分配 ID,用于数值型数据(Long,对应 mysql 中 BIGINT 类型)
IdType.ASSIGN_UUID 系统分配 UUID,用于字符串型数据(String,对应 mysql 中 varchar(32) 类型)
@TableField 用于定义表的非主键字段
- 属性
value 用于定义非主键字段名,用于别名匹配,假如java对象属性和数据库属性不一样
exist 用于指明是否为数据表的字段, true 表示是,false 为不是,假如某个java属性在数据库没对应的字段则要标记为faslse
fill 用于指定字段填充策略(FieldFill,用的不多)
字段填充策略:一般用于填充 创建时间、修改时间等字段
FieldFill.DEFAULT 默认不填充
FieldFill.INSERT 插入时填充
FieldFill.UPDATE 更新时填充
FieldFill.INSERT_UPDATE 插入、更新时填充。
示例
@Data
@TableName("banner")//表名映射
public class BannerDO {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String img;
private String url;
@TableField("weight")
private Integer weightAAA;
@TableField(exist = false)
private Date createTime;
}
查询
- selectById
- selectBatchIds
- selectOne
- selectCount
- selectList
package net.xdclass.shopmanager;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.shopmanager.mapper.BannerMapper;
import net.xdclass.shopmanager.model.BannerDO;
import net.xdclass.shopmanager.service.BannerService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.List;
@SpringBootTest(classes = Application.class)
@Slf4j
class ApplicationTests {
@Autowired
private BannerService bannerService;
@Autowired
private BannerMapper bannerMapper;
/**
* 根据主键ID查找
*/
@Test
public void testSelectById(){
BannerDO bannerDO = bannerMapper.selectById(1);
log.info("bannerDO:{}",bannerDO);
}
/**
* 批量查找根据主键
*/
@Test
public void selectBatchIds(){
List<BannerDO> bannerDOS = bannerMapper.selectBatchIds(Arrays.asList(1, 2));
log.info("bannerDOS:{}",bannerDOS);
}
/**
* 选择1条
*/
@Test
public void selectOne(){
BannerDO bannerDO = bannerMapper.selectOne(new QueryWrapper<BannerDO>().eq("id",1));
log.info("bannerDO:{}",bannerDO);
}
/**
* 统计行数
*/
@Test
public void selectCount(){
int size = bannerMapper.selectCount(null);
log.info("size:{}",size);
}
/**
* 查询所有数据
*/
@Test
public void testBannerList(){
List<BannerDO> list = bannerService.list();
log.info("轮播图列表:{}",list);
}
}
新增
/**
* 新增
*/
@Test
public void testAdd(){
BannerDO bannerDO = new BannerDO();
bannerDO.setImg("xxx");
bannerDO.setUrl("xdclass.net");
bannerMapper.insert(bannerDO);
log.info("轮播图:{}", bannerDO);
}
删除
- 根据id删除
/**
* 根据id删除
*/
@Test
public void testDeleteId(){
//影响行数
int rows = bannerMapper.deleteById(4);
log.info("rows:{}",rows);
}
- 条件删除
/**
* 通用删除操作 deleteByMap map要写列名条件 不能是实体属性名
*/
@Test
public void testCommonDeleteByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("weight",12);
columnMap.put("url","bbb");
int result=bannerMapper.deleteByMap(columnMap);
System.out.println("*******************"+result);
}
更新
- queryWrapper更新操作
BannerDO bannerDO = new BannerDO();
bannerDO.setImg("iiiii");
//空字段不会更新,只会更新设置的字段
bannerMapper.update(bannerDO,new QueryWrapper<BannerDO>().eq("id","1"));
- updateWrapper更新操作
UpdateWrapper updateWrapper = new UpdateWrapper();
//设置要更新的字段和值,key是db的属性名称
updateWrapper.set("img","uuuu");
//条件
updateWrapper.eq("id",1);
bannerMapper.update(null,updateWrapper);
查询封装类QueryWrapper比较API
QueryWrapper介绍
- 可以封装sql对象,包括where条件,order by排序,select哪些字段等等
- 查询包装类,可以封装多数查询条件,泛型指定返回的实体类
List<BannerDO> list = bannerMapper.selectList(new QueryWrapper<BannerDO>());
-
核心API
- eq 等于
- ne 不等于
- gt 大于
- ge 大于等于
- lt 小于
- le 小于等于
- or 拼接or
- between 两个值中间
- notBetween 不在两个值中间
-
- like 模糊匹配
- notLike 不像
- likeLeft 左匹配
- likeRight 右边匹配
- isNull 字段为空
- in in查询
- groupBy 分组
- orderByAsc 升序
- orderByDesc 降序
- having having查询
@Test
public void testQueryWrapper(){
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("id", 1);
queryWrapper.ne("url", "bbbc.com");
List list = bannerMapper.selectList(queryWrapper);
log.info("list:{}",list); // list:[BannerDO(id=1, img=https://file.xdclass.net/video/2020/alibabacloud/zx-lbt.jpeg, url=https://m.xdclass.net/#/member, weightAAA=1, createTime=null)]
}
配置分页插件
新建配置类
package net.xdclass.shopmanager.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusPageConfig {
/* 旧版本配置
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}*/
/**
* 新的分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试分页
/***
* 测试分页
*/
@Test
public void testPage() {
QueryWrapper<BannerDO> wrapper = new QueryWrapper<>();
wrapper.eq("weight",4);
//第1页,每页2条
Page<BannerDO> page = new Page<>(1, 2);
IPage<BannerDO> iPage = bannerMapper.selectPage(page, wrapper);
System.out.println("总条数"+iPage.getTotal());
System.out.println("总页数"+iPage.getPages());
//获取当前数据
System.out.println(iPage.getRecords().toString());
}
Mybatis plus 自定义xml sql脚本
Mapper类中增加方法
package net.xdclass.shopmanager.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.xdclass.shopmanager.model.BannerDO;
import java.util.List;
public interface BannerMapper extends BaseMapper<BannerDO> {
List<BannerDO> list();
}
新建xml src\main\resources\mapper\BannerMapper.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接口的路径,记得修改-->
<mapper namespace="net.xdclass.shopmanager.mapper.BannerMapper">
<select id="list" resultType="net.xdclass.shopmanager.model.BannerDO">
select * from banner
</select>
</mapper>
配置文件告诉mapper.xml路径(如果采用默认路径可以不配)
#默认配置路径
mybatis-plus.mapper-locations=classpath*:/mapper/*Mapper.xml
测试
@Test
public void testList(){
List<BannerDO> list = bannerMapper.list();
log.info("list:{}",list);
}
Mybatis plus全局配置案例
配置Myabits的全局配置文件
- 注意:config-location和configuration不能同时出现,需要注释配置文件里的相关配置
#配置文件
server.port=8081
#==============================数据库相关配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.0.114:3306/xd_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=xdclass.net
#开启控制台打印sql
#mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置mybatis plus打印sql日志
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置最新全局配置文件!!!!
mybatis-plus.config-location = classpath:mybatis-config.xml
- 创建mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--控制台输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
- 配置文件配置 自定义sql的包扫描
mybatis-plus.type-aliases-package= net.xdclass.shop.model
- XML改为
<!--旧-->
<select id="list" resultType="net.xdclass.shop.model.BannerDO">
select * from banner
</select>
<!--新-->
<select id="list" resultType="BannerDO">
select * from banner
</select>
- mybatis plus下划线转驼峰配置,默认就是true
mybatis-plus.configuration.map-underscore-to-camel-case=true
- 配置全局默认主键类型,实体类就不用加 @TableId(value = "id", type = IdType.AUTO)
mybatis-plus.global-config.db-config.id-type=auto
性能优化之指定select字段查询
面试题:select * 和 select 指定字段的区别
- 网络IO问题
select * 会查出所有的字段,有些是不需要的,当应用程序和服务器不在同一个局域网时,字段过多会影响网络传输的性能
- 索引问题
在 指定字段有索引的情况下,mysql是可以不用读data,直接使用index里面的值就返回结果的。
但是一旦用了select *,就会有其他列需要从磁盘中读取才会返回结果,这样就造成了额外的性能开销
- MybatisPlus指定查询字段
bannerMapper.selectList(new QueryWrapper<BannerDO>().select("id","name"));
ActiveRecord
- 什么是ActiveRecord(只做简单了解即可)
Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。
- Mybatis Plus对AR有一定支持, 在MP中开启AR,仅需要实体类继承Model类即可
@Data
//表名映射,用于新增才需要
@TableName("banner")
public class BannerDO extends Model<BannerDO> {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String img;
private String url;
private Integer weight;
}
- 使用
BannerDO bannerDO = new BannerDO();
BannerDO b = bannerDO.selectOne(new QueryWrapper<BannerDO>().eq("id","1"));
System.out.println(b);
使用建议
- 业务逻辑比较简单,当类基本上和数据库中的表一一对应时, ActiveRecord是非常方便的, 即业务逻辑大多数是对单表操作,简单,直观 一个类就包括了数据访问和业务逻辑.
- ActiveRecord虽然有业务逻辑, 但基本上都是基于单表的. 跨表逻辑一般会放到当发生跨表的操作时, 往往会配合使用事务脚本(Transaction Script)中.
- 如果对象间的关联越来越多, 你的事务脚本越来越庞大, 重复的代码越来越多, 就不建议使用了
- 模型容易混乱,ActiveRecord保存了数据, 使它有时候看上去像数据传输对象(DTO). 但是ActiveRecord有数据库访问能力, 所以所以分布式或者大型项目基本不用
- POJO: model/domain/dto/vo/bo/do
数据库高并发乐观锁
- 什么是乐观锁
每次去拿数据的时候都认为别人不会修改,更新的时候会判断是别人是否回去更新数据,通过版本来判断,如果数据被修改了就拒绝更新
Java里面大量使用CAS, CAS这个是属于乐观锁,性能较悲观锁有很大的提高
AtomicXXX 等原子类底层就是CAS实现,一定程度比synchonized好,因为后者是悲观锁
小结:悲观锁适合写操作多的场景,乐观锁适合读操作多的场景,乐观锁的吞吐量会比悲观锁多
- 数据库的乐观锁
大多是基于数据版本 (Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通
过为数据库表增加一个 “version” 字段来 实现。 读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据,库表对应记录的当前版本信息进行比对,如果提交的数据 版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据
MybatisPlus乐观锁插件使用
- Mybatis Plus里面自带一个插件,可以帮我们轻松实现乐观锁
- 使用:实体类增加version属性配置
@Version
private Integer version;
- 数据库增加version版本字段
CREATE TABLE `banner` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`img` varchar(524) DEFAULT NULL COMMENT '图片',
`url` varchar(524) DEFAULT NULL COMMENT '跳转地址',
`weight` int(11) DEFAULT NULL COMMENT '权重',
`version` int(11) DEFAULT '1' COMMENT '乐观锁版本号',
`deleted` int(11) DEFAULT '0' COMMENT '0是未删除,1是已经删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4;
- 增加乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
- 使用
//先根据ID找记录,得到 id 和 version
BannerDO bannerDO = new BannerDO();
bannerDO.setVersion(1);//旧版本号,即查询出来的版本号
bannerDO.setId(1);
bannerDO.setUrl("xdclass.net");
bannerMapper.updateById(bannerDO);
注意
- 乐观锁数据类型支持int、integer、long、timestamp
- 仅支持updateById和update方法
MybatisPlus 逻辑删除配置
- 什么是逻辑删除
很多互联网公司在数据库设计规范中都加入了逻辑删除的强制规定,运营人员可以分析和审查数据,也方便将数据沉淀下来用于商业分析
比如用户删除了订单,只不过是更新了标记,不会真正的物理删除。
- 数据量过多,也会采用数据仓库,通过监听应用数据库的数据数据变化,进行迁移到数据仓库
- MybatisPlus如何使用
- 数据库增加deleted字段,0是未删除,1表示删除
- 实体类增加属性配置@TableLogic 或者 在配置文件增加指定
@TableLogic
private Integer deleted;
- 配置文件新增配置
#删除是1
mybatis-plus.global-config.db-config.logic-delete-value=1
#未删除是0
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#如果java实体类没加注解@TableLogic,则可以配置这个,推荐这里配置
mybatis-plus.global-config.db-config.logic-delete-field=deleted
验证
- deleteById删除后就是,结果就是更新 字段 delete = 1
- 查询的时候会自动拼接上deleted=0的检索条件
Mybatis-plus-generator代码自动生成工具
- Mybatis-plus-generator介绍
- AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
- 底层是模板引擎技术,可以自定义生成的java类模板
- 大家以前或多或少用过基础版mybatis-genarator
- 进阶版mybatis-plus-genarator实战
- 添加依赖
<!-- 代码自动生成依赖 begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!-- 代码自动生成依赖 end-->
- 代码(标记TODO的记得修改)
package net.xdclass.shopmanager;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.apache.commons.lang3.StringUtils;
import java.util.Scanner;
/**
* 小滴课堂,愿景:让技术不再难学
*
* @Description
* @Author 二当家小D
* @Remark 有问题直接联系我,源码-笔记-技术交流群
* @Version 1.0
**/
public class MyBatisPlusGenerator {
public static void main(String[] args) {
//1. 全局配置
GlobalConfig config = new GlobalConfig();
// 是否支持AR模式
config.setActiveRecord(true)
// 作者
.setAuthor("二当家小D")
// 生成路径,最好使用绝对路径,window路径是不一样的
//TODO TODO TODO TODO
.setOutputDir("E:\\demo\\src\\main\\java")
// 文件覆盖
.setFileOverride(true)
// 主键策略
.setIdType(IdType.AUTO)
.setDateType(DateType.ONLY_DATE)
// 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的
.setServiceName("%sService")
//实体类结尾名称
.setEntityName("%sDO")
//生成基本的resultMap
.setBaseResultMap(true)
//不使用AR模式
.setActiveRecord(false)
//生成基本的SQL片段
.setBaseColumnList(true);
//2. 数据源配置
DataSourceConfig dsConfig = new DataSourceConfig();
// 设置数据库类型
dsConfig.setDbType(DbType.MYSQL)
.setDriverName("com.mysql.cj.jdbc.Driver")
//TODO TODO TODO TODO
.setUrl("jdbc:mysql://192.168.31.101:50000/xd_shop?useSSL=false")
.setUsername("root")
.setPassword("123456");
//3. 策略配置globalConfiguration中
StrategyConfig stConfig = new StrategyConfig();
//全局大写命名
stConfig.setCapitalMode(true)
// 数据库表映射到实体的命名策略
.setNaming(NamingStrategy.underline_to_camel)
//使用lombok
.setEntityLombokModel(true)
//使用restcontroller注解
.setRestControllerStyle(true)
// 生成的表, 支持多表一起生成,以数组形式填写
//TODO TODO TODO TODO 两个方式,直接写,或者使用命令行输入
.setInclude("product","banner","address","coupon","product_order");
//.setInclude(scanner("表名,多个英文逗号分割").split(","));
//4. 包名策略配置
PackageConfig pkConfig = new PackageConfig();
pkConfig.setParent("net.xdclass.shopmanager")
.setMapper("mapper")
.setService("service")
.setController("controller")
.setEntity("model")
.setXml("mapper");
//5. 整合配置
AutoGenerator ag = new AutoGenerator();
ag.setGlobalConfig(config)
.setDataSource(dsConfig)
.setStrategy(stConfig)
.setPackageInfo(pkConfig);
//6. 执行操作
ag.execute();
System.out.println("======= 小滴课堂 Done 相关代码生成完毕 ========");
}
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
}
自动化生成代码-加入项目
- 对比生成的代码进行配置
- 数据库连接和库名称
- 需要生成的表
- 生成的路径
- 拷贝自动生成的代码进入到项目
- model 类拷贝
- mapper 类拷贝
- mapper xml脚本拷贝
- service和controller不拷贝
- 注意
- 使用起来和普通版的mybatis generator一样,但是这个纯代码,不用复杂xml配置
- 任何框架,不要使用过多的侵入或者框架定制化深的内容,防止后续改动耦合性高,成本大
总结
- 优点
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
- 缺点
项目映入了第三方包,未来升级存在一定的兼容性问题
社区相对新生-文档缺乏相关的信息, 或者更新不及时
项目使用建议
- 任何框架或技术肯定有利也有弊,看的角度和结合团队实际情况
- 高内聚-低解耦肯定是软件设计思想必须要遵守的原则,所以业务代码可以适当使用MyBatisPlus好的功能
- 好用的:通用crud、自动生成工具、分页查询
- 有点耦合但也不错的功能:逻辑删除、乐观锁等
- AR则不怎么建议使用
- 偏业务型项目、管理后端项目等推荐使用,和jpa类似