mysql mybatis原数据相加_MyBatisPlus-入门使用笔记

本文介绍了MyBatisPlus的快速入门,包括创建数据库表、初始化SpringBoot工程、配置依赖、编写实体类和Mapper接口,以及使用MyBatisPlus进行数据的CRUD操作。同时,文章还讨论了MyBatisPlus的主键策略、自动填充功能以及日志配置,展示了如何实现自动插入和更新时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MyBatisPlus

MyBatisPlus

快速入门

官网文档地址:https://baomidou.gitee.io/mybatis-plus-doc/#/install

使用第三方组件步骤:

1、导入相应的依赖

2、研究依赖如何配置

3、代码如何编写

4、提高扩展技术能力

步骤

1、创建数据库 User 表

id

name

age

email

1

Jone

18

test1@baomidou.com

2

Jack

20

test2@baomidou.com

3

Tom

28

test3@baomidou.com

4

Sandy

21

test4@baomidou.com

5

Billie

24

test5@baomidou.com

其对应的数据库 Schema 脚本如下:

DROP TABLE IF EXISTS user;

CREATE TABLE user

(

id BIGINT(20) NOT NULL COMMENT '主键ID',

name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',

age INT(11) NULL DEFAULT NULL COMMENT '年龄',

email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',

PRIMARY KEY (id)

);

其对应的数据库 Data 脚本如下:

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES

(1, 'Jone', 18, 'test1@baomidou.com'),

(2, 'Jack', 20, 'test2@baomidou.com'),

(3, 'Tom', 28, 'test3@baomidou.com'),

(4, 'Sandy', 21, 'test4@baomidou.com'),

(5, 'Billie', 24, 'test5@baomidou.com');

初始化工程

创建一个springboot的工程

可以使用 Spring Initializer 快速初始化一个 Spring Boot 工程

添加依赖

引入spring boot starter 父工程依赖:

org.springframework.bootgroupId>

spring-boot-starter-parentartifactId>

2.3.0.RELEASEversion>

parent>

引入 spring-boot-starter、spring-boot-starter-test、mybatis-plus-boot-starter、lombok、mysql 依赖:

org.springframework.bootgroupId>

spring-boot-starterartifactId>

dependency>

org.springframework.bootgroupId>

spring-boot-starter-testartifactId>

testscope>

dependency>

org.projectlombokgroupId>

lombokartifactId>

trueoptional>

dependency>

com.baomidougroupId>

mybatis-plus-boot-starterartifactId>

3.0.5version>

dependency>

mysqlgroupId>

mysql-connector-javaartifactId>

dependency>

dependencies>

配置

在application.yml配置文件中添加mysql的基本配置和项目启动的基本配置

spring:

#配置thymeleaf模板

thymeleaf:

cache: false#开启缓存

prefix: classpath:/templates/

suffix: .html

encoding: utf-8

#配置mysql数据源

datasource:

driver-class-name: com.mysql.cj.jdbc.Driver

url: jdbc:mysql://39.105.27.58/ssmarkert?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT

username: root

password: lovehxp521..

#项目启动端口

server:

port: 8080

#打印日志

mybatis-plus:

configuration:

log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

在Springboot启动类中添加@MapperScan注解,扫描Mapper文件:

@SpringBootApplication

@MapperScan("com.hxp.ssmkert.mapper")

public class SsmkertApplication {

public static void main(String[] args) {

SpringApplication.run(SsmkertApplication.class, args);

}

}

编码

编写实体类User.java(此处使用了Lombok简化代码)

@Data

public class User{

private Long id;

private String name;

private Integer age;

private String email;

}

编写Mapper接口 UserMapper.java,所有的CRUD都已经帮我们写完了

public interface UserMapper extends BaseMapper {

}

开始使用

添加测试类,进行整合测试:

@RunWith(SpringRunner.class)

@SpringBootTest

public class UserMapperTest {

@Autowired

private UserMapper userMapper;

@Test

public void userMapperTest1(){

List users = userMapper.selectList(null);//条件为null

users.forEach(System.out::println);

}

}

userMapper 中的 selectList()方法的参数为MP内置的条件封装器Wrapper,所以不填写就是无条件

控制台输出:

User(id=1, name=Jone, age=18, email=test1@baomidou.com)

User(id=2, name=Jack, age=20, email=test2@baomidou.com)

User(id=3, name=Tom, age=28, email=test3@baomidou.com)

User(id=4, name=Sandy, age=21, email=test4@baomidou.com)

User(id=5, name=Billie, age=24, email=test5@baomidou.com)

思考问题?

1、SQL谁帮我们写的?Mybatis-Plus都写好了

2、方法哪里来的?Mybatis-Plus都写好了

配置日志

我们的mysql运行情况我们是不知道的,我们需要配置一下日志打印输出,帮助我们开发测试。

在appliaction.yml文件中

mybatis-plus:

configuration:

log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

主键的主键策略

1.使用注解添加主键策略

自增主键

//使用注解添加主键策略

@TableId(type = IdType.AUTO)//主键自增策略

private Long id;

注意:使用自增主键必须数据表设置自增

2.通过配置文件配置全局的主键生成策略

主键策略列表:

策略

说明

AUTO

数据库ID自增

NONE

该类型为未设置主键类型

INPUT

用户输入ID,该类型可以通过自己注册自动填充插件进行填充

ID_WORKER

