You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 2.7中使用@InsertOnlyProperty的实现方案咨询

Spring Boot 2.7 模拟@InsertOnlyProperty 实现方案解析

一、关于扩展RelationalPersistentProperty的可行性

RelationalPersistentProperty是Spring Data JDBC中的接口,无法直接“重写”添加属性。正确的做法是继承它的默认实现DefaultRelationalPersistentProperty,通过扩展子类来新增isInsertOnly()方法:

  • 先自定义一个@InsertOnlyProperty注解,标记需要仅插入的属性
  • 编写自定义PersistentProperty类,继承DefaultRelationalPersistentProperty,在类中添加逻辑判断当前属性是否带有@InsertOnlyProperty注解,实现isInsertOnly()方法

二、你的实现思路合理性

整体方向是正确的,但需要调整细节:

  1. 自定义DataAccessStrategy:基于DefaultAccessStrategy扩展是正确的,因为它是Spring Data JDBC中处理数据访问逻辑的核心类
  2. 重写SqlParametersFactory:需要在自定义的SqlParametersFactory中,调用自定义PersistentProperty的isInsertOnly()方法,在生成参数源(如BeanPropertySqlParameterSource)时,区分插入/更新场景:
    • 插入操作:保留所有标记为仅插入的属性
    • 更新操作:过滤掉标记为仅插入的属性,不传入SQL参数中

三、替代方案对比

如果短期无法迁移到Spring Boot 3.0,你的扩展方案是最贴合Spring Data生态的选择,比以下临时方案更稳定:

  • AOP拦截方案:通过AOP拦截Repository的插入/更新方法,修改入参的参数源,但需要手动匹配方法和场景,容易出现遗漏
  • 自定义参数源:直接使用自定义的BeanPropertySqlParameterSource子类,在创建时过滤属性,但无法统一覆盖所有Spring Data JDBC生成SQL的场景

四、核心实现步骤示例

  1. 自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InsertOnlyProperty {
}
  1. 扩展PersistentProperty
public class CustomRelationalPersistentProperty extends DefaultRelationalPersistentProperty {

    public CustomRelationalPersistentProperty(Property property, PersistentEntity<?, RelationalPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
        super(property, owner, simpleTypeHolder);
    }

    public boolean isInsertOnly() {
        return isAnnotationPresent(InsertOnlyProperty.class);
    }
}
  1. 自定义PersistentEntityFactory,使用自定义PersistentProperty
public class CustomRelationalPersistentEntityFactory implements RelationalPersistentEntityFactory {

    private final MappingContext<? extends RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext;

    public CustomRelationalPersistentEntityFactory(MappingContext<? extends RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext) {
        this.mappingContext = mappingContext;
    }

    @Override
    public <T> RelationalPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
        return new DefaultRelationalPersistentEntity<>(typeInformation, CustomRelationalPersistentProperty::new);
    }
}
  1. 自定义DataAccessStrategy并注册
public class CustomDataAccessStrategy extends DefaultDataAccessStrategy {

    public CustomDataAccessStrategy(RelationalMappingContext context, NamedParameterJdbcOperations operations, SqlGeneratorSource sqlGeneratorSource) {
        super(context, operations, sqlGeneratorSource);
    }

    @Override
    protected SqlParametersFactory createSqlParametersFactory(RelationalPersistentEntity<?> entity) {
        return new CustomSqlParametersFactory(entity);
    }

    // 自定义SqlParametersFactory实现参数过滤逻辑
    private static class CustomSqlParametersFactory extends DefaultSqlParametersFactory {

        public CustomSqlParametersFactory(RelationalPersistentEntity<?> entity) {
            super(entity);
        }

        @Override
        public SqlParameterSource getParameterSource(Object entityObject, boolean isInsert) {
            SqlParameterSource source = super.getParameterSource(entityObject, isInsert);
            if (!isInsert && source instanceof BeanPropertySqlParameterSource) {
                // 过滤仅插入属性,返回新的参数源
                Map<String, Object> filteredParams = new HashMap<>();
                for (String paramName : ((BeanPropertySqlParameterSource) source).getReadablePropertyNames()) {
                    RelationalPersistentProperty property = getEntity().getRequiredPersistentProperty(paramName);
                    if (property instanceof CustomRelationalPersistentProperty && ((CustomRelationalPersistentProperty) property).isInsertOnly()) {
                        continue;
                    }
                    filteredParams.put(paramName, source.getValue(paramName));
                }
                return new MapSqlParameterSource(filteredParams);
            }
            return source;
        }
    }
}

最后需要将自定义的CustomRelationalPersistentEntityFactory和CustomDataAccessStrategy注册到Spring容器中,替换默认实现。

内容的提问来源于stack exchange,提问作者Khusayn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 17:05:25