views:

69

answers:

3

My domain model in my Java EE 6 application contains bi-directional relationships like the following:

@Entity
public  class Users implements PrimaryKeyHolder<String>, Serializable {
  @Id
  private String username;

  @ManyToMany(mappedBy= "users")
  private List<Category> categories;

  public List<Category> getCategories() {
    if (categories == null) {
        categories = new ArrayList<Category>();
    }
    return Collections.unmodifiableList(categories);
  }

  public void addCategory(Category category) {
    if (categories == null) {
        categories = new ArrayList<Category>();
    }

    categories.add(category);
    if (!category.getUsers().contains(this)) {
        category.addUser(this);
    }
  }

  public void removeCategory(Category category) {
    if (categories == null) {
        categories = new ArrayList<Category>();
    }

    categories.remove(category);
    if (category.getUsers().contains(this)) {
        category.removeUser(this);
    }
  }

  public void setCategories(Collection<Category> categories) {
    if (this.categories == null) {
        this.categories = new ArrayList<Category>();
    }

    for (Iterator<Category> it = this.categories.iterator(); it.hasNext();) {
        Category category = it.next();
        it.remove();
        if (category.getUsers().contains(this)) {
            category.removeUser(this);
        }
    }

    for (Category category : categories) {
        addCategory(category);
    }
  }
}

@Entity
public class Category implements PrimaryKeyHolder<Long>, Serializable {
  @Id
  private Long id;

  @ManyToMany
  private List<User> users;

  public List<User> getUsers() {
    if (users == null) {
        users = new ArrayList<User>();
    }
    return Collections.unmodifiableList(users);
  }

  protected void addUser(User user) {
    if (users == null) {
        users = new ArrayList<User>();
    }
    users.add(user);
  }

  protected void removeUser(User user) {
    if (users == null) {
        users = new ArrayList<User>();
    }
    users.remove(user);
  }
}

UPDATE: I added relationship management code. Relationships are only set on the user side, therefore, the add/remove methods are protected in the Categoriy class. I set the categories on the user via setCategories.

Eclipselink correctly generates a join table CATEGORY_USERS. However, it does not persist any information in it (it only caches the information). E.g. when I execute a find operation on the entity manager (e.g. a user), it returns the complete object graph (including the category relationship). But when I look at the tables, information are not updated (even though the transactions are committed). I also inserted a flush operation in my code, without success. Basic information (like String, Integer, etc. columns) gets correctly persisted and updated. After turning the log level to FINE, I can see that no SQL statements are executed for the relationships and the join table, respectively. But I do see SQL statements for uni-directional relationships.

My datamodel is covered by extensive unit tests, which all pass successfully. I basically do the same operation as in the container, commit the transaction, reload the entities from the db and check if the relationships are correctly set, which they are (I'm using the in-memory derby database for testing).

My app server is Glassfish v3.1-b17.

Any help is appreciated. Thanks, Theo

+1  A: 

Ensure you are setting both sides of the relationship. The specification requires that the application sets both sides of the relationship as there is no relationship maintenance in JPA.

Gordon Yorke
I have relationship maintenance code. But neither side of the relationship is reflected in the db. So this must be another issue.
Theo
A: 

Four points:

1.- When you have an error, it's more simple find solution isolating them in an example (Or unit test) that reproduces the error. In your case, you could do an example with more simple getter and setter (for example, removing unmodifiableList use and other innecesary methods for testing actually issue).

2.- I advise you to use pojos for model, without any logic. So, remove logic from pojos.

3.- We are using eclipselink and we do not have problems persisting relations. So, it is more possible that error will be in your code.

4.- Test annoting relation with "cascade = javax.persistence.CascadeType.ALL"

Apology for my poor English :(

angelcervera
Yeah, you're right, there must be something wrong with my code. I didn't put any logic in my pojos. What you see is relationship management code for managing the in-memory relationship of entities.
Theo
A: 

After endless hours of trying I finally got to a solution: I simply changed the owning side of the relationship, i.e. I put the mappedBy attribute to the category entity like this:

@ManyToMany(mappedBy= "categories")
private List<User> users;

The explanation for this can be found here

Theo