最近捣鼓了下Spring boot和mysql,不可避免的用到了JPA,怎么说呢,快速开发挺好的,但确实有坑,记录下,以供后查。
一个需求是在mysql的一个column,名为tags中存储多于一个的字符串。
这个字段前端是以JSON array形式上送到server,而在server端(spring boot)的domain类中是:
private List<String> tags = new ArrayList<String>();
但是在关系型的mysql中是这样的:
| TAGS | varchar(96) | YES | | NULL | |
明显不匹配,所以我收获了意料之中的:
Could not determine type for: java.util.List, at table: PHOTOS, for columns: [org.hibernate.mapping.Column(tags)]
好吧,一番研究之后发现JPA有个好用的东西:@Convert
于是自己实现了接口javax.persistence.AttributeConverter,配置以后的样子是:
@Convert(converter = StringListConverter.class)
private List<String> tags = new ArrayList<String>();
StringListConverter是我的实现,功能是:
从property转column时将String数组变成逗号分隔的字符串
从column转property时将逗号分隔的字符串变为字符数组
也就是说,在mysql中tags存储的是逗号分隔的字符串,不错,美美的,没啥问题。
但很快遇到了问题:查询
查询需要能通过一个tag查询到拥有该tag的记录,当然在关系型数据库的一个column中存储通过逗号分隔的字符串是关系型数据库设计的一个典型的范模式,但是还是有办法达成这个需求的,比如
用like,但是不要忘了,在JPA中我们的tags是一个字符串数组,这就麻烦了。
我试图在CrudRepository中使用@Query和JPQL写出查询语句,怎么都不行,一番研究之后只能黯然放弃@Convert
JPA的解决方案是:
为tags另外建立一张表和domain class,然后使用@ElementCollection,@CollectionTable进行表连接,最后在JPQL中使用? member of p.person, 详细如下:
1. Table: tags
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| ID | mediumint(9) | YES | | NULL | |
| TAG | varchar(96) | YES | | NULL | |
+--------+--------------+------+-----+---------+-------+
2. Domain class:
新建domain class: Tag.class
在原有class的tag属性前加:
@ElementCollection
@CollectionTable(
name = “TAGS”,
joinColumns=@JoinColumn(name = "id")
)
private List<String> tag = new ArrayList<String>();
3. CrudRepository
在自己实现的CrudRepository的@Query中添加:#{#filter.tag} member of p.tag
这样就ok了,是不是很麻烦呢?