当前位置: 首页 > 编程日记 > 正文

Mybatis-Plus 自动属性填充与自定义Insert into语句顺序&MyBatisPlus中使用 @TableField完成字段自动填充

前言:系统中使用了Mybatis-Plus 自动属性填充为实体统一进行属性的填值,在Mapper的xml 文件中 insert into 语句 使用
<if test="id != null">id,</if> 进行判断会发现该属性是空的,明明已经为改字段进行了属性的自动填充,为什么Mybatis- 在拼接sql 语句时依然认为 改属性是空的呢;

1 问题重现:

1.1 在实体中使用了属性填充属性:

 @TableField(fill = FieldFill.INSERT)
 private String testFiled;

1.2 在拦截器里进行了属性填充:

@Override
public void insertFill(MetaObject metaObject) {
	this.setFieldValByName("testFiled", "test", metaObject);
}

1.3 mapper xml :

 insert into ${prefix}knowledge_authority
 <trim prefix="(" suffix=")" suffixOverrides=",">
   <if test="id != null">id,</if>
   <if test="testFiled != null">test_filed,</if>
  </trim>
 <trim prefix="values (" suffix=")" suffixOverrides=",">
   <if test="id != null">#{id},</if>
    <if test="testFiled != null">#{testFiled },</if>
  </trim>

在对实体id 设置完成之后,进行数据的插入,发现插入的数据中只有id 没有testFiled 属性;

2 推断问题产生的原因:

原因1:属性填充正常,但是在xml sql 语句中,在某些情况下 <if> 判断有问题;

原因2:自定义填充属性有问题,导致想要填充的属性没有被填充值,导致进行 <if> 判断有问题;

愿意3:属性填充和 <if> 标签判断都没有问题,但是sql 拼接的时机 在属性填充之前进行;

<if> 标签只进行简单的空判断,出问题的可能性不大,从原因2 入手:
使用自定义属性填充时,会调用MybatisParameterHandler 类中的process()方法完成属性填充的调用;

 private void process(Object parameter) {
  if (parameter != null) {
       TableInfo tableInfo = null;
       Object entity = parameter;
       if (parameter instanceof Map) {
           Map<?, ?> map = (Map)parameter;
           if (map.containsKey("et")) {
               Object et = map.get("et");
               if (et != null) {
                   entity = et;
                   tableInfo = TableInfoHelper.getTableInfo(et.getClass());
               }
           }
       } else {
           tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());
       }

       if (tableInfo != null) {
           MetaObject metaObject = this.configuration.newMetaObject(entity);
           if (SqlCommandType.INSERT == this.sqlCommandType) {
           		// 插入时 id 的填充
               this.populateKeys(tableInfo, metaObject, entity);
               // 这里会在insert 时 调用我们自己定义的拦截器进行属性的自动填充
               this.insertFill(metaObject, tableInfo);
           } else {
            // 这里会在update 时 调用我们自己定义的拦截器进行属性的自动填充
               this.updateFill(metaObject, tableInfo);
           }
       }
   }

}

通过debug 我们发现,在插入数据时确实调用了process 方法,并对实体完成了属性的填充,属性填充是正常的;所以会不会是原因3 ,属性填充的时机和sql 拼接的时机不同造成的。
如果 先进行了sql的拼接,此时进行 <if> 判断时 发现改属性为空,必然会跳过了该属性的拼接,即使后面自动填充为属性填充了数据,但是由于sql已经完成了拼接,最终执行的sql 也是没有该属性的;

基于此猜想,我们将xml 中 判断标签去掉,只保留占位符:

 insert into ${prefix}knowledge_authority
 <trim prefix="(" suffix=")" suffixOverrides=",">
   <if test="id != null">id,</if>
  	test_filed,
  </trim>
 <trim prefix="values (" suffix=")" suffixOverrides=",">
   <if test="id != null">#{id},</if>
     #{testFiled },
  </trim>

此时在次进行插入,发现插入成功,并且testFiled 属性也是有值的;

3 从Mybatis-Plus 代码层面查看sql 语句的拼接:

