运行时修改@ColumnTransformer触发Field.java:393反射错误如何解决?
报错根因
你当前的报错是调用field1.get(User.class)时逻辑完全错误:Field.get()方法的入参要求是对应类的实例对象,你传入的是User.class类对象,该方法试图从类对象上读取String类型的secretfield属性,自然抛出类型不匹配异常。同时你的实现方向也有问题:注解存储在Field类本身的私有结构里,不是存储在实体类字段的实例值中。
原方案修正版
如果要通过反射修改@ColumnTransformer注解的值,需要反射获取Field类本身存储注解的私有字段,操作必须在Hibernate初始化(SessionFactory构建)之前执行,否则Hibernate已经解析完旧注解,修改不会生效:
List<Field> fields = Arrays.asList(User.class.getDeclaredFields()); for(Field field:fields) { // 直接匹配字段名,不要用toString判断更可靠 if(field.getName().equals("secretfield")) { // 反射获取Field类存储注解的私有字段,JDK8下字段名为annotations,高版本JDK可能为declaredAnnotations Field annotationField = Field.class.getDeclaredField("annotations"); annotationField.setAccessible(true); Map<Class<? extends Annotation>, Annotation> annotationMap = (Map<Class<? extends Annotation>, Annotation>) annotationField.get(field); // 构造新的ColumnTransformer注解 ColumnTransformer newAnnotation = new ColumnTransformer() { @Override public String forColumn() { return "secretfield"; } @Override public String read() { // 此处替换为你从服务器其他位置读取密钥后拼接的解密逻辑 return "AES_DECRYPT(secretfield, '你读取到的实际密钥')"; } @Override public String write() { // 此处替换为加密逻辑 return "AES_ENCRYPT(?, '你读取到的实际密钥')"; } @Override public Class<? extends Annotation> annotationType() { return ColumnTransformer.class; } }; // 替换原有注解 annotationMap.put(ColumnTransformer.class, newAnnotation); annotationField.set(field, annotationMap); } }
更稳定的替代方案
反射修改注解依赖JDK内部实现,兼容性差,更推荐用Hibernate属性转换器实现动态密钥加解密,不需要修改注解:
- 自定义加解密转换器
@Converter public class EncryptConverter implements AttributeConverter<String, String> { private final String secretKey; public EncryptConverter() { // 构造阶段直接从服务器环境变量/配置文件/密钥服务读取加密密钥 secretKey = System.getenv("DB_ENCRYPT_SECRET"); } @Override public String convertToDatabaseColumn(String attribute) { // 自行实现加密逻辑 return AESUtils.encrypt(attribute, secretKey); } @Override public String convertToEntityAttribute(String dbData) { // 自行实现解密逻辑 return AESUtils.decrypt(dbData, secretKey); } }
- 实体字段关联转换器即可
@Column(name = "secretfield", nullable = true) @Convert(converter = EncryptConverter.class) private String secretfield;
内容的提问来源于stack exchange,提问作者Harshit Vijayvargia
相关产品推荐
相关产品推荐

