Spring Boot 2.7中使用@InsertOnlyProperty的实现方案咨询
Spring Boot 2.7 模拟@InsertOnlyProperty 实现方案解析
一、关于扩展RelationalPersistentProperty的可行性
RelationalPersistentProperty是Spring Data JDBC中的接口,无法直接“重写”添加属性。正确的做法是继承它的默认实现DefaultRelationalPersistentProperty,通过扩展子类来新增isInsertOnly()方法:
- 先自定义一个
@InsertOnlyProperty注解,标记需要仅插入的属性 - 编写自定义PersistentProperty类,继承
DefaultRelationalPersistentProperty,在类中添加逻辑判断当前属性是否带有@InsertOnlyProperty注解,实现isInsertOnly()方法
二、你的实现思路合理性
整体方向是正确的,但需要调整细节:
- 自定义DataAccessStrategy:基于
DefaultAccessStrategy扩展是正确的,因为它是Spring Data JDBC中处理数据访问逻辑的核心类 - 重写SqlParametersFactory:需要在自定义的SqlParametersFactory中,调用自定义PersistentProperty的
isInsertOnly()方法,在生成参数源(如BeanPropertySqlParameterSource)时,区分插入/更新场景:- 插入操作:保留所有标记为仅插入的属性
- 更新操作:过滤掉标记为仅插入的属性,不传入SQL参数中
三、替代方案对比
如果短期无法迁移到Spring Boot 3.0,你的扩展方案是最贴合Spring Data生态的选择,比以下临时方案更稳定:
- AOP拦截方案:通过AOP拦截Repository的插入/更新方法,修改入参的参数源,但需要手动匹配方法和场景,容易出现遗漏
- 自定义参数源:直接使用自定义的
BeanPropertySqlParameterSource子类,在创建时过滤属性,但无法统一覆盖所有Spring Data JDBC生成SQL的场景
四、核心实现步骤示例
- 自定义注解
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface InsertOnlyProperty { }
- 扩展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); } }
- 自定义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); } }
- 自定义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
相关产品推荐
相关产品推荐