3.1 先看下sql 拼接的流程:
MybatisParameterHandler 是 Mybatis 中用于处理数据库操作参数的接口,它的实现类 DefaultParameterHandler 负责将 Java 对象转换为 JDBC 预处理语句需要的参数值,以及将参数值设置到预处理语句中。其中,BoundSql 对象就是用于封装 SQL 语句和对应的参数值的。

BoundSql 对象的赋值过程主要由 SqlSource 和 ParameterMapping 来完成,具体流程如下:

  1. Mybatis 在执行 SQL 语句之前,会根据 Mapper 接口定义的方法和传入的参数生成 MappedStatement 对象。在 MappedStatement 中包含了 SQL 语句、参数映射信息等相关的元数据。
  2. MappedStatement 负责生成 BoundSql 对象。在生成 BoundSql 对象时,Mybatis 会首先根据 SQL 语句和参数信息生成一个 StaticSqlSource 对象,然后再通过它生成一个 DynamicSqlSource 对象。DynamicSqlSource 会根据传入的参数信息和 Mapper 接口定义的 SQL 语句,动态生成最终的 SQL 语句和参数值。这个过程中,ParameterMapping 负责将 Java 对象中的属性值和 SQL 语句中的占位符做映射关联,SqlSource 负责根据参数信息和 SQL 语句生成 BoundSql 对象。
  3. 生成 BoundSql 对象后,Mybatis 会通过 ParameterHandler 将 BoundSql 对象中的 SQL 语句和参数值设置到 JDBC 预处理语句中。默认的 ParameterHandler 实现类是 DefaultParameterHandler,它会通过反射获取 PreparedStatement 对象,并调用 setXxx() 方法将参数值设置到预处理语句中。在设置参数值的过程中,DefaultParameterHandler 会根据 ParameterMapping 中的信息获得 Java 对象中对应属性的值,并将其赋值给 BoundSql 对象中对应的参数占位符。
  4. 综上所述,BoundSql 对象的赋值过程主要由 SqlSource 和 ParameterMapping 来完成。它们会根据传入的参数信息和 Mapper 接口定义的 SQL 语句,动态生成最终的 SQL 语句和参数值,并将它们设置到 BoundSql 对象中。

3.2 MybatisParameterHandler 中的 BoundSql boundSql:

public class MybatisParameterHandler implements ParameterHandler {
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final MappedStatement mappedStatement;
    private final Object parameterObject;
    private final BoundSql boundSql;
    private final Configuration configuration;
    private final SqlCommandType sqlCommandType;

    public MybatisParameterHandler(MappedStatement mappedStatement, Object parameter, BoundSql boundSql) {
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        this.mappedStatement = mappedStatement;
        // 拼接好的sql
        this.boundSql = boundSql;
        this.configuration = mappedStatement.getConfiguration();
        this.sqlCommandType = mappedStatement.getSqlCommandType();
        // 主键id 和 属性的自动填充
        this.parameterObject = this.processParameter(parameter);
    }
}

可以看到在创建MybatisParameterHandler 对象时,boundSql 已经完成了sql 的解析和拼接,然后在this.processParameter(parameter) 方法完成了主键id 和 属性的自动填充,从构造方法可以看到,boundSql 的拼接是先于processParameter(parameter) 属性填充的方法的,这就解释了为什么我们明明已经为改属性进行了填充,为什么 最终自定义的insert into 语句 标签判断是空的,本质就是因为两者的顺序问题;

3.3 sql 语句的拼接:
进入DynamicSqlSource 类getBoundSql 方法:

public class DynamicSqlSource implements SqlSource {
    private final Configuration configuration;
    private final SqlNode rootSqlNode;

    public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
    }

    public BoundSql getBoundSql(Object parameterObject) {
    	// 参数解析
        DynamicContext context = new DynamicContext(this.configuration, parameterObject);
       	// sql 拼接
        this.rootSqlNode.apply(context);
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(this.configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        // 占位符拼接
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
        // sql 拼接
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        context.getBindings().forEach(boundSql::setAdditionalParameter);
        return boundSql;
    }
}

this.rootSqlNode.apply(context):

public class MixedSqlNode implements SqlNode {
    private final List<SqlNode> contents;