全局唯一ID (idWorker)

UUID

全局唯一ID (UUID)

ID_WORKER_STR

字符串全局唯一ID (idWorker 的字符串表示)

AUTO(0),//数据库ID自增

NONE(1),//该类型为未设置主键类型,新版本更新 默认为NONE

INPUT(2),//用户输入ID * 该类型可以通过自己注册自动填充插件进行填充

/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */

ID_WORKER(3),//全局唯一ID (idWorker)

UUID(4),//全局唯一ID (UUID)

ID_WORKER_STR(5);//字符串全局唯一ID (idWorker 的字符串表示)

mybatis-plus 的CRUD

update

//修改测试

@Test

public void UpdateTest() {

User user = new User();

user.setId(6L);

user.setName("一颗小土豆");

user.setAge(24);

user.setEmail("2426712259@qq.com");

int update = userMapper.updateById(user);//根据主键id修改

System.out.println(update);

}

/*打印输出的日志

==> Preparing: UPDATE user SET name=?, age=?, email=? WHERE id=?

==> Parameters: 一颗小土豆(String), 24(Integer), 2426712259@qq.com(String), 6(Long)

<== Updates: 1

*/

//这里我们只修改name不修改其他的参数

@Test

public void UpdateTest2() {

User user = new User();

user.setId(6L);

user.setName("一颗小辣椒");

// user.setAge(24);

// user.setEmail("2426712259@qq.com");

int update = userMapper.updateById(user);

System.out.println(update);

}

/*

==> Preparing: UPDATE user SET name=? WHERE id=?

==> Parameters: 一颗小辣椒(String), 6(Long)

<== Updates: 1

*/

发现:

通过两个修改案例,我们发现mybatis-plus为我们实现的方法是动态SQL

自动填充

创建时间,修改时间,这些操作都是自动化完成的,我们不希望手动更新。

阿里巴巴开发手册:所有的数据库表都必须配置这两个字段:gmt_create、gmt_modified ,而且需要自动化。

方法一:数据库级别

1、修改数据库

gmt_create/create_time : datetime 添加默认值:CURRENT_TIMESTAMP

gmt_modified/update_time : datetime 添加默认值:CURRENT_TIMESTAMP 勾选根据当前时间戳跟新

a8a7907a9752c7b511761df799cdc820.png

2、再次测试,同步实体类代码

private Date updateTime;

private Date createTime;

我们插入一条新的数据,create_time和update_time 都已经帮我们生成了

b49ef06996eecc660fe0a1a483a2c683.png

我们执行修改操作并不手动更新时间,查看update_time是否发生变化

@Test

public void UpdateTest() {

//这里并没有修改时间

User user = new User();

user.setId(7L);

user.setName("一颗小辣椒");

int update = userMapper.updateById(user);

System.out.println(update);

}

执行结果,时间发生了变化。

917fa55f999beee7430f3b81a1b6716f.png

方法二:代码级别

代码的自动填充

1.删除数据库的默认值

2.使用mybatis-plus注解

@TableField( fill = FieldFill.INSERT)

private Date createTime;

@TableField( fill = FieldFill.INSERT_UPDATE)

private Date updateTime;

3.创建自定义监听

mybatis-plus 3.3.0版本之前:

@Slf4j

@Component

public class MyMetaObjectHandler implements MetaObjectHandler {

//插入时填充策略

@Override

public void insertFill(MetaObject metaObject) {

log.info("start insert fill");

this.setFieldValByName("createTime",new Date(),metaObject);

this.setFieldValByName("updateTime",new Date(),metaObject);

}

//更新时填充策略

@Override

public void updateFill(MetaObject metaObject) {

log.info("start update fill");

this.setFieldValByName("updateTime",new Date(),metaObject);

}

}

mybatis-plus 3.3.0版本之后:

@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()); // 起始版本 3.3.0(推荐使用)

this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)

/* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) */

//this.setFieldValByName("operator", "Jerry", metaObject);

//this.setInsertFieldValByName("operator", "Jerry", metaObject);

}

@Override

public void updateFill(MetaObject metaObject) {

log.info("start update fill ....");

this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)

this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)

/* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) */

//this.setFieldValByName("operator", "Tom", metaObject);

//this.setUpdateFieldValByName("operator", "Tom", metaObject);

}

}

4.插入测试

//测试插入

@Test

public void testInert(){

User user = new User();

user.setName("一根小黄瓜");

user.setAge(24);

user.setEmail("15542256441@qq.com");

int insert = userMapper.insert(user);

System.out.println(insert);

}

/*

==> Preparing: INSERT INTO user ( name, age, email, create_time, update_time ) VALUES ( ?, ?, ?, ?, ? )

==> Parameters: 一根小黄瓜(String), 24(Integer), 15542256441@qq.com(String), 2020-05-28 15:24:06.774(Timestamp), 2020-05-28 15:24:06.774(Timestamp)

<== Updates: 1

*/

测试结果 create_time&update_time 都已经生成

139b2d60bb5faced0baced5ff1faac8f.png

5.更新测试

//修改测试

@Test

public void UpdateTest() {

User user = new User();

user.setId(8L);

user.setName("一坨小西瓜");

int update = userMapper.updateById(user);

System.out.println(update);

}

/*

==> Preparing: UPDATE user SET name=?, update_time=? WHERE id=?

==> Parameters: 一坨小西瓜(String), 2020-05-28 15:28:17.975(Timestamp), 8(Long)

<== Updates: 1

*/

