timaschew
timaschew

Reputation: 16602

JPA entity design / cannot delete entity

I though its simple what I want, but I cannot find any solution for my problem. I'm using playframework 1.2.3 and it's using Hibernate as JPA. So I think playframework has nothing to do with the problem.

I have some classes (I omit the nonrelevant fields)

public class User {
  ...
}

public class Task {
  public DataContainer dataContainer;
}

public class DataContainer  {
  public Session session;
  public User user;
}

public class Session  {
  ...
}

So I have association from Task to DataContainer and from DataContainer to Sesssion and the DataContainer belongs to a User. The DataContainers can have always the same User, but the Session have to be different for each instance. And the DataContainer of a Task have also to be different in each instance. A DataContainer can have a Sesesion or not (it's optinal). I use only unidirectional assoc. It should be sufficient.

In other words: Every Task must has one DataContainer. Every DataContainer must has one/the same User and can have one Session.

To create a DB schema I use JPA annotations:

@Entity
public class User extends Model {
  ...
}

@Entity
public class Task extends Model {
  @OneToOne(optional = false, cascade = CascadeType.ALL)
  public DataContainer dataContainer;
}

@Entity
public class DataContainer extends Model  {
  @OneToOne(optional = true, cascade = CascadeType.ALL)
  public Session session;

  @ManyToOne(optional = false, cascade = CascadeType.ALL)
  public User user;
}

@Entity
public class Session extends Model {
  ...
}

BTW: Model is a play class and provides the primary id as long type.

When I create some for each entity a object and 'connect them', I mean the associations, it works fine. But when I try to delete a Session, I get a constraint violation exception, because a DataContainer still refers to the Session I want to delete.

I want that the Session (field) of the DataContainer will be set to null respectively the foreign key (session_id) should be unset in the database. This will be okay, because its optional.

I don't know, I think I have multiple problems. Am I using the right annotation @OneToOne ?

I found on the internet some additional annotation and attributes: @JoinColumn and a mappedBy attribute for the inverse relationship. But I don't have it, because its not bidirectional. Or is a bidirectional assoc. essentially?

Another try was to use @OnDelete(action = OnDeleteAction.CASCADE) the the contraint changed from NO ACTIONs when update or delete to:

ADD CONSTRAINT fk4745c17e6a46a56 FOREIGN KEY (session_id)
      REFERENCES annotation_session (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE;

But in this case, when I delete a session, the DataContainer and User is deleted. That's wrong for me.


EDIT: I'm using postgresql 9, the jdbc stuff is included in play, my only db config is
db=postgres://app:app@localhost:5432/app

Upvotes: 0

Views: 410

Answers (2)

JB Nizet
JB Nizet

Reputation: 691635

Should you use OneToOne or ManyToOne? Use the association which best described the situation. If several DataContainers may have the same session, it's a ManyToOne. If only one DataContainer may have a given session, then it's a OneToOne.

Now if you delete the session, and a DataContainer still references it, of course you'll get an exception. That's what FKs are for: it makes sure you can't have a DataContainer which references an unexisting session. So, to delete the session, you should first update all the data containers referencing it.

Since your association is unidirectional, you'll need a query to do this:

select dc from DataContainer dc where dc.session = :session

Execute this query, iterate through the results, and set the session to null. Then, delete the session.

If your association was bidirectional, you could simply do:

for DataContainer dc : session.getDataContainers() {
    dc.setSession(null);
}

You could also use an update query to do everything in a single query. But be aware that these changes won't be made to already loaded DataContainers in the session:

update DataContainer dc set dc.session = null where dc.session = :session

Upvotes: 3

Subin Sebastian
Subin Sebastian

Reputation: 10997

not sure , but what is the database used? is it loaded by hibernate automatically, or are you using your own DDL query to create it. if so please check if constraint is established at database level, verify that sessionId foreign key in DataContainer table is nullable

Upvotes: 0

Related Questions