Wednesday, April 20, 2011

Two-way relation in JPA

Hi

I have the two entity classes, User and MyCharacter. User has a list of MyCharacters and each MyCharacter has a reference back to the User (owner). What I'd like to accomplish is, that I use the same join table for both relations, meaning, that the owner relation found in MyCharacter would automatically use the same join table as from User=>MyCharacter. This means that the getOwner() method in MyCharacter should work without me having to explicitly at some point call setOwner(user).

To clear this a bit more, here's my unit test which currently fails (last assert fails)


@Test
public void testTwoWayRelation() {
    User user = new User();
    MyCharacter character = new MyCharacter();
    List<MyCharacter> chars = new ArrayList<MyCharacter>();
    chars.add(character);
    user.setCharacters(chars);

    facade.store(user);
    assertNotNull(character.getId());

    character = facade.find(MyCharacter.class, character.getId());

    assertNotNull(character.getOwner());
}

My entity classes are listed below.


@Entity
@Table(name = "myuser")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;

    @OneToMany(cascade = { CascadeType.PERSIST })
    protected List<MyCharacter> characters;

    public User() {

    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }


    public List<MyCharacter> getCharacters() {
     return characters;
    }

    public void setCharacters(List<MyCharacter> characters) {
     this.characters = characters;
    }

}


@Entity
public class MyCharacter{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;

    @ManyToOne
    @JoinTable(name = "myuser_mycharacter", joinColumns = @JoinColumn(name = "characters_id"), inverseJoinColumns = { @JoinColumn(name = "user_id") })
    protected User owner;

    public MyCharacter() {

    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }


    public User getOwner() {
     return owner;
    }

    public void setOwner(User owner) {
     this.owner = owner;
    }
}
From stackoverflow
  • Here is an article from Oracle which explains how to map such relations.

    When you use this in Java, depending on your JPA framework, you will need to add the MyCharacter to the list in User and set the user field in MyCharacter or only one of the two (because the framework will manage the other side for you). I suggest to write a small test to figure out what works (and you should write test cases for all the way in which you use your objects, anyway).

    When objects are loaded from the database, you won't need to do that since all frameworks handle this case correctly.

  • That's how we join two entities with jpa in our project:

        @Entity
        @Table(name = "Period")
        public class Period implements Serializable {
          private List<Delay> delays = new ArrayList<Delay>();
    
          @OneToMany(mappedBy="period") //name of the field in joined entity
          public List<Delay> getDelays() {
          return delays;
          }
        }
    
       @Entity
       @Table(name = "Delay")
       public class Delay implements Serializable {
    
        private Period period;
    
        @ManyToOne
        @JoinColumn(name = "PERIODID")
        public Period getPeriod() {
           return period;
        }   
    }
    
  • i'm not sure i understand your problem correctly, but you could try to set mappedBy on MyCharacter.owner:

    @ManyToOne(mappedBy="characters")
    

0 comments:

Post a Comment