结果:update_time发生变化

70aa89a4b890eeb40ef7efbd207e8e0d.png

官方的自动填充的文档

自动填充功能

示例工程:

mybatis-plus-sample-auto-fill-metainfo

实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!

public class User {

// 注意!这里需要标记为填充字段

@TableField(.. fill = FieldFill.INSERT)

private String fillField;

....

}

自定义实现类 MyMetaObjectHandler

@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()); // 起始版本 3.3.0(推荐使用)

this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)

/* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) */

//this.setFieldValByName("operator", "Jerry", metaObject);

//this.setInsertFieldValByName("operator", "Jerry", metaObject);

}

@Override

public void updateFill(MetaObject metaObject) {

log.info("start update fill ....");

this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)

this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)

/* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) */

//this.setFieldValByName("operator", "Tom", metaObject);

//this.setUpdateFieldValByName("operator", "Tom", metaObject);

}

}

注意事项:

字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段

填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入

要想根据注解FieldFill.xxx和字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法

不需要根据任何来区分可以使用父类的fillStrategy方法

public enum FieldFill {

/**

* 默认不处理

*/

DEFAULT,

/**

* 插入填充字段

*/

INSERT,

/**

* 更新填充字段

*/

UPDATE,

/**

* 插入和更新填充字段

*/

INSERT_UPDATE

}

乐观锁

再面试过程中,我们经常被问到悲观锁和乐观锁!

乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不去上锁!如果出现问题,再次更新值测试

乐观锁:顾名思义非常悲观,他总是认为总是会出现问题,无论干什么都上锁!再去操作!

主要适用场景

意图:

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

取出记录时,获取当前version

更新时,带上这个version

执行更新时, set version = newVersion where version = oldVersion

如果version不对,就更新失败

乐观锁:1,先查询,获得版本号 version = 1

-- A线程 修改用户名

update user set name = "一颗小土豆" ,version = version + 1

where id = 2 and version = 1

-- B线程 抢先完成修改,这个时候 version = 2 ,会导致 A线程修改失败

update user set name = "一堆小土豆" ,version = version + 1

where id = 2 and version = 1

测试MP 乐观锁插件

1.添加 代码实体类和数据表 的字段 version

/*添加注解开启客观锁*/

@Version

private Integer version;

ee58dd75e10880dc5ac073241fccc9b3.png

2.创建Mybatis-Plus配置文件,开启乐观锁

@Configuration

public class MybatisPlusConfig {

@Bean

public OptimisticLockerInterceptor optimisticLockerInterceptor() {

return new OptimisticLockerInterceptor();

}

}

3.测试

测试乐观锁 成功的操作

/*测试乐观锁 成功的操作*/

@Test

public void testOptimsticLocker(){

//1、查询用户信息

User user = userMapper.selectById(8L);

//2、修改用户信息

user.setName("一堆小西瓜");

//3、执行更新操作

userMapper.updateById(user);

}

/*

==> Preparing: UPDATE user SET name=?, age=?, email=?, create_time=?, update_time=?, version=? WHERE id=? AND version=?

==> Parameters: 一堆小西瓜222222(String), 24(Integer), 15542256441@qq.com(String), 2020-05-28 15:24:07.0(Timestamp), 2020-05-28 16:29:41.583(Timestamp), 3(Integer), 8(Long), 2(Integer)

<== Updates: 1

*/

测试乐观锁 失败的操作

/*测试乐观锁 失败的操作*/

@Test

public void testOptimsticLocker2(){

//模拟两个线程同时操作

//线程一 没来得及提交,被线程二抢先

User user = userMapper.selectById(8L);

user.setName("一堆小西瓜1111111");

//线程二,

User user2 = userMapper.selectById(8L);

user2.setName("一堆小西瓜222222");

userMapper.updateById(user2);

userMapper.updateById(user);

}

/*执行结果

==> Preparing: UPDATE user SET name=?, age=?, email=?, create_time=?, update_time=?, version=? WHERE id=? AND version=?

==> Parameters: 一堆小西瓜222222(String), 24(Integer), 15542256441@qq.com(String), 2020-05-28 15:24:07.0(Timestamp), 2020-05-28 16:29:41.583(Timestamp), 3(Integer), 8(Long), 2(Integer)

<== Updates: 1 线程二 执行成功

==> Preparing: UPDATE user SET name=?, age=?, email=?, create_time=?, update_time=?, version=? WHERE id=? AND version=?

==> Parameters: 一堆小西瓜1111111(String), 24(Integer), 15542256441@qq.com(String), 2020-05-28 15:24:07.0(Timestamp), 2020-05-28 16:29:41.669(Timestamp), 3(Integer), 8(Long), 2(Integer)

<== Updates: 0 线程一 执行失败

*/

查询操作

1、根据全部

@Test

public void userMapperTest1(){

List users = userMapper.selectList(null);//条件为null

users.forEach(System.out::println);

}

2.根据id查询

@Test

public void testSelect(){

User user = userMapper.selectById(7);

System.out.println(user);

}

2.使用多个Id 查询

@Test

public void selectByIds(){

List userList = userMapper.selectBatchIds(Arrays.asList(7L, 8L, 9L));

userList.forEach(System.out::println);

}

