话说最近写的东西都是个人遇到的小问题的笔记,是不是这行干的久了看的多了就不爱写什么理论长文了?
题归正传,其实是个很简单的小问题,就是one to many的时候,如果one一端指明了,many为List类型,则many端会多一个 字段名_idx的字段,值是0,1,2,...如果你只是把many端的外键设置为null,剩下的idx从1,2,3...这样排序,会导致one加载的时候产生一个null,很诡异是吧...话说不知道为什么以前用hibernate的时候从来没有遇到过这个情况。
下面是具体例子。
User类是Many
import groovy.transform.EqualsAndHashCode;
@EqualsAndHashCode
class User {
String name
Company company
static belongsTo = [company: Company]
static constraints = {
company nullable:true
}
}
Comany是One,不过不一定严格关联,User可能属于一个Company,也可能不属于,那就是null。
import groovy.transform.EqualsAndHashCode;
@EqualsAndHashCode
class Company {
String name
List users
static hasMany = [users:User]
static mapping = {
users cascade:'none'
}
}
现在我插入几条测试数据
new Company(name:'Company1').save()
new Company(name:'Company2').save()
new User(name:'User1').save()
new User(name:'User2').save()
new User(name:'User3').save()
Company company1 = Company.get(1)
company1.addToUsers(User.get(1))
company1.addToUsers(User.get(2))
下面我要做的是取消user.get(1)和Company的关联。
于是我在controller里写了如下代码:
def deleteUser(Long id) {
def u = User.get(id)
u.company = null
boolean r = u.save(flush:true)
if (!r) {
println "saved ok"
} else {
println "not ok"
println u.errors
}
}
然后发现不好用,company无法被设置为null,不得已只能改为
def deleteUser(Long id) {
def u = User.get(id)
u.company = null
boolean r = u.save(flush:true)
if (!r) {
println "saved ok"
} else {
println "not ok"
println u.errors
}
int row = User.executeUpdate("update User set company = null where id = ?", [id])
println "row count:${row}"
}
结果发现这样是好用了,设置为null了,但是再次加载Company.get(1),然后对users进行循环的时候,发生空指针异常。
仔细看了一下sql发现就是因为List类型会检查users_idx字段,然后按值放到List的index里面,0没有了,只能null。
解决办法是如果需要排序,自己实现Comparable,去掉List
不过真的很奇怪为什么之前用了那么久hibernate,一直没遇到过这种场景?