Hibernate Attribute Converter<Integer,String>使用及BeanUtils自动加解密实现问询
用AttributeConverter + 自定义Bean拷贝逻辑实现自动加密/解密
先澄清一个关键点:AttributeConverter<Integer, String>本质是JPA用来处理实体属性和数据库列之间类型转换的工具,但你需要的是实体与DTO拷贝时自动完成id的加密/解密。不过我们可以复用加密解密的核心逻辑,结合BeanUtils的扩展来实现你要的效果,彻底省去手动处理的麻烦。
步骤1:实现通用加密解密工具类
先写一个通用工具类,把加密解密逻辑抽出来,方便后续所有转换器复用:
public class EncryptUtils { // 替换成你实际用的加密逻辑,比如AES、RSA或者自定义规则,这里用Base64做示例 public static String encrypt(int id) { return Base64.getEncoder().encodeToString(String.valueOf(id).getBytes()); } // 对应的解密逻辑,注意处理异常场景 public static int decrypt(String encId) throws IllegalArgumentException { try { String idStr = new String(Base64.getDecoder().decode(encId)); return Integer.parseInt(idStr); } catch (Exception e) { throw new IllegalArgumentException("无效的加密ID,请检查格式", e); } } }
步骤2:实现JPA AttributeConverter(按需选择)
如果你的数据库主键存储的是加密后的字符串,而实体层用int id来操作,那需要这个转换器让Hibernate自动在实体和数据库之间做转换:
@Converter(autoApply = true) // autoApply=true会自动应用到所有Integer类型的主键字段 public class IdEncryptConverter implements AttributeConverter<Integer, String> { @Override public String convertToDatabaseColumn(Integer attribute) { if (attribute == null) { return null; } return EncryptUtils.encrypt(attribute); } @Override public Integer convertToEntityAttribute(String dbData) { if (dbData == null) { return null; } return EncryptUtils.decrypt(dbData); } }
然后在实体的id字段上标记(如果开了autoApply=true可以省略这一步):
@Entity public class User { @Id @Convert(converter = IdEncryptConverter.class) private int id; // 其他业务属性... }
这一步完成后,Hibernate读写数据库时会自动处理加密解密,完全不用你手动干预。
步骤3:扩展BeanUtils实现实体-DTO自动映射
现在解决实体和DTO之间的拷贝问题:DTO里有int id和String encId,我们需要在拷贝时自动完成:
- 实体→DTO:把实体的
id加密后赋值给DTO的encId - DTO→实体:把DTO的
encId解密后赋值给实体的id
我们可以自定义BeanUtilsBean来覆盖默认的属性拷贝逻辑:
public class EncryptAwareBeanUtilsBean extends BeanUtilsBean { @Override public void copyProperty(Object dest, String name, Object value) throws IllegalAccessException, InvocationTargetException { // 处理实体→DTO:目标是DTO且要赋值encId时,加密源对象的id if (dest instanceof BaseDto && "encId".equals(name) && value instanceof Integer) { String encryptedId = EncryptUtils.encrypt((Integer) value); super.copyProperty(dest, name, encryptedId); return; } // 处理DTO→实体:源是DTO且要赋值id时,解密DTO的encId if (value instanceof BaseDto && "id".equals(name)) { BaseDto dto = (BaseDto) value; if (dto.getEncId() != null) { int decryptedId = EncryptUtils.decrypt(dto.getEncId()); super.copyProperty(dest, name, decryptedId); return; } } // 其他属性按默认逻辑拷贝 super.copyProperty(dest, name, value); } }
这里假设你所有的DTO都继承自BaseDto,统一包含encId的getter方法:
public abstract class BaseDto { private int id; private String encId; // 标准的getter、setter... }
最后封装一个工具类来方便调用:
public class EncryptBeanUtils { private static final BeanUtilsBean beanUtilsBean = new EncryptAwareBeanUtilsBean(); public static void copyProperties(Object dest, Object orig) { try { beanUtilsBean.copyProperties(dest, orig); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("属性拷贝失败", e); } } }
步骤4:实际使用
现在你只需要调用自定义的拷贝工具,就能自动完成所有加密解密操作:
// 实体→DTO:自动把实体id加密成DTO的encId User user = userRepository.findById(1).orElseThrow(); UserDto userDto = new UserDto(); EncryptBeanUtils.copyProperties(userDto, user); // DTO→实体:自动把DTO的encId解密成实体的id UserDto userDto = new UserDto(); userDto.setEncId("加密后的ID字符串"); User user = new User(); EncryptBeanUtils.copyProperties(user, userDto); // 直接把user存入数据库即可,无需额外处理
补充说明
- 如果你的数据库存储的是原始int类型主键,步骤2的
AttributeConverter可以直接省略,只需要步骤3的自定义BeanUtils就能满足需求。 - 加密逻辑要根据业务安全性要求选择,Base64只是示例,生产环境建议用AES这类对称加密算法,并妥善保管密钥。
- 如果你不想自定义BeanUtils,也可以用ModelMapper这类更灵活的映射工具,通过添加自定义转换器实现同样效果,但考虑到你提到了BeanUtils,优先给出了适配BeanUtils的方案。
内容的提问来源于stack exchange,提问作者Shakthi
相关产品推荐
相关产品推荐

