Hibernate - Composite Primary Key contains Foreign Key - java

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.

Related

How to trigger foreign key relation without adding a reference #Entity in hibernate?

The following relationship creates a foreign key mapping
#Entity
public class Department {
#Id
private String name;
//some more fields
}
#Entity
public class Employee {
#Id
private long id;
private String name;
private String designation;
#ManyToOne
#JoinColumn(name = "fk_department_id", foreignKey = #ForeignKey(name="fk_department"))
private Department department;
}
generates:
...CONSTRAINT fk_department FOREIGN KEY (fk_department_id) REFERENCES department (name)
Question: how can I trigger this constraint creation in hibernate without having to create the Department entity?
Eg just adding the foreign key #Id field without an explicit entity reference. But still trigger the fk on initial creation. The following is of course invalid:
#ManyToOne
#JoinColumn(name = "fk_department_id", foreignKey = #ForeignKey(name="fk_department"))
private String department;
You get the intention. Is that possible?
(sidenote: I'm not interested in creating that foreign key link by startup ddl/sql statements).
You'll have to drop #ManyToOne at least, since that's for entities.
The following should work by overriding the column definition to include the foreign key while creating it
#Column(name = "department_id", columnDefinition = "VARCHAR(255), foreign key (department_id) REFERENCES department(name)")
private String department;
Now there's only a column and a constraint defined, but no relation (as far as Hibernate knows) defined between entities.
Essentially copied from Hibernate and JPA: how to make a foreign key constraint on a String but that was darn hard to find, so I'm not just going to close this as a duplicate! ;)

Is it possible to use #JoinTable over a subclass that includes target + join fields?

My model (exemplified) is the following:
CREATE TABLE person (
id INT PRIMARY KEY,
name TEXT
...
);
CREATE TABLE team (
id INT PRIMARY KEY,
name TEXT
....
);
CREATE TABLE team_reference_persons (
team_id INT NOT NULL,
person_id INT NOT NULL,
uses_telephone BOOLEAN,
PRIMARY KEY (team_id, person_id),
FOREIGN KEY (team_id) REFERENCES team(id),
FOREIGN KEY (person_id) REFERENCES person(id)
);
And my JPA defintion:
#Entity
#Table(name = "team")
public class Team {
#Id
private Integer id;
#OneToMany
#JoinTable(name = "team_reference_persons", joinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "person_id", referencedColumnName = "id"))
private List<Person> teamReferencePersons;
...
}
#Entity
#Table(name = "person")
public class Person {
#Id
private UUID id;
private String name;
...
}
So far, so good, when all you need is the person list on the team. But now I need to add the team_reference_persons.uses_telephone property from the join table in my person domain, So I am looking for a way to keep the persons logic, while I create a new subclass.
private class TeamIndividual extends Person {
boolean uses_telephone;
}
Then changing List<Person> on Team entity by List<TeamIndividual>. Is that possible someway? JPA should be indicated in such smart way that it adds the join table property to the final target entity (on both read and save).
No need to extend TeamIndividual to Person.
Annotate TeamIndividual with #Table(name = "team_reference_persons")
Define fields(teamId,personId,uses_telephone) inside TeamIndividual
Annotate fields teamId and PersonId with #ManyToOne and #JoinColumn
Add List to Team without annotation
Try this,It will work..!!

Hibernate JPA, one to one relationship with a composite key and a constant value

I'm attempting to implement a limited type of object level ACL and its lead me to a place where I'm attempting to create a #OneToOne relationship using a composite key with a constant and dynamic value.
I have an Entity with a database id and a constant value defined in the class.
public class Entity{
private static final int objectType = 1;
#Id
Integer id;
}
I have an access_levels table with a composite key of objectId and objectType.
public class AccessLevel {
#EmbeddedId
private AccessLevelKey accessLevelKey;
#Embeddable
class AccessLevelKey implements Serializable{
private Integer objectType;
private Integer objectId;
....
}
}
Schema of access_levels
CREATE TABLE access_levels(
object_type INTEGER NOT NULL,
object_id INTEGER NOT NULL,
....
CONSTRAINT access_levels_type_id PRIMARY KEY (object_type, object_id)
);
I'm attempting to come up with a one to one relationship that Entity can use to fetch and update its associated AccessLevel
After taking a look a the docs on Non-Standard Joins it seems like I need something like this,
Inside of Entity:
#OneToOne
#JoinColumns({
#JoinColumn(name = "id", referencedColumnName = "object_id"),
#JoinColumn(name = "access_levels.object_type", referencedColumnName = "1"),
})
private AccessLevel accessLevel;
However this throws a hibernate MappingException at app launch
Caused by: org.hibernate.MappingException: Unable to find column with logical name: 1 in access_levels
Thanks!

