#Embeddable is Foo.class' PK and Bar.class' FK to Foo.class - java

I am trying to define JPA layer over a db I cannot redesign.
That layer contains a OneToMany relationship where the Many part has a foreign key consisting of the same 2 fields that the one part has as primary key.
#Embeddable
public class FooKey implements Serializable {
#Column(name="foo_id")
private String id;
private String secondaryId;
}
public class Foo {
#EmbeddedId
private FooKey id;
(...)
}
public class Bar {
#Id
private Long id;
(...)
//#Embedded FooKey fooKey;
#ManyToOne
private Foo foo;
}
How can I solve this? I am getting the error #Column(s) not allowed on a #ManyToOne property

I managed to do it by mean of:
#ManyToOne
#JoinColumns({
#JoinColumn (name = "foo_id", referencedColumnName = "foo_id"),
#JoinColumn (name = "secondary_id", referencedColumnName = "secondary_id")
})
private Foo foo;
Although if anyone has a more elegant solution, I will accept it

Related

Foreign key composed of two fields one in primary key and one non primary key JPA

I'm trying to implement a weird database design and I can't figure out how to do it. There is a many to one relationship between two entities having both of them a composed primary key. The foreign key is composed of a field present in the primary key of the other entitie and a field that is not in the primary key. Hope someone could help me.
#Entity
#Table(name = "A")
public class A {
#EmbeddedId
private Aid id;
#JoinColumn(name = "FOREIGN_KEY_FIELD2", referencedColumnName = "foreignKeyField2")
private B foreignKeyField2;
}
#Embeddable
public class Aid{
#JoinColumn(name = "FOREIGN_KEY_FIELD1", referencedColumnName = "foreignKeyField1")
private B foreignKeyField1;
private String otherField;
}
#Entity
#Table(name = "B")
public class B {
#EmbeddedId
private Bid id;
#OneToMany(targetEntity = A.class, fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(name = "FOREIGN_KEY_FIELD1", referencedColumnName = "foreignKeyField1"),
#JoinColumn(name = "FOREIGN_KEY_FIELD2", referencedColumnName = "foreignKeyField2") })
private List<A> as;
}
#Embeddable
public class Bid {
private String foreignKeyField1;
private String otherField;
}
Best regards.
Thanks in advance.
You can't solve this using #EmbeddedId you must define a IdClass.
Here is an example:
#Entity #IdClass(PartPK.class)
public class Part {
#Id int partNo;
#Id #ManyToOne
Supplier supplier;
}  
public class PartPK {
int partNo;
int supplier;
}  

Hibernate JPA - Map to an embedded id [duplicate]