/*

==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id IN ( ? , ? , ? )

==> Parameters: 7(Long), 8(Long), 9(Long)

<== Columns: id, name, age, email, create_time, update_time, version

<== Row: 7, 一颗小辣椒, 24, 15542256441@qq.com, 2020-05-28 07:00:32, 2020-05-28 06:55:47, 1

<== Row: 8, 一堆小西瓜222222, 24, 15542256441@qq.com, 2020-05-28 07:24:07, 2020-05-28 08:29:42, 3

<== Row: 9, 一颗小土豆, 25, 242642258@qq.com, 2020-05-28 08:04:42, 2020-05-28 08:04:42, 1

<== Total: 3

*/

4.使用map多条件查询

@Test

public void selectByMap(){

HashMap map = new HashMap<>();

map.put("name","一颗小土豆");

map.put("age",23);

List userList = userMapper.selectByMap(map);

userList.forEach(System.out::println);

}

/*

==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE name = ? AND age = ?

==> Parameters: 一颗小土豆(String), 23(Integer)

<== Columns: id, name, age, email, create_time, update_time, version

<== Row: 8, 一颗小土豆, 23, 15542256441@qq.com, 2020-05-28 07:24:07, 2020-05-28 08:29:42, 3

<== Total: 1

*/

分页查询

分页在网站中使用的十分之多!

1、原始的limit 分页

2、pageHelper 分页插件

3、MyBatis-Plus内置分页插件

MyBatis-Plus内置分页插件 如何使用!

1、配置拦截器组件即可

//Spring boot方式

@Configuration

@MapperScan("com.hxp.ssmkert.mapper")//扫描mapper

@EnableTransactionManagement//开启事务 事务管理器

public class MybatisPlusConfig {

/**

* 分页插件

*/

@Bean

public PaginationInterceptor paginationInterceptor() {

return new PaginationInterceptor();

}

/**

* 乐观锁插件

*/

@Bean

public OptimisticLockerInterceptor optimisticLockerInterceptor() {

return new OptimisticLockerInterceptor();

}

}

2、测试代码

//分页查询 查询第一页,显示5条数据

@Test

public void selectByPage(){

Page page = new Page<>(1,5);//pageNum,PageSize

userMapper.selectPage(page,null);

page.getRecords().forEach(System.out::println);

}

/*

==> Parameters:

<== Columns: id, name, age, email, create_time, update_time, version

<== Row: 1, Jone, 18, test1@baomidou.com, 2020-05-28 06:55:03, 2020-05-28 06:55:03, 1

<== Row: 2, Jack, 20, test2@baomidou.com, 2020-05-28 06:55:03, 2020-05-28 06:55:03, 1

<== Row: 3, Tom, 28, test3@baomidou.com, 2020-05-28 06:55:03, 2020-05-28 06:55:03, 1

<== Row: 4, Sandy, 21, test4@baomidou.com, 2020-05-28 06:55:03, 2020-05-28 06:55:03, 1

<== Row: 5, Billie, 24, test5@baomidou.com, 2020-05-28 06:55:03, 2020-05-28 06:55:03, 1

<== Total: 5

*/

删除操作

物理删除

1、通过id删除

//根据id删除

@Test

public void deleteById(){

int i = userMapper.deleteById(9L);

}

2、根据id删除多个

//根据id删除多个

@Test

public void deleteByIds(){

userMapper.deleteBatchIds(Arrays.asList(7L,8L,9L));

}

逻辑删除

1.添加字段,使用注解(数据库添加相应的字段)

@TableLogic//逻辑删除

private Integer deleted;

2.在配置文件中 开启逻辑删除组件

@Configuration

@MapperScan("com.hxp.ssmkert.mapper")//扫描mapper

@EnableTransactionManagement//开启事务 事务管理器

public class MybatisPlusConfig {

/**

*逻辑删除组件

*/

@Bean

public ISqlInjector sqlInjector(){

return new LogicSqlInjector();

}

}

3、逻辑删除

//根据id删除

@Test

public void deleteById2(){

int i = userMapper.deleteById(8L);

}

/*

==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0

==> Parameters: 8(Long)

<== Updates: 1 我们可以发现现在他走的是修改,不再是删除了

*/

结果:

addd75b301367ac7616b037b5f0675fd.png

性能分析插件

我们在平时的开发中,会遇到一些慢sql。测试!druid

作用:性能分析拦截器,用于输出每条SQL语句及其执行时间

MP也提供性能分析插件,如果超过这个时间就停止运行!

1、导入插件

/**

* SQL执行效率插件

*/

@Bean

@Profile({"dev","test"})// 设置 dev test 环境开启

public PerformanceInterceptor performanceInterceptor() {

PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();

performanceInterceptor.setMaxTime(500);//设置sql执行的最大时间 500毫秒

performanceInterceptor.setFormat(true);//开启sql格式化

return performanceInterceptor;

}

2、查询测试

@Test

public void selectByPage(){

Page page = new Page<>(1,5);

userMapper.selectPage(page,null);

page.getRecords().forEach(System.out::println);

}

/*

==> Parameters:

<== Columns: id, name, age, email, version, deleted, create_time, update_time

<== Row: 1, Jone, 18, test1@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 2, Jack, 20, test2@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 3, Tom, 28, test3@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 4, Sandy, 21, test4@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 5, Billie, 24, test5@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Total: 5

Time:50 ms 显示执行时间 只要超过我们指定的时间就会报错

显示格式化的SQL语句

Execute SQL:

SELECT

id,

name,

age,

email,

version,

deleted,

create_time,

update_time

FROM

user

WHERE

deleted=0 LIMIT 0,5

*/

