Grails gorm的one to many空指针问题
话说最近写的东西都是个人遇到的小问题的笔记,是不是这行干的久了看的多了就不爱写什么理论长文了?
题归正传,其实是个很简单的小问题,就是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<User> 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<User>这个声明。---当然实际这个问题折腾了我半天时间,因为many那个表字段太多了,根本没发现问题出在哪里,最后做了这么个小例子才看清楚。
不过真的很奇怪为什么之前用了那么久hibernate,一直没遇到过这种场景?