tags:

views:

48

answers:

2

I'm pretty lost with mapping the following structure with JPA annotations.

+===========+             +====================+
| Offer     |             | Text               |
+-----------+ 1      0..* +--------------------+
| id (pk)   |-------------| textkey (pk)       |
| namekey   |             | languagecode (pk)  |
| ...       |             | text               |
+===========+             | ...                |
                          +====================+

So, each Offer has a name which is i18n-aware. As I have the same cases over and over in the application (Offer also has a i18n comment, Article has a i18n name, etc.) I want to have a Text entity with a composite primary key. For each key there are as many records as there are supported languages. Text samples:

+=====================================+
| textkey    | languagecode | text    |
+=====================================+
| offer5Name | en           | foo     |
| offer5Name | fr           | bar     |
| offer6Name | en           | hello   |
...

The Offer entity would store Text#textkey in its namekey column.

On the Java side I'd like Offer to have a Set of names or even better a Map of names so I could have a method like Text getName(String language) instead of Set<Text> getNames().

What I already have is Text and its composite primary key TextPK:

@Entity
public class Text {

  @EmbeddedId
  private TextPK primaryKey;

  @Column(name = "text")
  private String text;

PK

@Embeddable
public class TextPK implements Serializable {

  @Column(name = "textkey")
  private Long key;

  @Column(name = "languagecode")
  @Enumerated(EnumType.STRING)
  private LanguageCode languageCode;

Question: how do I annotate the 'names' member variable in the Offer class to get what I need?

A: 

Ok, again I'm answering my own question...

JPA 1.0 does not support unidirectional OneToMany (http://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Example_of_a_JPA_2.0_unidirectional_OneToMany_relationship_database) and that's kind of what I would have ended up with.

What worked best for my case was to create an intermediary TextCollection entity. Each domain entity (such as Offer) has a OneToOne relationship to a TextCollection for each of its text attributes (name, description, etc.). The collection entity itself has nothing but an id and bidirectional OneToMany relationship to Text.

@Entity
@Table(name = "textcollection")
public class TextCollection {

  @Id
  @Column(name = "textkey")
  private String key;

  @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "textCollection")
  private final Set<Text> texts = new HashSet<Text>();


@Entity
@Table(name = "text")
public class Text {

  @EmbeddedId
  private TextPK primaryKey;

  @Column(name = "text", nullable = false)
  private String text;

  @ManyToOne
  @JoinColumn(name = "textkey", insertable = false, updatable = false)
  private TextCollection textCollection;

@Entity
@Table(name = "offer")
public class Offer {

  @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
  @JoinColumn(name = "namekey", nullable = false, insertable = true)
  private TextCollection name;
Marcel
A: 

Can you show your solution ?

van
Next time you want to ask a question please post it as a comment instead of an answer (which it isn't). I added some code to my answer.
Marcel