1

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

Theo
  • 3,074
  • 7
  • 39
  • 54
  • Few questions. You are finding which entity? What do you mean by basic information? By the way you are missing `@JoinColumn` annotation. – Adeel Ansari Oct 22 '10 at 09:33
  • 1
    I'm finding an entity that bi-directional relationships (e.g. a user in the example). And by basic information I mean types that be stored in a column (like String, Integer, etc). I updated the question. Thanks for pointing this out. Regarding @JoinColumn: If you omit it, default values are used, which are the PK's of the entities. – Theo Oct 22 '10 at 09:48
  • Please show some (pseudo) code, the generated SQL – Pascal Thivent Oct 22 '10 at 11:28
  • There is no SQL code generated when I only update the relationship. Updating a basic column looks like this: UPDATE Category SET NAME = ? WHERE (ID = ?) bind => [cat, 1] – Theo Oct 22 '10 at 18:19
  • Man, you can pray for a miracle but I think that showing some (pseudo) Java code might help to make it happen... – Pascal Thivent Oct 22 '10 at 18:36
  • What are you talking about? There is no more (pseudo) code. Above are the entities. More code would be em.persist(user), where em is the entity manager. What exactly do you wanna see? – Theo Oct 23 '10 at 12:30
  • `More code would be em.persist(user), where em is the entity manager` That's exactly what I'd like to see and especially how you set the Categories. – Pascal Thivent Oct 23 '10 at 17:26
  • Got it. I added relationship management code above. – Theo Oct 23 '10 at 18:13

3 Answers3

3

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
  • 1,996
  • 11
  • 7
  • I have relationship maintenance code. But neither side of the relationship is reflected in the db. So this must be another issue. – Theo Oct 22 '10 at 15:09
1

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

Community
  • 1
  • 1
Theo
  • 3,074
  • 7
  • 39
  • 54
0

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
  • 3,699
  • 1
  • 40
  • 68
  • 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 Oct 25 '10 at 20:03