views:

1310

answers:

2

[Edit: Apparently, this is only an issue for arrays and FoxyBOA's answer might direct to (or even is) the answer.]

Hi, my question relates to these software: Hibernate3+Annotation, Spring MVC, MySQL and in this example also Spring Security.

I was wondering, why collections, which are automatically associated by Hibernate contain null values for each row number of the child table (besides the elements which are correct). My Example:

I have a users and an authorities table, the primary key of the users table is username which serves as foreign key. Right now, there are 13 rows in my authorities table. When I retrieve a user from the database (MySQL InnoDB) and Hibernate automatically retrieves the user's authorities corresponding to this mapping:

@OneToMany
@JoinColumn(name = "username")
@IndexColumn(name="id") // "id" was the primary key and is used to sort the elements
public Authority[] getAuthorities() {
    return authorities;
}
public void setAuthorities(Authority[] authorities) {
    this.authorities = authorities;
}

... I end up with a collection "authorities" containing 14 (0-13) elements of which only four are not-null (four rows in the database table belong to that specific user, so that is correct). As far as I realize, I am using Hibernate defaults for properties like Fetchmode etc. I am getting the user like this:

Criteria criteria = getSession().createCriteria(User.class);
criteria.add(Restrictions.eq("username",username));
User user = (User) criteria.uniqueResult();

The logging information from org.hibernate.loader.loader correctly "mentions" four rows for the resultset. Still, the user created has the four correct elements plus ten null values in the Array. In my specific example, this results in this exception:

java.lang.IllegalArgumentException: Granted authority element 0 is null - GrantedAuthority[] cannot contain any null elements

Your help is appreciated, thanks!

Wolfram

A: 

I can recommend you check your data. If you have a missed indexes (id column in your case), then instead of missed id you'll get null in your array. I.e.

table authorities:
username id
bob 1
bob 3
bob 5

As a result you will have an array: {0=null, 1=bob, 2=null, 3=bob, 4=null, 5=bob}

UPDATE: I met the situation in two cases:

  1. Missed key values in indexed column id at authorities table (e.g. 0,1,3,4,5 - missing value 2. Hibernate will automatically add to an array value with key 2 and value null).
  2. Indexed values are in order, but select criteria filter part of them (e.g. your HQL similar to that "from user u join u.authorities a where a.id=2". In that case hibernate load a user, but in authorities array you will have only 3 values: 0 - null, 1 - null, 2 - authority with id 2).
FoxyBOA
I added and removed some indexes, but am still getting 14 values with four correct values at those array indexes that are the same of the value in my id column. So I really think this is where my problem lies. Could you "elaborate" a little bit more, I am not quite grasping what you mean. Thanks so far!
Wolfram
+2  A: 

The answer lies in the @IndexColumn annotation. It is using the value of id as the array index, thus the number of elements in the Array is basically going to be the value of the highest ID in the Authorities table.

see the hibernate documentation on indexed collections

try removing the annotation.

Also just as a thought; have you considered using a Set for the mapping? it isn't strictly necessary, it just a bit more common form of mapping that's all.

Gareth Davis
At first, I did not have the @IndexColumn annotation, which resulted in an "org.hibernate.AnnotationException: List/array has to be annotated with an @IndexColumn".As for the type question: I was implementing the "UserDetails" Interface and its method "GrantedAuthority[] getAuthorities();". I will look into it to get rid of the @IndexColumn.You really helped me understanding this issue.
Wolfram
I see. I've implemented spring security integration quite a few times, the pattern that has served me best is to have a method like 'UserDetails toUserDetails()' on the User entity. This allows you to use an immutable none hibernate implementation, which in turn will serve you quite well as putting hibernate objects in the http session (as most user details end up) nearly always ends in tears.
Gareth Davis
Sounds great. And instead of returning the user directly from the "loadUserByUsername(..)" method of the UserDetailsService, you return user.toUserDetails()?Regarding the "end in tears", when I was correcting the array manually I ended up in numerous Hibernate exceptions (e.g. on trying to write a null-filled user back to the DB, whereever that came from, I did not look into it any further but "rolled back" to my first problem). I guess that is what you meant.
Wolfram
that and more... once you detach an entity from the hibernate session and let it live in the http session you can get lazy initialization and concurrency issues. I'd definitely go for using Set
Gareth Davis