mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-08 17:48:25 +08:00
!492 新增 OptimisticLockManager,用于处理跳过乐观锁的场景,测试类就文档补充
Merge pull request !492 from tiankafei/optimisticlock
This commit is contained in:
commit
6ffc6694ac
@ -418,7 +418,7 @@ public class ClickhouseDialectImpl extends CommonsDialectImpl {
|
|||||||
|
|
||||||
// 乐观锁字段
|
// 乐观锁字段
|
||||||
String versionColumn = tableInfo.getVersionColumn();
|
String versionColumn = tableInfo.getVersionColumn();
|
||||||
if (StringUtil.isNotBlank(versionColumn)) {
|
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
|
||||||
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,7 +491,7 @@ public class ClickhouseDialectImpl extends CommonsDialectImpl {
|
|||||||
|
|
||||||
// 乐观锁字段
|
// 乐观锁字段
|
||||||
String versionColumn = tableInfo.getVersionColumn();
|
String versionColumn = tableInfo.getVersionColumn();
|
||||||
if (StringUtil.isNotBlank(versionColumn)) {
|
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
|
||||||
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -842,7 +842,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
// 乐观锁字段
|
// 乐观锁字段
|
||||||
String versionColumn = tableInfo.getVersionColumn();
|
String versionColumn = tableInfo.getVersionColumn();
|
||||||
if (StringUtil.isNotBlank(versionColumn)) {
|
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
|
||||||
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,7 +868,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
|
tableInfo.buildTenantCondition(sql, tenantIdArgs, this);
|
||||||
|
|
||||||
// 乐观锁条件
|
// 乐观锁条件
|
||||||
if (StringUtil.isNotBlank(versionColumn)) {
|
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
|
||||||
Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn);
|
Object versionValue = tableInfo.buildColumnSqlArg(entity, versionColumn);
|
||||||
if (versionValue == null) {
|
if (versionValue == null) {
|
||||||
throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);
|
throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);
|
||||||
@ -915,7 +915,7 @@ public class CommonsDialectImpl implements IDialect {
|
|||||||
|
|
||||||
// 乐观锁字段
|
// 乐观锁字段
|
||||||
String versionColumn = tableInfo.getVersionColumn();
|
String versionColumn = tableInfo.getVersionColumn();
|
||||||
if (StringUtil.isNotBlank(versionColumn)) {
|
if (StringUtil.isNotBlank(tableInfo.getOptimisticLockColumnOrSkip())) {
|
||||||
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
stringJoiner.add(wrap(versionColumn) + EQUALS + wrap(versionColumn) + " + 1 ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
||||||
@ -30,6 +30,7 @@ import com.mybatisflex.core.exception.FlexExceptions;
|
|||||||
import com.mybatisflex.core.exception.locale.LocalizedFormats;
|
import com.mybatisflex.core.exception.locale.LocalizedFormats;
|
||||||
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
|
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
|
||||||
import com.mybatisflex.core.mybatis.TypeHandlerObject;
|
import com.mybatisflex.core.mybatis.TypeHandlerObject;
|
||||||
|
import com.mybatisflex.core.optimisticlock.OptimisticLockManager;
|
||||||
import com.mybatisflex.core.query.Brackets;
|
import com.mybatisflex.core.query.Brackets;
|
||||||
import com.mybatisflex.core.query.CPI;
|
import com.mybatisflex.core.query.CPI;
|
||||||
import com.mybatisflex.core.query.Join;
|
import com.mybatisflex.core.query.Join;
|
||||||
@ -241,6 +242,10 @@ public class TableInfo {
|
|||||||
this.logicDeleteColumn = logicDeleteColumn;
|
this.logicDeleteColumn = logicDeleteColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOptimisticLockColumnOrSkip() {
|
||||||
|
return OptimisticLockManager.getOptimisticLockColumn(versionColumn);
|
||||||
|
}
|
||||||
|
|
||||||
public String getVersionColumn() {
|
public String getVersionColumn() {
|
||||||
return versionColumn;
|
return versionColumn;
|
||||||
}
|
}
|
||||||
@ -884,7 +889,7 @@ public class TableInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加乐观锁条件,只有在 update 的时候进行处理
|
// 添加乐观锁条件,只有在 update 的时候进行处理
|
||||||
if (StringUtil.isNotBlank(versionColumn) && entity != null) {
|
if (StringUtil.isNotBlank(getOptimisticLockColumnOrSkip()) && entity != null) {
|
||||||
Object versionValue = buildColumnSqlArg(entity, versionColumn);
|
Object versionValue = buildColumnSqlArg(entity, versionColumn);
|
||||||
if (versionValue == null) {
|
if (versionValue == null) {
|
||||||
throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);
|
throw FlexExceptions.wrap(LocalizedFormats.ENTITY_VERSION_NULL, entity);
|
||||||
|
|||||||
@ -54,6 +54,9 @@ public class Account extends BaseEntity implements Serializable, AgeAware {
|
|||||||
// @Column(isLogicDelete = true)
|
// @Column(isLogicDelete = true)
|
||||||
private Boolean isDelete;
|
private Boolean isDelete;
|
||||||
|
|
||||||
|
@Column(version = true)
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
private List<Article> articles;
|
private List<Article> articles;
|
||||||
|
|
||||||
@Column(ignore = true)
|
@Column(ignore = true)
|
||||||
@ -125,6 +128,14 @@ public class Account extends BaseEntity implements Serializable, AgeAware {
|
|||||||
isDelete = delete;
|
isDelete = delete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(Integer version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Article> getArticles() {
|
public List<Article> getArticles() {
|
||||||
return articles;
|
return articles;
|
||||||
}
|
}
|
||||||
@ -152,6 +163,7 @@ public class Account extends BaseEntity implements Serializable, AgeAware {
|
|||||||
", birthday=" + birthday +
|
", birthday=" + birthday +
|
||||||
", options=" + options +
|
", options=" + options +
|
||||||
", isDelete=" + isDelete +
|
", isDelete=" + isDelete +
|
||||||
|
", version=" + version +
|
||||||
", articles=" + articles +
|
", articles=" + articles +
|
||||||
", title='" + title + '\'' +
|
", title='" + title + '\'' +
|
||||||
'}';
|
'}';
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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);
|
||||||
@ -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
|
||||||
|
);
|
||||||
34
readme_zh.md
34
readme_zh.md
@ -451,6 +451,40 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
|||||||
|
|
||||||
> 更多关于 MyBatis-Flex APT 的配置,请点击 [这里](./docs/zh/others/apt.md)。
|
> 更多关于 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 工具类
|
||||||
|
|
||||||
Db + Row 工具类,提供了在 Entity 实体类之外的数据库操作能力。使用 Db + Row 时,无需对数据库表进行映射, Row 是一个 HashMap 的子类,相当于一个通用的 Entity。以下为 Db + Row 的一些示例:
|
Db + Row 工具类,提供了在 Entity 实体类之外的数据库操作能力。使用 Db + Row 时,无需对数据库表进行映射, Row 是一个 HashMap 的子类,相当于一个通用的 Entity。以下为 Db + Row 的一些示例:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user