I'm using Hibernate with annotations (in spring), and I have an object which has an ordered, many-to-one relationship which a child object which has a composite primary key, one component of which is a foreign key back to the id of the parent object.
The structure looks something like this:
+=============+ +================+
| ParentObj | | ObjectChild |
+-------------+ 1 0..* +----------------+
| id (pk) |-----------------| parentId |
| ... | | name |
+=============+ | pos |
| ... |
+================+
I've tried a variety of combinations of annotations, none of which seem to work. This is the closest I've been able to come up with:
#Entity
public class ParentObject {
#Column(nullable=false, updatable=false)
#Id #GeneratedValue(generator="...")
private String id;
#OneToMany(mappedBy="parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})
#IndexColumn(name = "pos", base=0)
private List<ObjectChild> attrs;
...
}
#Entity
public class ChildObject {
#Embeddable
public static class Pk implements Serializable {
#Column(nullable=false, updatable=false)
private String parentId;
#Column(nullable=false, updatable=false)
private String name;
#Column(nullable=false, updatable=false)
private int pos;
#Override
public String toString() {
return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
}
...
}
#EmbeddedId
private Pk pk;
#ManyToOne
#JoinColumn(name="parentId")
private ParentObject parent;
...
}
I arrived at this after a long bout of experimentation in which most of my other attempts yielded entities which hibernate couldn't even load for various reasons.
UPDATE: Thanks all for the comments; I have made some progress. I've made a few tweaks and I think it's closer (I've updated the code above). Now, however, the issue is on insert. The parent object seems to save fine, but the child objects are not saving, and what I've been able to determine is that hibernate is not filling out the parentId part of the (composite) primary key of the child objects, so I'm getting a not-unique error:
org.hibernate.NonUniqueObjectException:
a different object with the same identifier value was already associated
with the session: [org.kpruden.ObjectChild#null.attr1[0]]
I'm populating the name and pos attributes in my own code, but of course I don't know the parent ID, because it hasn't been saved yet. Any ideas on how to convince hibernate to fill this out?
Thanks!
The Manning book Java Persistence with Hibernate has an example outlining how to do this in Section 7.2. Fortunately, even if you don't own the book, you can see a source code example of this by downloading the JPA version of the Caveat Emptor sample project (direct link here) and examining the classes Category and CategorizedItem in the auction.model package.
I'll also summarize the key annotations below. Do let me know if it's still a no-go.
ParentObject:
#Entity
public class ParentObject {
#Id #GeneratedValue
#Column(name = "parentId", nullable=false, updatable=false)
private Long id;
#OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
#IndexColumn(name = "pos", base=0)
private List<ChildObject> attrs;
public Long getId () { return id; }
public List<ChildObject> getAttrs () { return attrs; }
}
ChildObject:
#Entity
public class ChildObject {
#Embeddable
public static class Pk implements Serializable {
#Column(name = "parentId", nullable=false, updatable=false)
private Long objectId;
#Column(nullable=false, updatable=false)
private String name;
#Column(nullable=false, updatable=false)
private int pos;
...
}
#EmbeddedId
private Pk id;
#ManyToOne
#JoinColumn(name="parentId", insertable = false, updatable = false)
#org.hibernate.annotations.ForeignKey(name = "FK_CHILD_OBJECT_PARENTID")
private ParentObject parent;
public Pk getId () { return id; }
public ParentObject getParent () { return parent; }
}
You should incorporate the ParentObject reference just into ChildObject.Pk rather than map parent and parentId separately:
(getters, setters, Hibernate attributes not related to problem and member access keywords omitted)
class ChildObject {
#Embeddable
static class Pk {
#ManyToOne...
#JoinColumn(name="parentId")
ParentObject parent;
#Column...
String name...
...
}
#EmbeddedId
Pk id;
}
In ParentObject you then just put #OneToMany(mappedBy="id.parent") and it works.
Firstly, in the ParentObject, "fix" the mappedBy attribute that should be set to "parent". Also (but this is maybe a typo) add an #Id annotation:
#Entity
public class ParentObject {
#Id
#GeneratedValue
private String id;
#OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
#IndexColumn(name = "pos", base=0)
private List<ObjectChild> attrs;
// getters/setters
}
Then, in ObjectChild, add a name attribute to the objectId in the composite key:
#Entity
public class ObjectChild {
#Embeddable
public static class Pk implements Serializable {
#Column(name = "parentId", nullable = false, updatable = false)
private String objectId;
#Column(nullable = false, updatable = false)
private String name;
#Column(nullable = false, updatable = false)
private int pos;
}
#EmbeddedId
private Pk pk;
#ManyToOne
#JoinColumn(name = "parentId", insertable = false, updatable = false)
private ParentObject parent;
// getters/setters
}
AND also add insertable = false, updatable = false to the #JoinColumn because we are repeating the parentId column in the mapping of this entity.
With these changes, persisting and reading the entities is working fine for me (tested with Derby).
After much experimentation and frustration, I eventually determined that I cannot do exactly what I want.
Ultimately, I went ahead and gave the child object its own synthetic key and let Hibernate manage it. It's a not ideal, since the key is almost as big as the rest of the data, but it works.
Found this question searching for the answer to it's problem, but it's answers didn't solve my problem, because I was looking for #OneToMany which isn't as good of a fit for the table structure I was going after. #ElementCollection is the right fit in my case. One of the gotchas of it I believe though is that it looks at the entire row of relations as being unique, not just the rows id.
#Entity
public class ParentObject {
#Column(nullable=false, updatable=false)
#Id #GeneratedValue(generator="...")
private String id;
#ElementCollection
#CollectionTable( name = "chidren", joinColumns = #JoinColumn( name = "parent_id" ) )
private List<ObjectChild> attrs;
...
}
#Embeddable
public static class ObjectChild implements Serializable {
#Column(nullable=false, updatable=false)
private String parentId;
#Column(nullable=false, updatable=false)
private String name;
#Column(nullable=false, updatable=false)
private int pos;
#Override
public String toString() {
return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
}
... getters and setters REQUIRED (at least they were for me)
}
It seems that you got pretty close, and I am trying to do the same thing in my current system. I started with the surrogate key but would like to remove it in favor of a composite primary key consisting of the parent's PK and the index in the list.
I was able to get a one-to-one relationship that shares the PK from the master table by using a "foreign" generator:
#Entity
#GenericGenerator(
name = "Parent",
strategy = "foreign",
parameters = { #Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
#Id
#GeneratedValue(generator = "Parent")
#Column(name = "parent_id")
private int parentId;
#OneToOne(mappedBy = "childObject")
private ParentObject parentObject;
...
}
I wonder if you could add the #GenericGenerator and #GeneratedValue to solve the problem of Hibernate not assigning the parent's newly acquired PK during insertion.
After saving the Parent object, you have to explicitly set the parentId in the Child objects for the inserts on the Child objects to work.
After spending three days on this, I think I have found a solution, but to be honest, I don't like it and it can definitely be improved. However, it works and solves our problem.
Here is your entity constructor, but you could also do it in the setter method.
Also, I used a Collection object but it should be same or similar with List:
...
public ParentObject(Collection<ObjectChild> children) {
Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
for(ObjectChild obj:children){
obj.setParent(this);
occ.add(obj);
}
this.attrs = occ;
}
...
Basically, as someone else suggested, we must first manually set all the children's parent id before saving the parent (along with all children)
I was badly looking for an answer but couldn't find a working solution. Though I had the OneToMany in parent correctly and ManyToOne in child correctly, during parent's save, child's key was not getting assigned, the auto-generated value from parent.
My problem was fixed upon adding an annotation javax.persistence.MapsId above the #ManyToOne mapping in the child entity (Java class)
#MapsId("java_field_name_of_child's_composite_key_that_needs_the_value_from_parent")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARENT_ID", nullable = false, insertable = false, updatable = false)
private Parent parent;
This is on top of what was answered by #Pascal Thivent (answered on Apr 10 '10 at 1:40)
Please refer to the example code snippet in his post, earlier in this thread.
Thanks,
PJR.

Hibernate creating relation on #JoinTable

I have two tables which have Many-to-Many relations which have a JoinTable USER_SERVICES as below.
#Entity
public class User implements Serializable {
#NotNull
#Column(unique=true)
private String username;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "USER_SERVICES",
joinColumns = {#JoinColumn(name = "id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "", referencedColumnName = "name")})
private Set<Services> services;
// Getters and Setters
}
#Entity
public class Services implements Serializable {
#NotNull
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long serviceId;
#NotNull
#Column(unique=true)
private String name;
//Getters and Setters
}
The above code creates a table USER_SERVICES, but I also want to have a Many-to-Many relation on the table USER_SERVICES with another table RATINGS which would result in another table USER_SERVICES_RATINGS. how can I define this relation with Hibernate/JPA annotations?
Bi-Directional Many to Many using user managed join table object (Relatively common)
Commonly used when you want to store extra information on the join object such as the date the relationship was created.
public class Foo{
private UUID fooId;
#OneToMany(mappedBy = "bar", cascade=CascadeType.ALL)
private List<FooBar> bars;
}
public class Bar{
private UUID barId;
#OneToMany(mappedBy = "foo", cascade=CascadeType.ALL)
private List<FooBar> foos;
}
#Entity
#Table(name="FOO_BAR")
public class FooBar{
private UUID fooBarId;
#ManyToOne
#JoinColumn(name = "fooId")
private Foo foo;
#ManyToOne
#JoinColumn(name = "barId")
private Bar bar;
//You can store other objects/fields on this table here.
}
You need to create an explicit UserServices entity and setup the relationship to the Ratings entity per your needs.
Remember that in hibernate you model relationships between entities (i.e. your java objects), not db tables.

Composite primary key, foreign key. Reference to object or key?

I have two classes Foo and Bar. The tables in the database look like this:
|Foo|
|id : INT (PK) | bar_id : INT (PK, FK) |
|Bar|
|id : INT (PK) |
Normally I would map it like this:
#Entity
public class Bar
{
#Id
#Column(name = "id")
private int id;
#OneToMany
private Set<Foo> foo;
}
#Entity
public class Foo
{
#EmbeddedId
private FooPK key;
#MapsId("barId")
#ManyToOne
#JoinColumn(name = "bar_id", referencedColumnName = "id")
private Bar bar;
}
#Embeddable
public class FooPK
{
#Column(name = "id")
private int id;
#Column(name = "bar_id")
private int barId;
}
However the id's in FooPK are loosely mapped and need to be connected manually. I would prefer a solution that maps using Objects in stead of loose ids.
I tried the following but (of course) it didn't work, but I think it gives an idea of what I would like to achieve:
#Entity
public class Bar
{
#Id
#Column(name = "id")
private int id;
#OneToMany
private Set<Foo> foo;
}
#Entity
public class Foo
{
#EmbeddedId
private FooPK key;
#MapsId("barId")
#ManyToOne
#JoinColumn(name = "bar_id", referencedColumnName = "id")
#Access(AccessType.FIELD)
private Bar getBar()
{
return key.getBar();
}
}
#Embeddable
public class FooPK
{
#Column(name = "id")
private int id;
#Transient
private Bar bar;
//....
#Column(name = "bar_id")
#Access(AccessType.PROPERTY)
private int getBarId
{
return bar.getId();
}
}
Another problem with the latter solution is that the getBarId() method in FooPK needs to have a setBarId(Int) method. Setting the Object using the ID can be done by accessing the data access layer, however this (in my opinion) violates the separation of business/domain/data layers.
So what to do? Go with the first solution and keep the ids in sync manually or is there another (best) practice?
Referring to the JPA 2: composite primary key classes discussion, make the following changes to the Foo and FooPK classes:
#Entity
public class Foo {
#EmbeddedId
private FooPK key;
#MapsId("barId") //references EmbeddedId's property
#JoinColumn(name = "bar_id", referencedColumnName = "id")
#ManyToOne
private Bar bar;
}
#Embeddable
public class FooPK {
#Column(name = "id")
private int id;
#Column(name = "bar_id")
private int barId;
}
I suggest first making it work with FIELD access and then apply PROPERTY access.
So what to do? Go with the first solution and keep the ids in sync
manually or is there another (best) practice?
Save yourself from pain - generate ids automatically.

#OneToMany and composite primary keys?

I'm using Hibernate with annotations (in spring), and I have an object which has an ordered, many-to-one relationship which a child object which has a composite primary key, one component of which is a foreign key back to the id of the parent object.
The structure looks something like this:
+=============+ +================+
| ParentObj | | ObjectChild |
+-------------+ 1 0..* +----------------+
| id (pk) |-----------------| parentId |
| ... | | name |
+=============+ | pos |
| ... |
+================+
I've tried a variety of combinations of annotations, none of which seem to work. This is the closest I've been able to come up with:
#Entity
public class ParentObject {
#Column(nullable=false, updatable=false)
#Id #GeneratedValue(generator="...")
private String id;
#OneToMany(mappedBy="parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})
#IndexColumn(name = "pos", base=0)
private List<ObjectChild> attrs;
...
}
#Entity
public class ChildObject {
#Embeddable
public static class Pk implements Serializable {
#Column(nullable=false, updatable=false)
private String parentId;
#Column(nullable=false, updatable=false)
private String name;
#Column(nullable=false, updatable=false)
private int pos;
#Override
public String toString() {
return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
}
...
}
#EmbeddedId
private Pk pk;
#ManyToOne
#JoinColumn(name="parentId")
private ParentObject parent;
...
}
I arrived at this after a long bout of experimentation in which most of my other attempts yielded entities which hibernate couldn't even load for various reasons.
UPDATE: Thanks all for the comments; I have made some progress. I've made a few tweaks and I think it's closer (I've updated the code above). Now, however, the issue is on insert. The parent object seems to save fine, but the child objects are not saving, and what I've been able to determine is that hibernate is not filling out the parentId part of the (composite) primary key of the child objects, so I'm getting a not-unique error:
org.hibernate.NonUniqueObjectException:
a different object with the same identifier value was already associated
with the session: [org.kpruden.ObjectChild#null.attr1[0]]
I'm populating the name and pos attributes in my own code, but of course I don't know the parent ID, because it hasn't been saved yet. Any ideas on how to convince hibernate to fill this out?
Thanks!
The Manning book Java Persistence with Hibernate has an example outlining how to do this in Section 7.2. Fortunately, even if you don't own the book, you can see a source code example of this by downloading the JPA version of the Caveat Emptor sample project (direct link here) and examining the classes Category and CategorizedItem in the auction.model package.
I'll also summarize the key annotations below. Do let me know if it's still a no-go.
ParentObject:
#Entity
public class ParentObject {
#Id #GeneratedValue
#Column(name = "parentId", nullable=false, updatable=false)
private Long id;
#OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
#IndexColumn(name = "pos", base=0)
private List<ChildObject> attrs;
public Long getId () { return id; }
public List<ChildObject> getAttrs () { return attrs; }
}
ChildObject:
#Entity
public class ChildObject {
#Embeddable
public static class Pk implements Serializable {
#Column(name = "parentId", nullable=false, updatable=false)
private Long objectId;
#Column(nullable=false, updatable=false)
private String name;
#Column(nullable=false, updatable=false)
private int pos;
...
}
#EmbeddedId
private Pk id;
#ManyToOne
#JoinColumn(name="parentId", insertable = false, updatable = false)
#org.hibernate.annotations.ForeignKey(name = "FK_CHILD_OBJECT_PARENTID")
private ParentObject parent;
public Pk getId () { return id; }
public ParentObject getParent () { return parent; }
}
You should incorporate the ParentObject reference just into ChildObject.Pk rather than map parent and parentId separately:
(getters, setters, Hibernate attributes not related to problem and member access keywords omitted)
class ChildObject {
#Embeddable
static class Pk {
#ManyToOne...
#JoinColumn(name="parentId")
ParentObject parent;
#Column...
String name...
...
}
#EmbeddedId
Pk id;
}
In ParentObject you then just put #OneToMany(mappedBy="id.parent") and it works.
Firstly, in the ParentObject, "fix" the mappedBy attribute that should be set to "parent". Also (but this is maybe a typo) add an #Id annotation:
#Entity
public class ParentObject {
#Id
#GeneratedValue
private String id;
#OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
#IndexColumn(name = "pos", base=0)
private List<ObjectChild> attrs;
// getters/setters
}
Then, in ObjectChild, add a name attribute to the objectId in the composite key:
#Entity
public class ObjectChild {
#Embeddable
public static class Pk implements Serializable {
#Column(name = "parentId", nullable = false, updatable = false)
private String objectId;
#Column(nullable = false, updatable = false)
private String name;
#Column(nullable = false, updatable = false)
private int pos;
}
#EmbeddedId
private Pk pk;
#ManyToOne
#JoinColumn(name = "parentId", insertable = false, updatable = false)
private ParentObject parent;
// getters/setters
}
AND also add insertable = false, updatable = false to the #JoinColumn because we are repeating the parentId column in the mapping of this entity.
With these changes, persisting and reading the entities is working fine for me (tested with Derby).
After much experimentation and frustration, I eventually determined that I cannot do exactly what I want.
Ultimately, I went ahead and gave the child object its own synthetic key and let Hibernate manage it. It's a not ideal, since the key is almost as big as the rest of the data, but it works.
Found this question searching for the answer to it's problem, but it's answers didn't solve my problem, because I was looking for #OneToMany which isn't as good of a fit for the table structure I was going after. #ElementCollection is the right fit in my case. One of the gotchas of it I believe though is that it looks at the entire row of relations as being unique, not just the rows id.
#Entity
public class ParentObject {
#Column(nullable=false, updatable=false)
#Id #GeneratedValue(generator="...")
private String id;
#ElementCollection
#CollectionTable( name = "chidren", joinColumns = #JoinColumn( name = "parent_id" ) )
private List<ObjectChild> attrs;
...
}
#Embeddable
public static class ObjectChild implements Serializable {
#Column(nullable=false, updatable=false)
private String parentId;
#Column(nullable=false, updatable=false)
private String name;
#Column(nullable=false, updatable=false)
private int pos;
#Override
public String toString() {
return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
}
... getters and setters REQUIRED (at least they were for me)
}
It seems that you got pretty close, and I am trying to do the same thing in my current system. I started with the surrogate key but would like to remove it in favor of a composite primary key consisting of the parent's PK and the index in the list.
I was able to get a one-to-one relationship that shares the PK from the master table by using a "foreign" generator:
#Entity
#GenericGenerator(
name = "Parent",
strategy = "foreign",
parameters = { #Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
#Id
#GeneratedValue(generator = "Parent")
#Column(name = "parent_id")
private int parentId;
#OneToOne(mappedBy = "childObject")
private ParentObject parentObject;
...
}
I wonder if you could add the #GenericGenerator and #GeneratedValue to solve the problem of Hibernate not assigning the parent's newly acquired PK during insertion.
After saving the Parent object, you have to explicitly set the parentId in the Child objects for the inserts on the Child objects to work.
After spending three days on this, I think I have found a solution, but to be honest, I don't like it and it can definitely be improved. However, it works and solves our problem.
Here is your entity constructor, but you could also do it in the setter method.
Also, I used a Collection object but it should be same or similar with List:
...
public ParentObject(Collection<ObjectChild> children) {
Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
for(ObjectChild obj:children){
obj.setParent(this);
occ.add(obj);
}
this.attrs = occ;
}
...
Basically, as someone else suggested, we must first manually set all the children's parent id before saving the parent (along with all children)
I was badly looking for an answer but couldn't find a working solution. Though I had the OneToMany in parent correctly and ManyToOne in child correctly, during parent's save, child's key was not getting assigned, the auto-generated value from parent.
My problem was fixed upon adding an annotation javax.persistence.MapsId above the #ManyToOne mapping in the child entity (Java class)
#MapsId("java_field_name_of_child's_composite_key_that_needs_the_value_from_parent")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARENT_ID", nullable = false, insertable = false, updatable = false)
private Parent parent;
This is on top of what was answered by #Pascal Thivent (answered on Apr 10 '10 at 1:40)
Please refer to the example code snippet in his post, earlier in this thread.
Thanks,
PJR.

Categories

Resources