Почему grails бросает исключение нулевого указателя при обращении к hasMany отношениям в первый раз?
У меня странная проблема.
У меня есть два класса домена User
и Post
с полями:
class User {
String name
static hasMany = [posts: Post]
static constraints = { }
}
и
class Post {
String content
long date = System.getTimeInMillis()
static constraints = { }
static belongsTo = [user: User]
static mapping = {
version: 'false'
}
}
и код контроллера:
class UserController {
def addUser = {
def user
if (User.count() == 0) {
user = new User()
user.name = "Manish Zedwal"
user.save(flush: true)
} else {
user = User.get(1)
}
println "Posts count: " + user.posts.size()
render "post count: " + user.posts.size()
}
}
Впервые при доступе к url http://localhost:8080/test/user/addUser
он выбрасывает исключение из null-указателя, но после этого работает нормально.
Это исключение, которое я получаю
2011-08-04 15:41:25,847 [http-8080-1] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [GET] /test/user/addUser
Stacktrace follows:
java.lang.NullPointerException: Cannot invoke method size() on null object
at test.UserController$_closure2.doCall(UserController.groovy:18)
at test.UserController$_closure2.doCall(UserController.groovy)
at java.lang.Thread.run(Thread.java:636)
и во второй раз он печатает и делает отлично, как шарм
Posts count: 0
В классе домена пользователя, coz отношения hasMany
для posts
, posts
- это список объектов Post
, тогда для получения размера пустого списка не должно быть исключения с единственным указателем, а должно равна нулю.
Любая помощь оценена
Ответы
Ответ 1
При сопоставлении такой коллекции объявление hasMany
добавляет к вашему классу поле типа Set
с указанным именем (в данном случае posts
). Однако он не инициализирует набор, поэтому он сначала имеет значение null. Когда вы вызываете addToPosts
, он проверяет, имеет ли он значение null и создает новый пустой набор, если это необходимо, и добавляет сообщение в коллекцию. Но если вы не вызываете addToPosts
или явно инициализируете набор, он будет равен нулю.
Когда вы загружаете User
из базы данных, Hibernate заполняет все поля, и коллекция включена в это. Он создает новый Set (с поддержкой изменений PersistentSet
), который пуст, и добавляет к нему экземпляры, если они есть. Вызов save()
не перезагружает экземпляр из базы данных, поэтому нулевой набор по-прежнему будет иметь значение null.
Чтобы заставить класс вести себя так же, когда он новый и когда он настойчив, вы можете добавить поле в свой класс. Как показал Роб в своем ответе, инициализировался пустой набор (Set posts = []
)
Ответ 2
Вы можете предотвратить это, явно объявив свое свойство коллекции (со значением) вместе с вашим сопоставлением:
class User {
String name
Set posts = []
static hasMany = [posts: Post]
}
Вы можете определить тип коллекции, который вам нужен. По умолчанию используется Set
, но если вам нужно поддерживать порядок, вы можете рассмотреть List
или SortedSet
.