Hibernate one to one with multiple columns

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;
........
}

Java/Hibernate/JPA: cannot persist with compound key -> transient object

my problem is that I cannot save my entity because it contains another entity, mapped by a key that is also a part of this table's primary key. The table looks like this:
table C:
+-----+------+
| id_A | id_B |
+-----+------+
..where idA is the primary key of table A with EntityA and idB the primary key of table B with EntityB.
so its basically a n-to-m relation. This is the entity I'm using for table C:
#Entity
public class EntityC {
private long idA;
private EntityB b;
#Id
#Column(name = "id_A")
public long getIdA() {
return idA;
}
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_B")
public EntityB getB() {
return b;
}
...setters are here...
}
Please note that id_A is mapped as is (the id), while id_B is mapped as its object representation, EntityB. This is what I want to do with it:
EntityC c = new EntityC();
c.setIdA(123);
c.setB(new EntityB());
em.persist(c);
tx.commit();
em.close();
I want to persist EntityB ONLY IF I can persist EntityC.
on tx.commit() I get this exception: org.hibernate.TransientObjectException: object references an unsaved transient instance
I suppose this happens because part of the primary key, id_B, is not saved. But i set cascading to all so there should be no problem!
Why is this not working?
EDIT:
When I do this:
em.persist(c.getB());
em.persist(c);
it works. But can't Hibernate/JPA do that automatically? I thought that's what cascading is good for.
EDIT2:
added an embeddedId instead of id_A and id_B:
#Embeddable
public class EntityCID implements Serializable {
public long idA;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_B", referencedColumnName = "id")
public EntryB b;
}
EntityC now looks like:
#Entity
public class EntityC implements Serializable {
private EntityCID id;
...
#EmbeddedId
public void getId() {
return id;
}
}
but I still get the transient object exception if I don't em.persist(c.getId().b); before em.persist(c). Sticking to that, although it is ugly.
#Trein: it is not bidirectional. EntityB code:
#Entity
public class EntityB implements Serializable {
public long id;
public String text;
}
If you think about it what you are seeing makes perfect sense.
EntityC is is the 'owning side' of the relationship C<>B: it defines the JoinColumn and EntityB has the 'mappedBy' attribute.
So on saving C, order of events would normally be:
insert into C/update C
insert into B/update B
Now in your case this causes issues as obviously C can only be saved if B has been persisted first.
In terms of your statement above: I want to persist "EntityB ONLY IF I can persist EntityC." How can this ever be the case?
JPA has a concept of 'Derived Identifiers', which I am not overly familiar with however is defined in the book Pro JPA as occurring when:
When an identifier in one entity includes a foreign key to another
entity, we call it a derived identifier. Because the entity containing
the derived identifier depends upon another entity for its identity,
we call the first the dependent entity. The entity that it depends
upon is the target of a many-to-one or one-toone relationship from the
dependent entity, and is called the parent entity
Now, despite the original advice that you had two #Id attributes defined and this was wrong it would however appear that having an additional #Id on a 1-2-m is in fact valid in JPA 2 for precisely this case.
The book gives a number of ways of dealing with Derived Identifiers however one example given below looks fairly similar to your case. So you may want to investigate further the #MapsId attribute.
#Entity
public class Project {
#EmbeddedId private ProjectId id;
#MapsId("dept")
#ManyToOne
#JoinColumns({
#JoinColumn(name="DEPT_NUM", referencedColumnName="NUM"),
#JoinColumn(name="DEPT_CTRY", referencedColumnName="CTRY")})
private Department department;
// ...
}
#Embeddable
public class ProjectId implements Serializable {
#Column(name="P_NAME")
private String name;
#Embedded
private DeptId dept;
// ...
}
See further:
How do I properly cascade save a one-to-one, bidirectional relationship on primary key in Hibernate 3.6
Is it a bidirectional relationship? I would suggest you to remove #Id getB() and perform the modifications:
#OneToOne(cascade = CascadeType.ALL, mappedBy = "id_B")
#PrimaryKeyJoinColumn(name = "id_B")
public EntityB getB() {
return b;
}
Your entity class must have only one attribute annotated with #Id. Usually when you need this, you create a class that will store both properties and this will act as a Id Class.
You can not pass new Entity() for reference. Because it won't have any values in it(even primary key). So how can hibernate will insert it as foreign key to the table. And cascade will save your parent object if its not saved,no need to call save method for all. But when you passing new object it won't do.

Categories

Resources