I have a strange problem.
I have two domain classes User
and Post
with fields:
class User {
String name
static hasMany = [posts: Post]
static constraints = { }
}
and
class Post {
String content
long date = System.getTimeInMillis()
static constraints = { }
static belongsTo = [user: User]
static mapping = {
version: 'false'
}
}
and controller code is:
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()
}
}
For the first time while accessing url http://localhost:8080/test/user/addUser
, it throws null pointer exception, but after this works fine.
This is the exception I am getting
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)
and for second time, it prints and renders fine like charm
Posts count: 0
In user domain class, coz of hasMany
relationship for posts
, posts
is a list of Post
objects then there shouldn't be null pointer exception on getting the size of empty list, rather it should be zero.
Any help appreciated
You can prevent this by explicitly declaring your collection property (with a value) alongside your mapping:
You can define the collection type you need. The default is
Set
, but if you need to maintain order, you might considerList
orSortedSet
.When you map a collection like that, the
hasMany
declaration adds a field of typeSet
with the specified name (in this caseposts
) to your class. It doesn't initialize the set though, so it's initially null. When you calladdToPosts
it checks if it's null and creates a new empty Set if necessary, and adds the Post to the collection. But if you don't calladdToPosts
or explicitly initialize the set, it will be null.When you load a
User
from the database, Hibernate will populate all the fields, and the collection is included in that. It creates a new Set (a modification-awarePersistentSet
) that's empty, and adds instances to it if there are any. Callingsave()
doesn't reload the instance from the database though, so the null set will still be null.To get the class to behave the same way when it's new and when it's persistent, you can add a field to your class Like Rob showed in his answer, initialized to an empty set (
Set posts = []
)