Hibernate one to one with multiple columns - java

How can i bind two columns, using #OneToOne annotation?
Lets say I've 2 tables A and B.
Table A:
id1 (primary key)
id2 (pk)
other columns
Table B:
id1 (pk)
id2 (pk)
other columns
In class A i want to write something like this:
#OneToOne(fetch = FetchType.EAGER, targetEntity = B.class)
#JoinColumn(name = "id1 and id2", referencedColumnName = "id1 and id2")
private B b;
Is it possible to do this using annotations?
Thanks.

What you need is composite keys.Use either #IdClass or #EmbeddedId
Example of #EmbeddedId something like this.
your composite key class:
#Embeddable
public class CompositePK implements Serializable {
protected Integer id1;
protected Integer id2;
// equals, hashCode
}
Your Enity class :
#Entity
public class A{
#EmbeddedId
private CompositePK compkey;
#OneToOne(optional=true, mappedBy="A")
private B b;
........
}

Related

JPA - how to map composite keys hierarchy

I have a db structure similar to this on the picture
table questions has composite primary key (id, cate_id), where cate_id is foreign key to categories(cate_id)
table answers has composite primary key (id, question_id), where question_id is foreign key to questions(id)
from data perspective it looks like this:
CATEGORIES
cate_id
name
C1
Cat 1
C2
Cat 2
QUESTIONS
id
cate_id
question
Q1
C1
what ?
Q2
C1
why ?
Q1
C2
when ?
Q2
C2
who ?
ANSWERS
id
question_id
answer
A1
Q1
aaa
A2
Q1
bbb
A1
Q2
ccc
A2
Q2
ddd
Now I'm trying to map it to JPA model:
the category entity is the simplest:
#Entity
#Table(name = "categories")
public class Category {
#Id
#Column(name = "cate_id")
private Long id;
...
}
now I'm adding the QuestionId to reflect the composite key and Question entity:
#Embeddable
public class QuestionId implements Serializable {
#Column(name = "id")
private Long id;
#Column(name = "cate_id")
private Long categoryId;
//constructors, hashCode, equals, etc
}
#Entity
#Table(name = "questions")
public class Question {
#EmbeddedId
private QuestionId id;
#ManyToOne
#MapsId(value = "categoryId")
#JoinColumn(name = "cate_id")
private Category category;
}
all good so far
the problem starts when I try to add next level: the Answers entity
#Embeddable
public class AnswerId implements Serializable {
#Column(name = "id")
private Long id;
#Column(name = "question_id")
private Long questionId;
//constructors, hashCode, equals, etc
}
#Entity
#Table(name = "answers")
public class Answer {
#EmbeddedId
private AnswerId id;
#ManyToOne
#MapsId(value = "questionId")
#JoinColumn(name = "question_id", referencedColumnName = "id")
private Question question;
}
After creating the Answer entity I'm receiving this error on application startup
Caused by: org.hibernate.AnnotationException: Unable to find column reference in the #MapsId mapping: cate_id
For me it looks like the Answer entity needs access to Categories which makes no sense I guess.
And it has nothing to do with existing database configuration because I have the same result on H2 in-memory db and spring.jpa.hibernate.ddl-auto=create
any suggestions will be more than welcome
thanks in advance
I just noticed that my DB model makes no sense.
The question in the Answers table is not unique accross the system, so there are two ways of solving it:
add a category_id reference to answers table (which is what JPA is
complaining about)
or
make the question_id unique in the system through some sequence generator or anything else

Hibernate - Composite Primary Key contains Foreign Key

I have a similar question as below, but the solution didn't solve my problem.
hibernate composite Primary key contains a composite foreign key, how to map this
I am trying to join 2 tables, each having a composite primary key with partial foreign key reference.
Table A
--------
f1 (pk)
f2 (pk)
f3 (pk)
f4 (pk)
Table B
--------
f1 (pk, fk)
f2 (pk, fk)
f5 (pk)
f6 (pk)
I created A, APK, B, BPK
In A:
private Set<B> bSet;
#OneToMany(targetEntity=B.class, cascade = CascadeType.ALL, mappedBy= "bpk.a")
public Set<MovesEntity> getBSet() {
return bSet;
}
In BPK:
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="f1", referencedColumnName="f1", nullable=false, insertable=false, updatable = false),
#JoinColumn(name="f2", referencedColumnName="f2", nullable=false, insertable=false, updatable = false)
})
public A getA() {
return a;
}
The above approach gives me this Exception:
AnnotationException: referencedColumnNames(f1, f2) of entity.BPK.bpk.a
referencing com.example.entity.A not mapped to a single property
Can you please help ?
Assuming f1 and F2 uniquely identify A and exist within APK, you can use JPA 2.0's derived IDs for this in a few ways. Easiest to show would be:
#Entity
#IdClass(BPK.class)
public class B {
#ID
String f5;
#ID
String f6;
#ID
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="f1", referencedColumnName="f1", nullable=false),
#JoinColumn(name="f2", referencedColumnName="f2", nullable=false)
})
A a;
}
public class BPK {
String f5;
String f6;
APK a;
}
Key points here are that B has a reference to A that control the foriegn key fields f1 and f2, and A's primary key is used within B's primary key - with the same name as the relationship. Another way to map it would be to make B's PK an embeddid id, but embedded IDs still cannot have reference mappings, so it might look:
#Entity
#IdClass(BPK.class)
public class B {
#EmbeddedId
BPK pk;
#MapsId("apk")
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="f1", referencedColumnName="f1", nullable=false),
#JoinColumn(name="f2", referencedColumnName="f2", nullable=false)
})
A a;
}
#Embeddable
public class BPK {
String f5;
String f6;
APK apk;
}
Notice the mapsId - this tells JPA that the columns in the embedded 'apk' reference use the foreign key fields from the reference mapping as pulled from A. JPA will populate the foreign keys for you from the reference mapping, important if you are using sequencing.

