解决方法
在实体类的属性中添加以下两个注解:
@Column(columnDefinition= "varchar(2)", name="chou" )
@ColumnTransformer( forColumn = "chou",read = "DECODE(chou, '1234')", write = "ENCODE(?, '1234')" )
public String getChou() {
return chou;
}
@Column:来标识实体类中属性与数据表中字段的对应关系,@Column可以标注在属性前或getter方法前,因为我搜的资料都加在get方法所以我也放这里了,其实都可以的
columnDefinition:指定属性在数据库的存储类型,这里设置了字段对应的varchar(2)
name:定义了被标注字段在数据库表中所对应字段的名称,这里使用了chou这个属性
@ColumnTransformer:转换列值,通过实现数据库和实体的内容通过自定的规则转换,如数据库kg->实体g,加密解密
forColumn:指定生效的属性,这里是使用了了chou这个属性
read:读数据的规则,这里直接写了用于加密的DECODE函数,是可行的
write:写数据的规则,注意一定要有个问号用来表示被替换的值,这里的==?==就代表我要加密的数据,“1234”是密钥,可更换为别的
注意事项
- 写入时不能通过update的方式存储,这样还是原来的值,我用entitymanager.persist()方法就可以实现写入加密
- 取出没发现有什么限制,但是不管怎么取目标属性都是通过ENCODE解密后的结果
- 加密除了ENCODE的方法还可以参考meijm0103的文章
顺带一提
加上@Column后出现了遇到了错误
java.sql.SQLException: Value ‘0000-00-00 00:00:00’ can not be represented as java.sql.Timestamp
原因是数据库中的值为空,但是查询得到的数据自动转为了‘0000-00-00 00:00:00’这样的格式,所以在数据库连接的后面加上zeroDateTimeBehavior=convertToNull即可,如果你有不止一个参数,在参数之间加上&即可,是;的转义字符
jdbc:mysql://www.xxttblog.com:3306/xttblog?zeroDateTimeBehavior=convertToNull
参考资料
官方文档
meijm0103的文章Hibernate对字段进行加密,运行时解密
segmentfault里的提问Spring-data-jpa中用@ColumnTransformer注解加密,可是解密后返回的数据为null
kevin_zhuzj的文章java.sql.SQLException: Value ‘0000-00-00 00:00:00’ can not be represented as java.sql.Timestamp
碎碎念
- 项目经理要求将一个字段插入时加密,取出时解密
- 但是系统已经按照不加密的方式设计好了,所有关于这个字段的操作都是直接取出
- 难道要单独写一个SQL用来加密解密吗?那样就要在所有字段保存取出时再添加一个方法,而涉及的地方实在是太多了所以这个方法先作为下下之选
- 还有一个类似的方法是在代码中加密完成再存入数据库,取出时再在代码中解密,两者的区别只是在于加密解密由数据库完成或人工完成,同样的麻烦
- 最后想到Hibernate能否在进行持久化的时候自动加密解密,最终经过多次尝试终于成功