Подтвердить что ты не робот

Почему 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, тогда для получения размера пустого списка не должно быть исключения с единственным указателем, а должно равна нулю.

Любая помощь оценена

4b9b3361

Ответ 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.