    public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
    }

    public boolean apply(DynamicContext context) {
    	// 这里会判断xml 中的所有属性标签,只有判断为true ,才进行属性的拼接
        this.contents.forEach((node) -> {
            node.apply(context);
        });
        return true;
    }
}

可以看到这里会根据标签不同调用不同的实现完成判断:
在这里插入图片描述
并且会逐个进行属性的判断,只有为true 才进行属性拼接:
在这里插入图片描述
以IfSqlNode 为例,可以看出只有当属性不为空时,才返回true 否则返回false,只有在返回true 时后续才会对改属性进行拼接
在这里插入图片描述

4 总结:

Mybatis-Plus 自定义的sql 语句其BoundSql的解析和拼接是在属性填充之前进行的,所以如果在自定义sql 语句中使用了<if>标签进行属性的非空判断,就不会拼接改属性,此时需要在自定义的sql 中去除<<if>的非空判断直接使用#{testFiled },这样最终在进数据插入时,Mybatis会动态的替换掉改占位符。



MyBatisPlus中使用 @TableField完成字段自动填充

引言

MyBatisPlus是一款强大的Java持久层框架,它在MyBatis的基础上进行了功能扩展和优化。其中,自动填充字段是一个常见的需求,可以通过使用MyBatisPlus的注解@TableField来实现。本文将介绍如何使用@TableField注解完成字段自动填充的功能。

@TableField注解简介

@TableField注解是MyBatisPlus提供的用于实体类字段的注解,用于配置字段的属性和行为。其中,我们可以通过设置fill属性来实现字段自动填充的功能。

使用方法

下面是使用@TableField注解完成字段自动填充的步骤:

1. 在实体类中添加字段

首先,在实体类中添加需要自动填充的字段。例如,我们在User实体类中添加createTime和updateTime字段,用于记录创建时间和更新时间。

javaCopy codepublic class User {
    private Long id;
    private String name;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    // 省略getter和setter方法
}
2. 配置字段填充处理器

接下来,我们需要配置字段填充的处理器。在MyBatisPlus中,我们可以通过实现MetaObjectHandler接口来自定义填充处理器。

javaCopy code@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}

在上述代码中,我们实现了insertFill和updateFill两个方法,分别用于在插入和更新操作时自动填充字段的值。

3. 配置MyBatisPlus的自动填充

最后,我们需要在MyBatisPlus的配置文件中进行配置,以启用自动填充功能。

@Configuration
public class MyBatisPlusConfig {
    @Autowired
    private MyMetaObjectHandler myMetaObjectHandler;
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new MetaObjectHandlerInterceptor(myMetaObjectHandler));
        return interceptor;
    }

在上述代码中,我们将自定义的填充处理器MyMetaObjectHandler添加到MybatisPlusInterceptor中,并将其作为一个内部拦截器。

4. 测试自动填充功能

现在,我们可以进行测试,看看自动填充功能是否生效。

javaCopy codeUser user = new User();
user.setName("John");
userService.save(user);

在上述代码中,我们创建了一个User对象,并设置了name属性的值为"John"。当调用userService的save方法保存对象时,createTime和updateTime字段将会被自动填充为当前的时间。

以下是一个示例代码,演示了如何使用@TableField注解完成字段自动填充的功能:

// User.java
public class User {
    private Long id;
    private String name;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    // 省略getter和setter方法
}
// MyMetaObjectHandler.java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}
// MyBatisPlusConfig.java
@Configuration
public class MyBatisPlusConfig {
    @Autowired
    private MyMetaObjectHandler myMetaObjectHandler;
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new MetaObjectHandlerInterceptor(myMetaObjectHandler));
        return interceptor;
    }
}
// UserService.java
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    public void save(User user) {
        userMapper.insert(user);
    }
}
// UserController.java
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping("/users")
    public void createUser(@RequestBody User user) {
        userService.save(user);
    }
}

