jaguarpaw
jaguarpaw

Reputation: 122

Foreign key constraint using ebean in play

Tables:

  1. Attributes: Movie attributes (drama, thriller etc) (has columns: id, name)
  2. User: User in the system (has columns: id, name)
  3. Movie: Has attributes associated (like 'matrix' having 'sci-fi - 60%', 'thriller - 40%' etc) (has columns: id, attrid, fraction) - attrid is a foreign key into Attributes
  4. Preference: User's preference for a movie (has columns: id, uid, movieid, rating) - uid is foreign key into User and movieid is foreign key into Movie.

This is the model description I have so far:

  User:

  @Entity
  @Table(name="USER")
  public class User extends Model {
    @Id
    @Column(name = "uid")
    public Long uid;

    @Column(name = "name")
    public String name;
  }

  Movie Attribute:

  @Entity
  @Table(name = "MOVIEATTRIBUTE")
  public class Attribute extends Model {
    @Id
    @Column(name = "attrid")
    public Long attrid;

    @Column(name = "name")
    public String name;

    @ManyToMany
    @JoinColumn(name = "movieid")
    public Movie movie;
  }

  Movie:

  @Entity
  public class Movie extends Model {
    @Id
    @Column(name = "movieid")
    public Long movieid;

    @ManyToMany
    @JoinColumn(name = "attrid")
    public Attribute attribute;

    @Column(name = "rating")
    public Integer rating;
  }

  Preference:

  @Entity
  public class Preference extends Model {
    @Id
    @Column(name = "prefid")
    public Long prefid;

    @ManyToMany
    @JoinColumn(name="uid")
    public User user;

    @ManyToMany
    @JoinColumn(name="movieid")
    private Movie movie;

    @Column(name = "rating")
    public Integer rating;
  }

I get the following runtime exception: Cannot read annotations for Preference.

What am I missing?

Thanks!

Upvotes: 1

Views: 1368

Answers (1)

rtruszk
rtruszk

Reputation: 3922

You have made several mistakes in this code:

  1. You use @ManyToMany annotation incorrectly. You added it to 'Movie movie' field in Attribute class and 'Attribute attribute' field in Movie class. So you can assign only one attribute to movie object and only one movie to attribute. But annotations say that there can be many attributes for single movie and many movies for attribute. If you use @ManyToMany or @OneToMany annotation then you should use collection. So the appropriate relation here should be:

    In Movie class:

    @OneToMany(mappedBy="movie") public List attributes;

In Attribute class:

@ManyToOne
public Movie movie;
  1. When you define bidirectional relation between two model classes (you put annotations in both classes) you should use 'mappedBy' attribute in one of them. This attribute indicates field in second class which is the second end of this relation.
  2. You used @ManyToMany relation everywhere. But from your description I deduced that you don't needed this relation. You need two bidirectional @ManyToOne relations and one unidirectional @OneToOne relation.
  3. Your Movie class has no 'title' attribute
  4. You don't have to specify names for tables and columns. If you omit this they would be the same as class and field names.

    I made some correction in your code to make it working:

User class:

@Entity
public class User extends Model {
    @Id
    public Long id;

    public String name;

    @OneToMany(mappedBy="user")
    public List<Preference> preferences;    
}

Movie class:

@Entity
public class Movie extends Model {
    @Id
    public Long id;

    @OneToMany(mappedBy="movie")
    public List<Attribute> attributes;

    public Integer rating;

    public String title;
}

Attribute class:

@Entity
public class Attribute extends Model {
    @Id
    public Long id;

    public String name;

    @ManyToOne
    public Movie movie;
}

Preferences class:

@Entity
public class Preference extends Model {
    @Id
    public Long id;

    @ManyToOne
    public User user;

    @OneToOne
    public Movie movie;

    public Integer rating;
}

Test method:

@Test
public void movieTest () {
    FakeApplication app = Helpers.fakeApplication(Helpers.inMemoryDatabase());
    Helpers.start(app);

    User u = new User();
    u.id=1L;
    u.name="John";

    Movie m = new Movie();
    m.id = 1L;
    m.title = "Matrix";
    m.rating = 5;

    Attribute a = new Attribute();
    a.id = 1L;
    a.name = "Comedy";
    a.movie = m;
    m.attributes.add(a);

    Preference p = new Preference();
    p.id = 1L;
    p.rating = 10;
    p.user=u;
    p.movie=m;

    Ebean.save(u);
    Ebean.save(m);
    Ebean.save(a);      
    Ebean.save(p);

    User fu = Ebean.find(User.class, 1L);
    Movie fm = Ebean.find(Movie.class, 1L);
    Attribute fa = Ebean.find(Attribute.class, 1L);
    Preference fp = Ebean.find(Preference.class, 1L);

    System.out.println("User: id:"+fu.id+" name:"+fu.name+ " preference_rating:"+fu.preferences.get(0).rating);
    System.out.println("Movie:  id:" + fm.id+" rating:" + fm.rating + " attrname:"+fm.attributes.get(0).name);
    System.out.println("Attribute:  id:"+fa.id+" name:"+fa.name);
    System.out.println("Preference:  id:"+fp.id+" name:"+fp.rating+" username:"+fp.user.name+" movietitle:"+fp.movie.title);
}

Upvotes: 1

Related Questions