1. Mybatis-Plus简介
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。这是官方给的定义,关于mybatis-plus的更多介绍及特性,可以参考https://mp.baomidou.com/。那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于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 操作智能分析阻断,也可自定义拦截规则,预防误操作
2. Spring整合Mybatis-Plus
正如官方所说,mybatis-plus 在 mybatis 的基础上只做增强不做改变,因此其与 spring 的整合亦非常简单。
只需把 mybatis 的依赖换成 mybatis-plus 的依赖,再把 sqlSessionFactory 换成 mybatis-plus 的即可。
注意: 实际应用中,更多的是 SpringBoot 整合 MyBatis-Plus 相对较少。
1) 配置 pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_mybatisplus</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<!--MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--用于配置连接池数据源-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
<!--用于spring集成测试-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.5</version>
</dependency>
<!--打印SQL日志需要-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!--只能在test目录下才能使用-->
<!-- <scope>test</scope>-->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2) db.properties
连接数据库的配置信息,以 MySQL8 为例。
resources/目录下:
url=jdbc:mysql://127.0.0.1:3306/wdzldb?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
driver=com.mysql.cj.jdbc.Driver
dbuname=root
password=root
3) log4j.properties
如果需要打印输出执行过程中的 SQL 日志,配置 log4j
需要下面配置
指定自己的包名:log4j.logger.com.wdzl=debug
其他全局的配置:log4j.rootLogger=warn, stdout
# Global logging configuration debug info warn error
log4j.rootLogger=warn, stdout
# MyBatis logging configuration...
log4j.logger.org.apache.ibatis=debug
## MySelf Package
log4j.logger.com.wdzl=debug
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.Target=System.out
#log4j.appender.stdout.layout.ConversionPattern=%5p %d [%t] - %m%n
log4j.appender.stdout.layout.ConversionPattern=%3p - %m%n
4) spring-mybatis.xml
整合配置基本顺序:数据源-->--SessionFactory-->--Dao
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置连接数据库的属性文件-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- DataSource: URL Driver Username Password -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${dbuname}"/>
<property name="password" value="${password}"/>
</bean>
<!-- SessionFactory 注意这里所在包,和之前mybatis整合不同。同时,plus 版本不同,这个类位置也有不同 -->
<bean id="sessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.wdzl.pojo"/>
</bean>
<!-- Dao -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描基包下的所有的接口,生成对应的实现代理对象-->
<property name="basePackage" value="com.wdzl.dao" />
<property name="sqlSessionFactoryBeanName"
value="sessionFactory"></property>
</bean>
<!--注意 启动注解扫描 注意 -->
<context:component-scan base-package="com.wdzl"/>
</beans>
注意:
SessionFactoy 不是 spring+mybatis 整合时的工厂bean;
com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean
5) 编写 pojo 类
// 驼峰命名的属性,默认生成sql字段时,会变成 book_Id 需要映射
@TableId("bookid")
如果数据库表主键自增的,则不添加此@TableId 注解,执行insert 时,是可以的
但是通过主键查询时,会出现 where null=? 问题。
所以需要配置@TableId
配置注解后的问题
- 1.不指定 type type = IdType.NONE ,自己设置主键。如果不给主键,默认指定long的唯一主键
- 2.type = IdType.AUTO : 数据库指定主键。保存时给主键与否,都会采用数据库自增策略
- 3.ASSIGN_ID 等同NONE : 程序指定主键
- a.手动设置有值,则使用设置的主键
- b.如果没有指定,则框架默认使用雪花算法生成主键
/**
*
*/
@Data
@TableName("Book")
public class Book {
@TableId(value = "bookid",type = IdType.ASSIGN_ID)
// @TableField("bookid")//注意:使用TabelId后,@TableField同时使用无效
private Integer bookId;
@TableField("bookName")
private String bookName;
private String author;
@TableField("pubDate") // @TableField 可以设置忽略 exist=true
private Date pubDate;
private Float price;
}
6) DAO接口
注意:
- @Repository 注解
- BaseMapper<Book> 泛型
@Repository
public interface IBookDao extends BaseMapper<Book> {
}
只需要自定义接口继承 BaseMapper 接口同时指定泛型即可。
7) 使用测试
需要依赖 Junit4 以上版本和 spring-test
注意:junit 依赖配置
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!--只能在test目录下才能使用-->
<!--<scope>test</scope>-->
</dependency>
测试用例代码:
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.wdzl.dao.IBookDao;
import com.wdzl.pojo.Book;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-mybatis.xml"})
public class BookService {
@Autowired
private IBookDao bookDao;
@Test
public void get(){
Book book = bookDao.selectById(26);
System.out.println(book);
}
@Test
public void findAll(){
bookDao.selectList(null).forEach(System.out::println);
}
@Test
public void findByMap(){
Map<String,Object> map = new HashMap<>();
map.put("author","张宇昂");
map.put("bookName","JavaEE框架");
map.put("price",45f);
// 转为等值条件查询
bookDao.selectByMap(map).forEach(System.out::println);
}
@Test
public void findByWrapper(){
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
//选择要查询的列
queryWrapper.select("bookname","price","author");
//指定条件
queryWrapper.ge("price",10);
queryWrapper.le("price",100);
queryWrapper.like("bookname","%Java%");
queryWrapper.in("bookid",23,4,5,63);
queryWrapper.between("price",23,89);
bookDao.selectList(queryWrapper);
}
@Test
public void findByIdList(){
List<Integer> integers = Arrays.asList(23, 26, 47);
bookDao.selectBatchIds(integers).forEach(System.out::println);
}
@Test
public void insert(){
Book book = new Book();
// book.setBookId(49);
book.setBookName("JavaEE框架");
book.setAuthor("张宇昂");
book.setPrice(45f);
book.setPubDate(new Date());
int insert = bookDao.insert(book);
//插入成功后,默认可以自动获取主键
System.out.println(insert+"==="+book.getBookId());
}
}
8) 雪花算法
简介:
SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的
分布式ID的特点
全局唯一性:不能出现有重复的ID标识,这是基本要求。
递增性:确保生成ID对于用户或业务是递增的。
高可用性:确保任何时候都能生成正确的ID。
高性能性:在高并发的环境下依然表现良好。
分布式ID的常见解决方案
UUID
Java自带的生成一串唯一随机36位字符串(32个字符串+4个“-”)的算法。它可以保证唯一性,且据说够用N亿年,但是其业务可读性差,无法有序递增。
SnowFlake
今天的主角雪花算法,它是Twitter开源的由64位整数组成分布式ID,性能较高,并且在单机上递增
组成部分(64bit)
1.第一位 占用1bit,其值始终是0,没有实际作用。
2.时间戳 占用41bit,精确到毫秒,总共可以容纳约69年的时间。
3.工作机器id 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点。
4.序列号 占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID。
SnowFlake算法在同一毫秒内最多可以生成多少个全局唯一ID呢:
: \同一毫秒的ID数量 = 1024 X 4096 = 4194304**