在上述示例代码中,我们定义了一个User实体类,其中包含了需要自动填充的createTime和updateTime字段。我们使用@TableField注解来标记这两个字段,通过设置fill属性为FieldFill.INSERT和FieldFill.INSERT_UPDATE来指定字段自动填充的时机。 我们还定义了一个MyMetaObjectHandler类,实现了MetaObjectHandler接口,并重写了insertFill和updateFill方法来实现具体的字段填充逻辑。 在MyBatisPlusConfig类中,我们将自定义的填充处理器MyMetaObjectHandler添加到MybatisPlusInterceptor中,并作为一个内部拦截器。 最后,在UserController中,我们通过调用userService的save方法来保存用户对象。当保存操作触发时,createTime和updateTime字段将会被自动填充为当前的时间。 请根据实际需求,修改代码中的参数和逻辑以适应你的项目。

MetaObjectHandlerInterceptor 类是一个自定义的实现类,用于实现 MyBatis-Plus 框架的拦截器功能。下面是一个可能的 MetaObjectHandlerInterceptor 类的代码示例:

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MetaObjectHandlerInterceptor implements Interceptor {
    private MyMetaObjectHandler myMetaObjectHandler; // 自定义的 MetaObjectHandler
    public MetaObjectHandlerInterceptor(MyMetaObjectHandler myMetaObjectHandler) {
        this.myMetaObjectHandler = myMetaObjectHandler;
    }
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        // 在执行 update 方法之前,调用自定义的 MetaObjectHandler 填充数据
        myMetaObjectHandler.insertFill(mappedStatement, parameter);
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }
    @Override
    public void setProperties(Properties properties) {
        // 设置拦截器的属性
    }
}

上述代码使用了 MyBatis 的拦截器功能 (@Intercepts 和 @Signature 注解) 来实现对 Executor 类的 update 方法进行拦截。在拦截方法中,将会调用自定义的 MetaObjectHandler 的 insertFill 方法进行数据填充,然后再继续执行原始的方法逻辑。 请注意,上述代码中的 MyMetaObjectHandler 是自定义的 MyMetaObjectHandler 类,需要根据自己的业务逻辑来实现该类。MyMetaObjectHandler 可以继承 com.baomidou.mybatisplus.core.handlers.MetaObjectHandler 类,并重写对应的方法,以实现数据库操作前后的自定义处理逻辑。

一个实际的应用场景是,在用户注册时,自动为用户生成一个唯一的邀请码,并将邀请码存储到用户表中。以下是一个示例代码:

javaCopy code// User.java
public class User {
    private Long id;
    private String name;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    @TableField(fill = FieldFill.INSERT)
    private String inviteCode;
    // 省略getter和setter方法
}
// MyMetaObjectHandler.java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "inviteCode", String.class, generateInviteCode());
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
    
    private String generateInviteCode() {
        // 生成唯一的邀请码逻辑
        // 可以使用UUID、随机字符串等方式生成唯一的邀请码
        // 这里只是一个示例,实际应用中需要根据具体需求进行处理
        return UUID.randomUUID().toString().replace("-", "");
    }
}
// MyBatisPlusConfig.java
@Configuration
public class MyBatisPlusConfig {
    @Autowired
    private MyMetaObjectHandler myMetaObjectHandler;
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new MetaObjectHandlerInterceptor(myMetaObjectHandler));
        return interceptor;
    }
}
// UserService.java
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    public void save(User user) {
        userMapper.insert(user);
    }
}
// UserController.java
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping("/users")
    public void createUser(@RequestBody User user) {
        userService.save(user);
    }
}

在上述示例代码中,我们新增了一个inviteCode字段,并使用@TableField注解将其标记为需要自动填充的字段。在MyMetaObjectHandler类的insertFill方法中,我们通过调用generateInviteCode方法生成一个唯一的邀请码,并将其填充到inviteCode字段中。generateInviteCode方法中使用UUID来生成一个唯一的字符串作为邀请码,然后去掉其中的"-"字符。 当用户注册时,会调用UserController中的createUser方法,该方法会调用userService的save方法保存用户对象。在保存操作触发时,createTime、updateTime和inviteCode字段将会被自动填充。inviteCode字段的值将会是一个唯一的邀请码。 请根据你的实际需求,修改代码中的参数和逻辑以适应你的项目。

总结

通过使用@TableField注解和自定义的MetaObjectHandler填充处理器,我们可以很方便地实现字段的自动填充功能。在MyBatisPlus中,这个功能可以简化我们的开发工作,提高代码的可维护性和可读性。希望本文对大家在使用MyBatisPlus进行开发时有所帮助!