条件构造器(非常重要)

1、allEq

测试一:null2IsNull 为 true 时

@Test

public void allEqTest1(){

/**

* allEq 第二个参数 boolean null2IsNull 设为true,默认为true,可以不填

*/

QueryWrapper wrapper = new QueryWrapper<>();

HashMap map = new HashMap<>();

map.put("age",18);

map.put("name",null);

wrapper.allEq(map);

List users = userMapper.selectList(wrapper);

users.forEach(System.out::println);

/**

* Execute SQL:

* SELECT

* *

* FROM

* user

* WHERE

* deleted=0

* AND name IS NULL 这里我们发现,当我们的参数为null时,查询这个参数为null的。一般我们开发一个多个条件,当参数为null时应该是不查询该参数

* AND age = 18

*/

}

测试二:null2IsNull 为 false 时

@Test

public void allEqTest2(){

/**

* allEq 第二个参数 boolean null2IsNull 设为 false

*/

QueryWrapper wrapper = new QueryWrapper<>();

HashMap map = new HashMap<>();

map.put("age",18);

map.put("name",null);

wrapper.allEq(map,false);

List users = userMapper.selectList(wrapper);

users.forEach(System.out::println);

/*

Execute SQL:

SELECT

*

FROM

user

WHERE

deleted=0

AND age = 18 看这里的查询条件我们会发现,当参数为null时,他会过滤掉该条件

*/

}

官文:

allEq(Map params)

allEq(Map params, boolean null2IsNull)

allEq(boolean condition, Map params, boolean null2IsNull)

全部eq(或个别isNull)

个别参数说明:

params : key为数据库字段名,value为字段值

null2IsNull : 为true则在map的value为null时调用 isNull 方法,为false时则忽略value为null的

例1: allEq({id:1,name:"老王",age:null})—>id = 1 and name = '老王' and age is null

例2: allEq({id:1,name:"老王",age:null}, false)—>id = 1 and name = '老王'

allEq(BiPredicate filter, Map params)

allEq(BiPredicate filter, Map params, boolean null2IsNull)

allEq(boolean condition, BiPredicate filter, Map params, boolean null2IsNull)

个别参数说明:

filter : 过滤函数,是否允许字段传入比对条件中

params 与 null2IsNull : 同上

例1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})—>name = '老王' and age is null

例2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)—>name = '老王'

2、eq 字符的比较 等于 =

eq(R column, Object val)

eq(boolean condition, R column, Object val)

例: eq("name", "老王")—>name = '老王'

测试:

//测试等于 =

@Test

public void eqTest(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.eq("name","一颗小土豆");

User user = userMapper.selectOne(wrapper);//selectOne() 查询一个

System.out.println(user);

/**

* ==> Preparing: SELECT * FROM user WHERE deleted=0 AND name = ? 一个简单的字符串比较 `=`

* ==> Parameters: 一颗小土豆(String)

*/

}

3、 ne 不等于

例: ne("name", "老王")—>name <> '老王'

测试:

@Test

public void eqTest2(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.ne("name","一颗小土豆");//不等于 name != '一颗小土豆'

List objects = userMapper.selectObjs(wrapper);//查询object集合

objects.forEach(System.out::println);

/*

SELECT * FROM user WHERE deleted=0 AND name <> ?

==> Parameters: 一颗小土豆(String)

<== Columns: id, name, age, email, version, deleted, create_time, update_time

<== Row: 1, Jone, 18, test1@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 2, Jack, 20, test2@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 3, Tom, 28, test3@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 4, Sandy, 18, test4@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 5, Billie, 24, test5@baomidou.com, 1, 0, 2020-05-28 06:55:03, 2020-05-28 06:55:03

<== Row: 7, 一颗小辣椒, 24, 15542256441@qq.com, 1, 0, 2020-05-28 07:00:32, 2020-05-28 06:55:47

<== Total: 6

*/

}

4、大于gt、小于 lt ,大于等于 ge、小于等于 le

测试

/* gt 大于 */

@Test

public void geTest(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.gt("age",24);

List> mapList = userMapper.selectMaps(wrapper);

mapList.forEach(System.out::println);

/*

* SELECT * FROM user WHERE deleted=0 AND age > ?

*/

}

/* 小于同理 */

5、between BETWEEN 值1 AND 值2

/* between between and */

@Test

public void betweenTest(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.between("age",18,24);

List users = userMapper.selectList(wrapper);

users.forEach(System.out::println);

}

6、notBetween 对应的

/* notBetween */

@Test

public void notBetween(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.notBetween("age",18,24);

userMapper.selectList(wrapper).forEach(System.out::println);

/**

* SELECT * FROM user WHERE deleted=0 AND age NOT BETWEEN ? AND ?

*/

}

7、like 语句

/**

* like 普通的like 两边都查找

*/

@Test

public void likeTest(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.like("name","小");

userMapper.selectList(wrapper).forEach(System.out::println);

/**

* SELECT * FROM ser WHERE deleted=0 AND name LIKE '%小%'

*/

}

8、notLike 语句

/**

* notLike

*/

@Test

