!492 新增 OptimisticLockManager,用于处理跳过乐观锁的场景,测试类就文档补充

Merge pull request !492 from tiankafei/optimisticlock
This commit is contained in:
Michael Yang 2024-09-07 03:13:02 +00:00 committed by Gitee
commit 6ffc6694ac
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
10 changed files with 237 additions and 6 deletions

View File

@ -418,7 +418,7 @@ public class ClickhouseDialectImpl extends CommonsDialectImpl {
// 乐观锁字段
String versionColumn = tableInfo.getVersionColumn();
if (StringUtil.isNotBlank(versionColumn)) {
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
}
@ -491,7 +491,7 @@ public class ClickhouseDialectImpl extends CommonsDialectImpl {
// 乐观锁字段
String versionColumn = tableInfo.getVersionColumn();
if (StringUtil.isNotBlank(versionColumn)) {
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
}

View File

@ -842,7 +842,7 @@ public class CommonsDialectImpl implements IDialect {
// 乐观锁字段
String versionColumn = tableInfo.getVersionColumn();
if (StringUtil.isNotBlank(versionColumn)) {
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
}
@ -868,7 +868,7 @@ public class CommonsDialectImpl implements IDialect {
tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
// 乐观锁条件
if (StringUtil.isNotBlank(versionColumn)) {
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn);
if (versionValue == null) {
throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);
@ -915,7 +915,7 @@ public class CommonsDialectImpl implements IDialect {
// 乐观锁字段
String versionColumn = tableInfo.getVersionColumn();
if (StringUtil.isNotBlank(versionColumn)) {
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.optimisticlock;
import java.util.function.Supplier;
/**
* 乐观锁管理器
*/
public class OptimisticLockManager {
private OptimisticLockManager() {
}
private static final ThreadLocal<Boolean> skipFlags = new ThreadLocal<>();
/**
* 跳过乐观锁字段处理直接进行数据库物理操作
*/
public static <T> T execWithoutOptimisticLock(Supplier<T> supplier) {
try {
skipOptimisticLock();
return supplier.get();
} finally {
restoreOptimisticLock();
}
}
/**
* 跳过乐观锁字段处理直接进行数据库物理操作
*/
public static void execWithoutOptimisticLock(Runnable runnable) {
try {
skipOptimisticLock();
runnable.run();
} finally {
restoreOptimisticLock();
}
}
/**
* 跳过乐观锁字段处理
*/
public static void skipOptimisticLock() {
skipFlags.set(Boolean.TRUE);
}
/**
* 恢复乐观锁字段处理
*/
public static void restoreOptimisticLock() {
skipFlags.remove();
}
/**
* 获取乐观锁列返回 {@code null} 表示跳过乐观锁
*
* @param optimisticLockColumn 乐观锁列
* @return 乐观锁列
*/
public static String getOptimisticLockColumn(String optimisticLockColumn) {
if (optimisticLockColumn == null) {
return null;
}
Boolean skipFlag = skipFlags.get();
if (skipFlag == null) {
return optimisticLockColumn;
}
return skipFlag ? null : optimisticLockColumn;
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 乐观锁相关功能
*/
package com.mybatisflex.core.optimisticlock;

View File

@ -30,6 +30,7 @@ import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.exception.locale.LocalizedFormats;
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
import com.mybatisflex.core.mybatis.TypeHandlerObject;
import com.mybatisflex.core.optimisticlock.OptimisticLockManager;
import com.mybatisflex.core.query.Brackets;
import com.mybatisflex.core.query.CPI;
import com.mybatisflex.core.query.Join;
@ -241,6 +242,10 @@ public class TableInfo {
this.logicDeleteColumn = logicDeleteColumn;
}
public String getOptimisticLockColumnOrSkip() {
return OptimisticLockManager.getOptimisticLockColumn(versionColumn);
}
public String getVersionColumn() {
return versionColumn;
}
@ -884,7 +889,7 @@ public class TableInfo {
}
// 添加乐观锁条件只有在 update 的时候进行处理
if (StringUtil.isNotBlank(versionColumn) && entity != null) {
if (StringUtil.isNotBlank(getOptimisticLockColumnOrSkip()) && entity != null) {
Object versionValue = buildColumnSqlArg(entity, versionColumn);
if (versionValue == null) {
throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);

View File

@ -54,6 +54,9 @@ public class Account extends BaseEntity implements Serializable, AgeAware {
// @Column(isLogicDelete = true)
private Boolean isDelete;
@Column(version = true)
private Integer version;
private List<Article> articles;
@Column(ignore = true)
@ -125,6 +128,14 @@ public class Account extends BaseEntity implements Serializable, AgeAware {
isDelete = delete;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public List<Article> getArticles() {
return articles;
}
@ -152,6 +163,7 @@ public class Account extends BaseEntity implements Serializable, AgeAware {
", birthday=" + birthday +
", options=" + options +
", isDelete=" + isDelete +
", version=" + version +
", articles=" + articles +
", title='" + title + '\'' +
'}';

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.test;
import com.mybatisflex.core.MybatisFlexBootstrap;
import com.mybatisflex.core.audit.AuditManager;
import com.mybatisflex.core.audit.ConsoleMessageCollector;
import com.mybatisflex.core.audit.MessageCollector;
import com.mybatisflex.core.optimisticlock.OptimisticLockManager;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
public class OptimisticLockTestStarter {
public static void main(String[] args) {
DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("schema_optimisticlock.sql")
.addScript("data_optimisticlock.sql").setScriptEncoding("UTF-8")
.build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()
.setDataSource(dataSource)
.addMapper(AccountMapper.class)
.start();
//开启审计功能
AuditManager.setAuditEnable(true);
//设置 SQL 审计收集器
MessageCollector collector = new ConsoleMessageCollector();
AuditManager.setMessageCollector(collector);
AccountMapper accountMapper = bootstrap.getMapper(AccountMapper.class);
accountMapper.selectAll().forEach(System.out::println);
System.out.println(">>>>>>>>>>>>>>>update id=1 user_name from 张三 to 张三1");
Account account = new Account();
account.setId(1L);
account.setUserName("张三1");
OptimisticLockManager.execWithoutOptimisticLock(() -> accountMapper.update(account));
accountMapper.selectAll().forEach(System.out::println);
}
}

View File

@ -0,0 +1,3 @@
INSERT INTO tb_account
VALUES (1, '张三', 18, 0,'2020-01-11', '{"key":"value1"}',0,0),
(2, '王麻子叔叔', 19, 1, '2021-03-21', '{"key":"value2"}',0,0);

View File

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS `tb_account`
(
`id` INTEGER auto_increment,
`user_name` VARCHAR(100),
`age` Integer,
`sex` Integer,
`birthday` DATETIME,
`options` VARCHAR(1024),
`is_delete` Integer,
`version` Integer
);

View File

@ -451,6 +451,40 @@ QueryWrapper queryWrapper = QueryWrapper.create()
> 更多关于 MyBatis-Flex APT 的配置,请点击 [这里](./docs/zh/others/apt.md)。
## 乐观锁
### 乐观锁配置
```java
@Table(value = "tb_account", dataSource = "ds2", onSet = AccountOnSetListener.class)
public class Account extends BaseEntity implements Serializable, AgeAware {
......
@Column(version = true)
private Integer version;
}
```
### 跳过乐观锁的使用
```java
AccountMapper accountMapper = bootstrap.getMapper(AccountMapper.class);
accountMapper.selectAll().forEach(System.out::println);
System.out.println(">>>>>>>>>>>>>>>update id=1 user_name from 张三 to 张三1");
Account account = new Account();
account.setId(1L);
account.setUserName("张三1");
// 跳过乐观锁
OptimisticLockManager.execWithoutOptimisticLock(() -> accountMapper.update(account));
accountMapper.selectAll().forEach(System.out::println);
```
## Db + Row 工具类
Db + Row 工具类,提供了在 Entity 实体类之外的数据库操作能力。使用 Db + Row 时,无需对数据库表进行映射, Row 是一个 HashMap 的子类,相当于一个通用的 Entity。以下为 Db + Row 的一些示例: