Two tables like:
CREATE TABLE foo (
id INT PRIMARY KEY,
x TEXT
);
CREATE TABLE bar (
foo_id INT REFERENCES foo (id) ON DELETE CASCADE,
y TEXT,
z TEXT
);
...can be mapped like so:
@Table(name = "foo")
@SecondaryTable(name = "bar", pkJoinColumns = @PrimaryKeyJoinColumn(name = "foo_id", referencedColumnName = "id"))
class Foo {
@Id
int id;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "y", column = @Column(table = "bar")),
@AttributeOverride(name = "z", column = @Column(table = "bar"))
})
Bar bar;
}
@Embeddable
class Bar {
String y;
String z;
}
Is there a less awkward way to do this mapping, using either just standard JPA annotations, or else Hibernate-specific annotations (and without introducing a parent reference in the embeddable object)?
Compare this to how easily a collection of @Embeddable objects can be referenced using an @ElementCollection and @CollectionTable.
-
Can't you simply map it using @ElementCollection but hide that implementation detail i.e. don;t expose the collection but provide set/getBar methods that manipulate the collection?Alan Hay– Alan Hay2017年04月25日 08:22:12 +00:00Commented Apr 25, 2017 at 8:22
-
That would work, too, but would be even uglier than the verbose annotations above :-)ejain– ejain2017年04月25日 16:22:15 +00:00Commented Apr 25, 2017 at 16:22
1 Answer 1
Sorry for the late answer, but the question interested me so I figured I'd take a crack at it.
A more elegant solution is to make the contents of bar an Entity instead of an Embeddable, and to use a OneToOne relationship instead of an Embedded. You also need to remember to use @PrimaryKeyJoinColumn instead of @JoinColumn:
@Entity
@Table(name = "bar")
class Bar {
@Id
int fooId;
String y;
String z;
}
...
@Entity
@Table(name = "foo")
class Foo {
@Id
int id;
@OneToOne
@PrimaryKeyJoinColumn
Bar bar;
}
The biggest advantage is that Foo no longer needs to know about the contents of Bar. Depending on your DB's naming strategy, you may also need to specify the column name of fooId in Bar with something like @Column(name = "foo_id").
16 Comments
foo_id INT REFERENCES foo (id) ON DELETE CASCADE. That's all the unique identifier you need. If you're saying that there can be multiple rows in that table (bar) that refer to the same Foo entity, then even your original SecondaryTable solution wouldn't work. If the SecondaryTable solution works, so will this one.@PrimaryKeyJoinColumn with @MapsId @JoinColumn(name = "id"), and I'll accept your solution!@Embedded solution creates a foreign key that matches the DDL also posted above; looks like using @MapsId allows us to have a foreign key without having to make the relationship bidirectional.@MapsId solution is based on your solution, and doesn't use @Embedded or @SecondaryTable. It's possible that this is a blatant misuse of this annotation, but it appears to work, and doesn't require changing the Bar domain object to have a reference back to a Foo object.