15

I'm using JPA2 with EclipseLink implementation

![Simple table structure][1]

Here are the two tables which I try to map and the JPA annotations.

public class Story implements Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    Integer id;
    @Temporal(TemporalType.TIMESTAMP)
    @Column (name="DATE_CREATED")
    Date dateCreated;
    String title;
    String description;
    @Column(name="AUTHOR_ID")
    Integer authorId;
    @Column(name="COUNTRY_ID")
    Integer countryId;
    private String reviews;

    @OneToMany(mappedBy = "story", cascade=CascadeType.ALL)
    private List<Tip> tipList;
}

public class Tip implements Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;
    private String description;
    private Integer vote;

    @ManyToOne (cascade=CascadeType.ALL)
    @JoinColumn(name="STORY_ID", referencedColumnName="ID")
    private Story story;
}

As a simple example I would like to persist a story and some story related tips in the same transaction. Here is the section of code which does that:

Story newStory = new Story(title, body, ...);

EntityTransaction transaction = em.getTransaction().begin();
    boolean completed = storyService.create(newStory);
    //The tips are saved as a List<String>. This methods creates the needed List<Tip> from the Strings
    List<Tip> tips = TipUtil.getTipList(tipList);
    newStory.setTipList(tips)
transaction.commit();

I have no errors and all the entities are persisted in the database. The problem is that in the tip table the story_id field is always NULL. I can imagine that JPA is unable to get the new id from the story table. What's the correct approach here?

LE

In the current state of the code, the Tip entities are persisted but the country ID remains null.

Ionut
  • 2,788
  • 6
  • 29
  • 46

4 Answers4

11

With JPA, it is always recommended to update the relationship on both the sides in a bi-directional relationship. This is to ensure that the data is consistent in your application layer and nothing to do with the database.

However it is mandatory that you update the owning side of the relationship in a bidirectional relationship.

So, setting/not setting

story.setTipList(tips)

is up to you. But if you want the changes to reflect properly in DB then you mush call

tip.setStory(story)

as Tip is the owning side here, as per your code. Also your code looks incomplete to me. Reasons is,

  • the entity returned by storyService.create(newStory) is managed but not the newStory. So just setting newStory.setTipList(tips) will not updated the db
Pragalathan M
  • 1,673
  • 1
  • 14
  • 19
7

Because you need to update the parent link story in each of your child.

The way its is done is to create a addTip(Tip tip) method in your Story class.

This method does :

tip.setStory(this);
tipList.add(tip);

If you don't need bedirectional approach, you can remove the story field in Tip and it will resolve your problem

Jerome Cance
  • 8,103
  • 12
  • 53
  • 106
  • 1
    I am wondering if there isn't a more automatic way to do this. What is more, I really don't think that deleting the story field in Tip would be a smart idea, because this way, he would need to get rid of the ` @PrimaryKeyJoinColumn(name="STORY_ID", referencedColumnName="ID")`, leaving JPA with no clue about how it should make the join. This is just what I understood from reading a bit on the subject. Please let me know if I'm wrong. – Dragos Feb 28 '12 at 11:50
  • Ebean has automatic solution for this one, but JPA implementations do not provide automatic solution. – Palesz Mar 08 '12 at 15:10
1

Remove the

@Column(name = "STORY_ID") private Integer storyId;

You are already declaring it in @JoinColumn(name="STORY_ID", referencedColumnName="ID")

That is why you are getting the error Multiple writable mappings exist for the field [tip.STORY_ID]

Pau
  • 803
  • 1
  • 6
  • 12
  • Thanks! Indeed there is no other error but the `Tip` entities are not persisted any more – Ionut Feb 28 '12 at 17:35
  • Depending of the implementation, in a bidirectional relationship you have to set both ways of the relationship (I am not sure if in eclipselink it's like that but it could be worth a try). So, try to set the newStory to each of the tips of the list tips. In addition, the line " boolean completed = storyService.create(newStory);" it would be more logic to place it right before transaction.commit; – Pau Feb 29 '12 at 10:16
0

You should not be using PrimaryKeyJoinColumn, just JoinColumn, but having your complete class would help giving a certain answer.

PrimaryKeyJoinColumn would only be used if the story_id was also the id of the Tip (no id in Tip) and there was a duplicate basic mapping for it. It should rarely be used, and is not required in JPA 2.0 anymore as duplicate id mappings are no longer required.

James
  • 17,965
  • 11
  • 91
  • 146
  • Hi! Thank you for your response! I updated my question. I added all the entity attributes and the changes that took place after changing the `@PrimaryKeyJoinColumn` annotation. I hope it is clearer now. – Ionut Feb 28 '12 at 14:38