How to map a one-many relationship that has an attribute (Hibernate)

I have 2 entities O and D with a one to many relationship from O (one) to D (many). The relationship itself has an attribute - count.
What is the best way to model this using hibernate?
What I have currently is another entity OD representing the relationship. It has its own artificial key
The abbreviated version of the entities is as below
#Entity
class O {
#Id
private Long id;
#OneToMany(mappedBy = "o")
private Set<OD> ods;
}
#Entity
class OD {
#Id
private Long id;
#ManyToOne
private O o;
// This is uni-directional reln
#OneToOne
private D d;
private int count;
}
Is this the best way? I do not like the fact that the relationship has its own id, but is there a better way to model this relationship?
You can model the OD as #Embeddable, and change the owning side from #OneToMany to #ElementCollection e.g.
#Entity
public class O {
#Id
private Long id;
#ElementCollection
#CollectionTable( name = "OD")
private Set<OD> ods;
}
#Embeddable
public class OD {
// This is uni-directional reln
#OneToOne
private D d;
private int count;
}
The DDL statements would be almost the same with the distinction that the life-cycle of the OD would always be dependent on the life of its parent object, and would not hold an identity of its own

EclipseLink: Map of key to entity where the PK of the entity is the key in the map

What I am looking to do is have the primary key for Object3 entity contain both its parent Object1(#ManyToOne) and the Object2 that is the key in the map that the Object3 is paired with in the Object1 (property1). Is this possible to do with EclipseLink, perhaps with an annotation?
I hope that made sense, to summarize: The PK of Object3 is (Object1,Object2)
#Entity
public class Object1{
...
private Map<Object2,Object3> property1;
...
}
#Entity
public class Object3{
#Column(name = "c1")
private int number;
#ManyToOne
#JoinColumn(name = "Object1")
private Object1 object1;
...
}
Thanks for the help!
Look at it this way - what are you going to pass to em.find and obtain a particular Object3 instance? Object3 needs mappings to all its foreign key fields to use for its primary key or it isn't an independent entity. Something like:
#Entity
#IdClass(Object3PK.class)
public class Object3{
#Column(name = "c1")
private int number;
#Id
#ManyToOne
#JoinColumn(name = "Object1")
private Object1 object1;
#Id
#ManyToOne
#JoinColumn(name = "Object2")
private Object2 object2;
...
}
public class Object3PK{
private int object1;
private int object2;
}
In the above, Object3PK must use the same types as the primary keys within Object1 and Object2. This is described here
In Object1 then, you can use a standard OneToMany mapping that points to the Object1 ManyToOne mapping back. The only part missing is a MapKey :
#Entity
public class Object1{
...
#OneToMany(mappedBy="object1")
#MapKey(name="object2")
private Map<Object2,Object3> property1;
...
}

JPA: persisting entity with composite primary key

I have two tables in database, A and B. Table B has an id composed of two fields. One of them is a foreign key to A. Id of A is automatically generated on insert by a sequence.
A:
ID (PK)
(*other fields*)
B:
SOME_FIELD (PK)
A_ID (PK, FK to A)
I have mapped the two tables in JPA (Hibernate) following JPA specification, in this way:
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "A_SEQ", sequenceName = "A_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "A_SEQ")
#Column(name = "ID")
private Long id;
(...)
}
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
#AttributeOverride(name = "someField", column = #Column(name = SOME_FIELD))
private BPK pk;
#MapsId("aId")
#ManyToOne
#JoinColumn(name = "A_ID")
private A a;
(...)
}
#Embeddable
public class BPK implements Serializable {
private Long aId;
private String someField;
#Override
public boolean equals(Object o) {
(...)
}
#Override
public boolean hashCode() {
(...)
}
(...)
}
The problem is that when I try to save an B object calling entityManager.persist(b), where b.a is set to an A object, which exists already in database, I get an exception:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: package.name.A
I don't know why this happens. I'm trying to save object of class B, not A. Is my entity class wrong? Or maybe I shouldn't use persist here?
It could be that the entity A is no longer being held by entity manager. Have you tried setting B.a with a "fresh" instance of A?
b.setA(get(b.a));
entityManager.persist(b);
The get(b.a) method can be whatever you usually use to find entity A from your datasource e.g. entityManager.getReference(A.class, a.id);

Categories

Resources