public void notLike(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.notLike("name","小");

userMapper.selectList(wrapper).forEach(System.out::println);

/**

* SELECT * FROM user WHERE deleted=0 AND name NOT LIKE '%小%'

*/

}

9、likeLeft 语句

/**

* liveLeft

*/

@Test

public void likeLeft(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.likeLeft("name","土");

userMapper.selectList(wrapper).forEach(System.out::println);

/**

* SELECT id,name,age,email,version,deleted,create_time,update_time FROM user WHERE deleted=0 AND name LIKE '%土'

*/

}

10、likeRight 语句

/**

* liveLeft

*/

@Test

public void likeRight(){

QueryWrapper wrapper = new QueryWrapper<>();

wrapper.likeRight("name","T");

userMapper.selectList(wrapper).forEach(System.out::println);

/**

* SELECT id,name,age,email,version,deleted,create_time,update_time FROM user WHERE deleted=0 AND name LIKE 'T%'

*/

}

代码生成器

AutoGenerator 是一个Mybatis-Plus 的代码生成器,通过AutoGenerator可以快速的生成Entity、Mapper、Mapper XML、service、Controller等各个模块代码,极大地提升了开发效率。

package com.hxp.ssmkert.test;

import com.baomidou.mybatisplus.annotation.DbType;

import com.baomidou.mybatisplus.annotation.FieldFill;

import com.baomidou.mybatisplus.annotation.IdType;

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.po.TableFill;

import com.baomidou.mybatisplus.generator.config.rules.DateType;

import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;

/**

* \* Created with IntelliJ IDEA.

* \* User: 一颗小土豆

* \* Date: 2020/5/30

* \* Time: 11:25

* \* mybatis-plus代码生成器

*/

public class MyAutoGeneratorTest {

public static void main(String[] args) {

String driverClassName = "com.mysql.cj.jdbc.Driver";

String url = "jdbc:mysql://39.105.27.58/ssmarkert?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT";

String username = "root";

String password = "lovehxp521..";

//1.首先需要创建一个代码生成器对象

AutoGenerator generator = new AutoGenerator();

//2.配置策略

//(1) 全局配置

GlobalConfig gconfig = new GlobalConfig();

gconfig.setAuthor("一颗小土豆");//配置开发人员的名字

gconfig.setSwagger2(true);//开启Swagger2文档

gconfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java");//设置代码输出的路径

gconfig.setOpen(false);//是否打开输出目录

gconfig.setFileOverride(true);//是否覆盖

gconfig.setServiceName("%sService");//去掉service的前缀I

gconfig.setIdType(IdType.AUTO);//设置主键策略

gconfig.setDateType(DateType.ONLY_DATE);//时间类型对应策略,也可以默认

//添加配置

generator.setGlobalConfig(gconfig);

//(2) 设置数据源

DataSourceConfig dsc = new DataSourceConfig();

dsc.setDriverName(driverClassName);

dsc.setDbType(DbType.MYSQL);

dsc.setUrl(url);

dsc.setUsername(username);

dsc.setPassword(password);

generator.setDataSource(dsc);

//(3) 包的配置

PackageConfig packageConfig = new PackageConfig();

packageConfig.setModuleName("test");

packageConfig.setParent("com.hxp.ssmkert");

packageConfig.setMapper("mapper");

packageConfig.setEntity("entity");

packageConfig.setService("service");

packageConfig.setServiceImpl("service.impl");

packageConfig.setController("controller");

//添加包的配置

generator.setPackageInfo(packageConfig);

//(4) 策略配置

StrategyConfig strategyConfig = new StrategyConfig();

strategyConfig.setInclude("user");//要映射的表名 ,可以传递多个

strategyConfig.setNaming(NamingStrategy.underline_to_camel);//设置驼峰命名

strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 设置驼峰命名

strategyConfig.setEntityLombokModel(true);//启用lombok

strategyConfig.setLogicDeleteFieldName("deleted");//逻辑删除

//自动填充

TableFill createTime = new TableFill("create_time", FieldFill.INSERT);

TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);

ArrayList tableFields = new ArrayList<>();

tableFields.add(createTime);

tableFields.add(updateTime);

//设置自动填充

strategyConfig.setTableFillList(tableFields);

//设置乐观锁

strategyConfig.setVersionFieldName("version");

//生成 @RestController 控制器

strategyConfig.setRestControllerStyle(true);

//驼峰转连字符 localhost:8080/hello_id_2

strategyConfig.setControllerMappingHyphenStyle(true);

//数据库表配置

generator.setStrategy(strategyConfig);

//3.执行代码生成器

generator.execute();

}

}

Mybatis、Mybatis-Plus二级缓存使用

注意点:

在最新的3.x版本,实现二级缓存的配置也有了一些改变。

官方建议在service使用缓存,但是你也可以直接在mapper层缓存,这里的二级缓存就是直接在Mapper层进行缓存操作

Mybatis的二级缓存实现也十分简单,只要在springboot的配置文件打开二级缓存,即 mybatis-plus:

configuration:

cache-enabled: true

缓存接口的实现 package com.qsmam.weixin.config;

import com.qsmam.weixin.util.SpringUtils;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections.CollectionUtils;

import org.apache.ibatis.cache.Cache;

import org.springframework.dao.DataAccessException;

import org.springframework.data.redis.connection.RedisConnection;

import org.springframework.data.redis.core.RedisCallback;