相关文章:

并发编程下的集合:数组寻址、LinkedList、HashMap、ConcurrentHashMap

如果发现hash取模后的数组索引位下无元素则直接新增,若不是空那就说明存在hash冲突,则判断数组索引位链表结构中的第一个元素的key以及hash值是否与新的key一致则直接覆盖,若不一致则判断当前的数组索引下的链表结构是否为红黑树,若为红黑树则走红黑树的新增方法,若不为红黑树则遍历当前链表结构,遍历中发现某个节点元素的next为null是则直接将新元素指针与next进行关联,若在遍历到next为空前判断到,某个节点的key以及key的hash值与新的key与新的keyhash值一致时则走覆盖。

【日常开发之插件篇】IDEA plugins 神器助我!!

今早因为老代码的一些bug让我突然觉得Idea的一些插件特别好用,我准备将我平时所用到的一些插件做个推荐以及记录。

【日常开发之FTP】Windows开启FTP、Java实现FTP文件上传下载

FTP是一个专门进行文件管理的操作服务,一般来讲可以在任意的操作系统之中进行配置,但是如果考虑到简便性,一般来讲可以直接在Linux系统下进行安装。FTP (File Transfer Protocol、文件传输协议)是TCP/IP协议中的一部分,属于应用层协议。使用FTP最主要的功能是对文件进行管理,所以在FTP内部对于文件支持有两种传输模式:文本模式(ASCII、默认)和二进制模式(Binary),通常文本文件使用ASCIl模式,而对于图片、视频、声音、压缩等文件则会使用二进制的方式进行传输。

【Linux之升华篇】Linux内核锁、用户模式与内核模式、用户进程通讯方式

alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。字符设备描述符 struct cdev,cdev_alloc()用于动态的分配 cdev 描述符,cdev_add()用于注。外,还支持语义符合 Posix.1 标准的信号函数 sigaction(实际上,该函数是基于 BSD 的,BSD。从最初的原子操作,到后来的信号量,从。(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的。

【Mongdb之数据同步篇】什么是Oplog、Mongodb 开启oplog,java监听oplog并写入关系型数据库、Mongodb动态切换数据源

oplog是local库下的一个固定集合,Secondary就是通过查看Primary 的oplog这个集合来进行复制的。每个节点都有oplog,记录这从主节点复制过来的信息,这样每个成员都可以作为同步源给其他节点。Oplog 可以说是Mongodb Replication的纽带了。

【日常开发之Windows共享文件】Java实现Windows共享文件上传下载

下拉框选择你选择的用户点击添加,然后共享确定。创建一个文件夹然后点击属性界面,点击共享。maven版本存在于SMB协议的兼容问题。首先开启服务,打开控制面板点击程序。点击启用或关闭Windows功能。我这边是专门创建了一个用户。SMB1.0选中红框内的。

CXFServlet类的作用

CXFServlet是Apache CXF框架中的一个核心组件,用于处理HTTP请求并将它们转换为Web服务调用。通过配置CXFServlet,你可以轻松地部署和管理SOAP和RESTful Web服务。

@Scheduled注解的scheduler属性什么作用

注解是 Spring Framework 提供的一种机制,用于定义计划任务,即周期性执行的任务。 注解可以应用于方法上,以指示 Spring 容器在特定的时间间隔或按照某种调度规则来调用该方法。 属性是 注解的一个可选属性,它的作用是允许开发者指定一个自定义的 对象来控制任务的调度方式。默认情况下, 注解使用 Spring 内部的 来执行任务,但如果需要更高级的定制化需求,可以通过 属性指定一个自定义的 实现。自定义调度器:共享调度器资源:高级调度需求:假设你想使用 作为调度器,并且希望所有带有

过滤器、拦截器、aop的先后顺序和作用范围&拦截器preHandle(),postHandle(),afterComplation()方法执行顺序

在Spring框架中,过滤器(Filter)、拦截器(Interceptor)和面向切面编程(AOP)都是用于处理请求和处理流程的组件,但它们的作用范围和触发时机有所不同。下面我会解释这三者的先后顺序和作用范围。执行顺序:请注意,这个顺序可能因具体的配置和使用的技术而有所不同。在实际应用中,建议根据项目的具体需求来合理配置和使用这些组件。拦截器执行流程图:实现拦截器需要实现这个接口,这个 接口中有三个默认方法,这三个方法的执行顺序:我们实现接口然后重写这三个方法,就会在对应的时机被自动执行。这里就是调用处理

Zookeeper概要、协议、应用场景

Zoopkeeper提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,作为分布式系统的沟通调度桥梁。

spring.factories文件的作用

即spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器中。在Spring Boot启动时,它会扫描classpath下所有的spring.factories文件,加载其中的自动配置类,并将它们注入到Spring ApplicationContext中,使得项目能够自动运行。spring.factories文件是Spring Boot自动配置的核心文件之一,它的作用是。

Spring事务七大传播机制与五个隔离级别,嵌套事务

如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。

常见的七种加密算法及实现

**数字签名**、**信息加密** 是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、`oauth` 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 **签名加密算法** 来达到业务目标。这里简单的给大家介绍几种常见的签名加密算法和一些典型场景下的应用。## 正文### 1. 数字签名**数字签名**,简单来说就是通过提供 **可鉴别** 的 **数字信息** 验证 **自身身份** 的一种方式。一套 **数字签名** 通常定义两种 **互补

7min到40s:SpringBoot 启动优化实践

然后重点排查这些阶段的代码。先看下。

SpringBoot系列教程之Bean之指定初始化顺序的若干姿势

之前介绍了@Order注解的常见错误理解,它并不能指定 bean 的加载顺序,那么问题来了,如果我需要指定 bean 的加载顺序,那应该怎么办呢?本文将介绍几种可行的方式来控制 bean 之间的加载顺序。

在Java中使用WebSocket

WebSocket是一种协议,用于在Web应用程序和服务器之间建立实时、双向的通信连接。它通过一个单一的TCP连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket协议最初由W3C开发,并于2011年成为标准。

3种方案,模拟两个线程抢票

在多线程编程中,资源竞争是一个常见的问题。资源竞争发生在多个线程试图同时访问或修改共享资源时,可能导致数据不一致或其他并发问题。在模拟两个线程抢票的场景中,我们需要考虑如何公平地分配票,并确保每个线程都有机会成功获取票。本篇文章将通过三种方式来模拟两个线程抢票的过程,以展示不同的并发控制策略。使用 Synchronized 来确保一次只有一个线程可以访问票资源。使用 ReentrantLock 来实现线程间的协调。使用 Semaphore 来限制同时访问票的线程数量。

替代Druid,HakariCP 为什么这么快?

这次源码探究,真的感觉看到了无数个小细节,无数个小优化,积少成多。平时开发过程中,一些小的细节也一定要“扣”。

Java中volatile 的使用场景有哪些?

volatile是一种轻量级的同步机制,它能保证共享变量的可见性,同时禁止重排序保证了操作的有序性,但是它无法保证原子性。所以使用volatilevolatile。

JDK22 正式发布了 !

Java 22 除了推出了新的增强功能和特性,也获得 Java Management Service (JMS) 的支持,这是一项新的 Oracle 云基础设施远程软件服务(Oracle Cloud Infrastructure, OCI) 原生服务,提供统一的控制台和仪表盘,帮助企业管理本地或云端的 Java 运行时和应用。使包含运行时计算值的字符串更容易表达,简化 Java 程序的开发工作,同时提高将用户提供的值编写成字符串,并将字符串传递给其他系统的程序的安全性。支持开发人员自由地表达构造器的行为。

Jackson 用起来!

你可以创建自定义序列化器和反序列化器以自定义特定字段或类的序列化和反序列化行为。为此,请创建一个实现或接口的类,并在需要自定义的字段或类上使用和注解。@Override// ...其他代码...优势性能优异:Jackson在序列化和反序列化过程中表现出优秀的性能,通常比其他Java JSON库更快。灵活性:通过注解、自定义序列化器/反序列化器等功能,Jackson提供了丰富的配置选项,允许你根据需求灵活地处理JSON数据。易于使用:Jackson的API设计简洁明了,易于学习和使用。

拜托!别再滥用 ! = null 判空了!!

另外,也许受此习惯影响,他们总潜意识地认为,所有的返回都是不可信任的,为了保护自己程序,就加了大量的判空。如果你养成习惯,都是这样写代码(返回空collections而不返回null),你调用自己写的方法时,就能大胆地忽略判空)这种情况下,null是个”看上去“合理的值,例如,我查询数据库,某个查询条件下,就是没有对应值,此时null算是表达了“空”的概念。最终,项目中会存在大量判空代码,多么丑陋繁冗!,而不要返回null,这样调用侧就能大胆地处理这个返回,例如调用侧拿到返回后,可以直接。

详解Java Math类的toDegrees()方法:将参数从弧度转换为角度

Java Math 类的 toDegrees() 方法是将一个角度的弧度表示转换为其度表示,返回值为double类型,表示从弧度数转换而来的角度数。这就是Java Math 类的 toDegrees() 方法的攻略。我们已经了解了该方法的基本概念、语法、注意事项以及两个示例。希望这篇攻略对你有所帮助。

SpringBoot接口防抖(防重复提交)的一些实现方案

作为一名老码农,在开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。啥是防抖所谓防抖,一是防用户手抖,二是防网络抖动。

公司新来一个同事:为什么 HashMap 不能一边遍历一边删除?一下子把我问懵了!

前段时间,同事在代码中KW扫描的时候出现这样一条:上面出现这样的原因是在使用foreach对HashMap进行遍历时,同时进行put赋值操作会有问题,异常ConcurrentModificationException。于是帮同简单的看了一下,印象中集合类在进行遍历时同时进行删除或者添加操作时需要谨慎,一般使用迭代器进行操作。于是告诉同事,应该使用迭代器Iterator来对集合元素进行操作。同事问我为什么?这一下子把我问蒙了?对啊,只是记得这样用不可以,但是好像自己从来没有细究过为什么?

每天一个摆脱if-else工程师的技巧——优雅的参数校验

在日常的开发工作中,为了程序的健壮性,大部分方法都需要进行入参数据校验。最直接的当然是在相应方法内对数据进行手动校验,但是这样代码里就会有很多冗余繁琐的if-else。throw new IllegalArgumentException("用户姓名不能为空");throw new IllegalArgumentException("性别不能为空");throw new IllegalArgumentException("性别错误");

SpringBoot请求转发与重定向

但是可能由于B网址相对于A网址过于复杂,这样搜索引擎就会觉得网址A对用户更加友好,因而在重定向之后任然显示旧的网址A,但是显示网址B的内容。在平常使用手机的过程当中,有时候会发现网页上会有浮动的窗口,或者访问的页面不是正常的页面,这就可能是运营商通过某种方式篡改了用户正常访问的页面。重定向,是指在Nginx中,重定向是指通过修改URL地址,将客户端的请求重定向到另一个URL地址的过程,Nginx中实现重定向的方式有多种,比如使用rewrite模块、return指令等。使用场景:在返回视图的前面加上。

SSO 单点登录和 OAuth2.0 有何区别?

此方法的缺点是它依赖于浏览器和会话状态,对于分布式或者微服务系统而言,可能需要在服务端做会话共享,但是服务端会话共享效率比较低,这不是一个好的方案。在单点登录的上下文中,OAuth 可以用作一个中介,用户在一个“授权服务器”上登录,并获得一个访问令牌,该令牌可以用于访问其他“资源服务器”上的资源。首先,SSO 主要关注用户在多个应用程序和服务之间的无缝切换和保持登录状态的问题。这种方法通过将登录认证和业务系统分离,使用独立的登录中心,实现了在登录中心登录后,所有相关的业务系统都能免登录访问资源。

TCP协议-TCP连接管理

TCP协议是 TCP/IP 协议族中一个非常重要的协议。它是一种面向连接、提供可靠服务、面向字节流的传输层通信协议。TCP(Transmission Control Protocol,传输控制协议)。

接口响应慢?那是你没用 CompletableFuture 来优化!

大多数程序员在平时工作中,都是增删改查。这里我跟大家讲解如何利用CompletableFuture优化项目代码,使项目性能更佳!