Spring Data JDBC自定义CustomConversions转换器不生效问题
需求是实现Spring Data JDBC实体类字段的自动加解密,JPA的AttributeConverter在Spring Data JDBC中不支持,因此尝试用框架自带的CustomConversions特性实现:
- 首先自定义加密字段包装类型
EncryptionDataType:
import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode public class EncryptionDataType { private String value; @Override public String toString() { return value ; } }
- 在实体类中使用该自定义类型作为字段类型:
@Table("EMPLOYEE") @Builder @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Data public class Employee { @Column("name") private EncryptionDataType name; @Id private Integer id; @Version private Long version; }
- 编写对应的读写转换器,预期框架自动将
EncryptionDataType转成字符串存入MySQL的字符串列:
@WritingConverter public class EncryptionDataTypeWriteConverter implements Converter<EncryptionDataType, String> { @Override public String convert(EncryptionDataType source) { return source.toString()+"add"; } } @ReadingConverter public class EncryptionDataTypeReadConverter implements Converter<String, EncryptionDataType> { @Override public EncryptionDataType convert(String source) { return new EncryptionDataType(source); } }
- 编写配置类注册转换器:
@Configuration public class MyConfig { @Bean protected JdbcCustomConversions JdbcConversion(Dialect dialect) { return new JdbcCustomConversions( Arrays.asList(new EncryptionDataTypeReadConverter(),new EncryptionDataTypeWriteConverter())); } }
配置完全不生效,运行时抛出SQL语法错误:
PreparedStatementCallback; bad SQL grammar [INSERT INTO `encryption_data_type` (`name`, `value`) VALUES (?, ?)]; nested exception is java.sql.SQLSyntaxErrorException: Table 'testschema.encryption_data_type' doesn't exist
从生成的SQL可以看出,框架没有将EncryptionDataType转换为字符串处理,反而将其识别为关联/嵌入式实体,尝试向不存在的encryption_data_type表插入数据。
后续补充了自定义配置,手动注册了数据源、JdbcOperations、事务管理器、JdbcConverter、JdbcMappingContext等相关Bean:
@Configuration @EnableJdbcRepositories(transactionManagerRef = "CustomJdbcTranasactionManager", jdbcOperationsRef = "CustomJdbcOperationsReference", repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class, basePackages = { "com.java.testy" }) @EnableAutoConfiguration(exclude = { org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class, org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration.class }) public class MyConfig { @Bean protected JdbcCustomConversions JdbcConversion(Dialect dialect) { return new JdbcCustomConversions( Arrays.asList(new EncryptionDataTypeReadConverter(),new EncryptionDataTypeWriteConverter())); } // 手动创建datasource、CustomJdbcOperationsReference、CustomJdbcTranasactionManager、JdbcConverter、JdbcMappingContext、DataAccessStrategy、JdbcAggregateTemplate相关Bean }
手动定义JdbcMappingContext和JdbcConverterBean的时候,没有将自己注册的JdbcCustomConversions注入到这两个核心组件的实例中,框架初始化映射模型的时候根本读不到自定义转换器,就会默认把非基础类型、无对应转换器的自定义类型判定为嵌入式实体,按照嵌套实体的逻辑生成关联表插入SQL,最终报出表不存在的错误。
修改手动创建JdbcMappingContext和JdbcConverter的代码,将自定义的JdbcCustomConversions注入进去完成初始化:
- 初始化
JdbcMappingContext时,调用setSimpleTypeHolder方法传入自定义Conversions持有的简单类型标识,让框架识别EncryptionDataType是需要转换的简单值类型,不是嵌套实体 - 初始化
JdbcConverter(一般用BasicJdbcConverter实现)时,把自定义的JdbcCustomConversions作为构造参数传入,让转换器加载自定义的加解密转换逻辑
示例配置代码:
@Bean public JdbcMappingContext jdbcMappingContext(JdbcCustomConversions customConversions) { JdbcMappingContext mappingContext = new JdbcMappingContext(); // 注入自定义转换器的简单类型持有器,标记自定义类型为值类型 mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); return mappingContext; } @Bean public JdbcConverter jdbcConverter(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations, JdbcCustomConversions customConversions) { // 构造BasicJdbcConverter时传入自定义Conversions,加载读写转换器 return new BasicJdbcConverter( mappingContext, o -> null, customConversions, null, operations.getJdbcOperations().getExceptionTranslator() ); }
配置完成后重启,框架就会正常识别自定义转换器,不会再把EncryptionDataType当成嵌套实体处理,字段读写时会自动走定义的加解密逻辑。
注意:如果同时自定义了
JdbcAggregateTemplate和DataAccessStrategy,也要确保这两个组件引用的是正确配置了转换器的JdbcConverter实例,避免出现组件引用不一致的问题。
内容的提问来源于stack exchange,提问作者Ravi