import org.springframework.data.redis.core.RedisTemplate;

import java.util.Set;

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**

* \* Created with IntelliJ IDEA.

* \* User: 一颗小土豆

* \* Date: 2020/7/23

* \* Time: 10:53

* \* 配置mybatis二级缓存 保存到redis

*/

@Slf4j

public class MybatisRedisCache implements Cache {

//读写锁

private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);

//RedisTemplate

private RedisTemplate redisTemplate;

{

//RedisTemplate不能使用自动注入,所以使用工具类 BeanFactoryPostProcessor获得

this.redisTemplate = SpringUtils.getBean(RedisTemplate.class);

}

private String id;

public MybatisRedisCache(final String id){

if (id == null){

throw new IllegalArgumentException("Cache instances require an ID");

}

this.id = id;

}

@Override

public String getId() {

return this.id;

}

@Override

public void putObject(Object key, Object value) {

if (redisTemplate == null){

//犹豫启动期间注入失败,只能运行期间注入,这段代码可以删除

//redisTemplate = (RedisTemplate) ApplicationContextRegister.getApplicationContext().getBean("RedisTemplate");

}

if (value != null){

redisTemplate.opsForValue().set(key.toString(),value);

}

}

@Override

public Object getObject(Object key) {

try {

return redisTemplate.opsForValue().get(key.toString());

}catch (Exception e){

log.error("缓存出错!");

log.error(e.getMessage());

}

return null;

}

/**

* 删除指定的缓存

* @param key

* @return

*/

@Override

public Object removeObject(Object key) {

if (key!=null)

redisTemplate.delete(key.toString());

return null;

}

/**

* 删除全部 缓存

*/

@Override

public void clear() {

log.debug("清楚缓存");

if (redisTemplate!=null){

Set keys = redisTemplate.keys("*:" + this.id + "*");

if (!CollectionUtils.isEmpty(keys)){

redisTemplate.delete(keys);

}

}

}

@Override

public int getSize() {

return (int) redisTemplate.execute(new RedisCallback() {

public Long doInRedis(RedisConnection connection) throws DataAccessException {

return connection.dbSize();

}

});

}

@Override

public ReadWriteLock getReadWriteLock() {

return this.readWriteLock;

}

}

Spring工具类 方便在非spring管理环境中获取bean package com.qsmam.weixin.util;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import org.springframework.stereotype.Component;

/**

* \* Created with IntelliJ IDEA.

* \* User: 一颗小土豆

* \* Date: 2020/7/23

* \* Time: 11:57

* \spring工具类 方便在非spring管理环境中获取bean

*/

@Component

public class SpringUtils implements BeanFactoryPostProcessor {

/** Spring应用上下文环境 */

private static ConfigurableListableBeanFactory beanFactory;

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

SpringUtils.beanFactory = configurableListableBeanFactory;

}

/**

* 获取对象

*

* @param name

* @return Object 一个以所给名字注册的bean的实例

* @throws org.springframework.beans.BeansException

*

*/

@SuppressWarnings("unchecked")

public static T getBean(String name) throws BeansException

{

return (T) beanFactory.getBean(name);

}

/**

* 获取类型为requiredType的对象

*

* @param clz

* @return

* @throws org.springframework.beans.BeansException

*

*/

public static T getBean(Class clz) throws BeansException

{

T result = (T) beanFactory.getBean(clz);

return result;

}

/**

* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true

*

* @param name

* @return boolean

*/

public static boolean containsBean(String name)

{

return beanFactory.containsBean(name);

}

/**

* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)

*

* @param name

* @return boolean

* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException

*

*/

public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException

{

return beanFactory.isSingleton(name);

}

/**

* @param name

* @return Class 注册对象的类型

* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException

*

*/

public static Class> getType(String name) throws NoSuchBeanDefinitionException

{

return beanFactory.getType(name);

}

/**

* 如果给定的bean名字在bean定义中有别名,则返回这些别名

*

* @param name

* @return

* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException

*

*/

public static String[] getAliases(String name) throws NoSuchBeanDefinitionException

{

return beanFactory.getAliases(name);

}

}

mapper.xml文件声明缓存,这里3.x只需要这样配置

Mapper接口使用注解 @Repository

@CacheNamespace(implementation=MybatisRedisCache.class,eviction=MybatisRedisCache.class)

public interface CarouselMapper extends BaseMapper{

}

注意:

当我们重新配置过 RedisTemplate 并且交给spring进行管理的时候就需要制定一个主Bean,

方法:使用@Primary注解

@Primary

package com.qsmam.weixin.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;

import com.fasterxml.jackson.annotation.PropertyAccessor;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.cache.CacheManager;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

import org.springframework.data.redis.cache.RedisCacheConfiguration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.serializer.*;

import java.time.Duration;

/**

* \* Created with IntelliJ IDEA.

* \* User: 一颗小土豆

* \* Date: 2020/7/1

* \* Time: 17:14

* \

*/

@Configuration

public class SpringWebConfig {

/**

* 配置reids 序列化 防止乱码等问题

* @param redisConnectionFactory

* @return

*/

@Primary //使用@Primary修饰的bean优先级会更高

@Bean("redisTemplate")

public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory)

{

RedisTemplate template = new RedisTemplate();

template.setConnectionFactory(redisConnectionFactory);

//使用JSON格式的序列化,保存

template.setKeySerializer(new StringRedisSerializer());

template.setHashKeySerializer(new StringRedisSerializer());

template.setValueSerializer(new GenericJackson2JsonRedisSerializer());

template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

return template;

}

//自定义cacheManager缓存管理器

@Bean

public CacheManager cacheManager(RedisConnectionFactory factory)

{

RedisSerializer redisSerializer = new StringRedisSerializer();

Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

//解决查询缓存转换异常的问题

ObjectMapper om = new ObjectMapper();

om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

jackson2JsonRedisSerializer.setObjectMapper(om);

//配置序列化(解决乱码的问题)

RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()

.entryTtl(Duration.ZERO)

.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))

.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))

.disableCachingNullValues();

RedisCacheManager cacheManager = RedisCacheManager.builder(factory)

.cacheDefaults(config)

.build();

return cacheManager;

}

}

若果不指定就会出现以下的错误:↓↓↓↓↓↓

***************************

APPLICATION FAILED TO START

***************************

Description:

file [E:\码云项目\easy_mom_service_number\weixin\target\classes\com\qsmam\weixin\mapper\security\UserInfoMapper.class] required a single bean, but 2 were found: 需要一个单例的Bean,但是发现了2个

- redisTemplate: defined by method 'redisTemplate' in class path resource [com/qsmam/weixin/config/SpringWebConfig.class]

- stringRedisTemplate: defined by method 'stringRedisTemplate' in class path resource [org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

雪花算法

Twitter的分布式自增ID雪花算法snowflake:https://github.com/souyunku/SnowFlake

概述

分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。

有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。

而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。

结构

snowflake的结构如下(每部分用-分开):

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)

snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。

/**

* 描述: Twitter的分布式自增ID雪花算法snowflake (Java版)

*

* @author yanpenglei

* @create 2018-03-13 12:37

**/

public class SnowFlake {

/**

* 起始的时间戳

*/

private final static long START_STMP = 1480166465631L;

/**

* 每一部分占用的位数

*/

private final static long SEQUENCE_BIT = 12; //序列号占用的位数

private final static long MACHINE_BIT = 5; //机器标识占用的位数

private final static long DATACENTER_BIT = 5;//数据中心占用的位数

/**

* 每一部分的最大值

*/

private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);

private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);

private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

/**

* 每一部分向左的位移

*/

private final static long MACHINE_LEFT = SEQUENCE_BIT;

private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;

private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

private long datacenterId; //数据中心

private long machineId; //机器标识

private long sequence = 0L; //序列号

private long lastStmp = -1L;//上一次时间戳

public SnowFlake(long datacenterId, long machineId) {

if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {

throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");

}

if (machineId > MAX_MACHINE_NUM || machineId < 0) {

throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");

}

this.datacenterId = datacenterId;

this.machineId = machineId;

}

/**

* 产生下一个ID

*

* @return

*/

public synchronized long nextId() {

long currStmp = getNewstmp();

if (currStmp < lastStmp) {

throw new RuntimeException("Clock moved backwards. Refusing to generate id");

}

if (currStmp == lastStmp) {

//相同毫秒内,序列号自增

sequence = (sequence + 1) & MAX_SEQUENCE;

//同一毫秒的序列数已经达到最大

if (sequence == 0L) {

currStmp = getNextMill();

}

} else {

//不同毫秒内,序列号置为0

sequence = 0L;

}

lastStmp = currStmp;

return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分

| datacenterId << DATACENTER_LEFT //数据中心部分

| machineId << MACHINE_LEFT //机器标识部分

| sequence; //序列号部分

}

private long getNextMill() {

long mill = getNewstmp();

while (mill <= lastStmp) {

mill = getNewstmp();

}

return mill;

}

private long getNewstmp() {

return System.currentTimeMillis();

}

public static void main(String[] args) {

SnowFlake snowFlake = new SnowFlake(2, 3);

long start = System.currentTimeMillis();

for (int i = 0; i < 1000000; i++) {

System.out.println(snowFlake.nextId());

}

System.out.println(System.currentTimeMillis() - start);

}

}

this.datacenterId = datacenterId;

this.machineId = machineId;

}

/**

* 产生下一个ID

*

* @return

*/

public synchronized long nextId() {

long currStmp = getNewstmp();

if (currStmp < lastStmp) {

throw new RuntimeException("Clock moved backwards. Refusing to generate id");

}

if (currStmp == lastStmp) {

//相同毫秒内,序列号自增

sequence = (sequence + 1) & MAX_SEQUENCE;

//同一毫秒的序列数已经达到最大

if (sequence == 0L) {

currStmp = getNextMill();

}

} else {

//不同毫秒内,序列号置为0

sequence = 0L;

}

lastStmp = currStmp;

return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分

| datacenterId << DATACENTER_LEFT //数据中心部分

| machineId << MACHINE_LEFT //机器标识部分

| sequence; //序列号部分

}

private long getNextMill() {

long mill = getNewstmp();

while (mill <= lastStmp) {

mill = getNewstmp();

}

return mill;

}

private long getNewstmp() {

return System.currentTimeMillis();

}

public static void main(String[] args) {

SnowFlake snowFlake = new SnowFlake(2, 3);

long start = System.currentTimeMillis();

for (int i = 0; i < 1000000; i++) {

System.out.println(snowFlake.nextId());

}

System.out.println(System.currentTimeMillis() - start);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值