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

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.

Related

JPA: Mapping crossed OneToOne and ManyToOne relations

I have two entities, which we'll call A and B. B always has A as a parent with a ManyToOne relation.
However, I need A to have a OneToOne relation with the latest record inserted in table B.
This is because I need to save multiple versions of B but 99% of the time will only need to use the most recent one.
This looks something like this:
#Data
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#OneToOne(optional = false)
private B latest;
}
#Data
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#Column(nullable = false)
private Date lastModified;
#ManyToOne(optional = false)
private A parent;
}
Now, the issue at hand is that I cannot seem to persist these entities as one always appears to be transient:
A cannot be persisted because latest references B, yet B is not persisted.
B cannot be persisted because parent references A, yet A is not persisted.
Attempting to do so results in:
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : B.parent -> A
I tried wrapping the code responsible for persisiting them in a #Transactional method but the same happens:
#Transactional
public void saveAB(A parent, B child) {
parent.setLatest(child);
child.setParent(parent);
Arepository.save(parent);
Brepository.save(child);
}
I also thought of disregarding the OneToOne relation from A to B, instead having latest as a transient #Formula field which would query B to take the most recent record. However, #Formula seems to be limited to primitives, not full entities.
What would be the proper way to do this with JPA? Am I approaching this the wrong way?
Since A and B depend on each other they should probably be considered a single aggregate with A being the aggregate root.
This means you'd have only an ARepository and also CascadeType.ALL on the relationships.
The solution was to apply #JoinFormula as explained here.
#Data
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Setter(AccessLevel.NONE)
private Long id;
/* Properties
...
*/
#ManyToOne
#JoinFormula(value = "(SELECT b.id FROM b " +
"WHERE b.id = id ORDER BY b.lastModified DESC LIMIT 1)")
private B latest;
}
Then on B:
#ManyToOne(optional = false)
private A parent;

Why in this Hibernate mapping it is used #ManyToOne instead #OneToOne?

I am absolutly new in Hibernate development and I have the following problem.
I have 2 entity classes that maps 2 DB tables:
1) The first entity class (the main one) is named KM_ProjectInfo and map a DB table named KM_PROJECT.
2) The second entity class is named KM_ProjectInfoStatus and map a DB table named KM_PROJECT_INFO_STATUS.
So the second one represent a specific field of the first one (a status of the row representd by an instance of the KM_ProjectInfo class). Infact I have something like this:
1) KM_ProjectInfo class:
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#Column(name = "name")
private String name;
#Column(name = "technology")
private String technology;
#ManyToOne
#JoinColumn(name = "idCountry")
private KMCountry country;
#Column(name = "power")
private long power;
#Column(name = "cod")
private String cod;
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
// GETTERS & SETTERS
}
2) KM_ProjectInfoStatus:
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#Column(name = "foldertech")
private Long foldertech;
#Column(name = "folderproject")
private Long folderproject;
// GETTERS & SETTERS
}
So, as you can see in the previous snippet, the KM_ProjectInfoStatuss is a field of the KM_ProjectInfo because I want that it contains the primary key of this table as foreign key.
In the logic of my application I want that at one row of the KM_PROJECT table (so at one instance of the KM_ProjectInfo entity class) is associated a single row of the KM_PROJECT_INFO_STATUS (one instance of the KM_ProjectInfoStatus entity class) because it represent a specific status for the KM_PROJECT row.
In my code I have:
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
but I think that is wrong because at one row of my first table it is associated a specific single row of the second table. But maybe I am missing something about how Hibernate work.
Can you help me to understand what I am missing? What it work? Why I have #ManyToOne instead #OneToOne?
Tnx
It all depends on how you want to model things. In terms of Database structure, OneToOne and ManyToOne are implemented in the same way:
One or more JoinColumns which makes a foreign key pointing to the primary key of the other table.
So both solutions correctly map to your database, but it depends if you want to allow several KM_ProjectInfo to point to the same KM_ProjectInfoStatus, or only allow a single one.
Note that, even though you would declare a OneToOne, you could still end up with multiple KM_ProjectInfo pointing to the same KM_ProjectInfoStatus if you don't manipulate Hibernate properly.
Here you did not declare the reverse relationship, but if you did, the declaration would have to be different:
In case of a OneToOne, you would have a KM_ProjectInfo member
In case of a OneToMany (reverse of ManyToOne), you would have a Collection<KM_ProjectInfo> member
From the description it seems you want to have one-to-one relationship. That is the project entity should have its very own status not shared by any other project. You could achieve this by using #OneToOne as below.
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#OneToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
}
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#OneToOne(mappedBy="idProjectInfoStatus")
private KM_ProjectInfo project;
}
This way you can have specific status for the KM_PROJECT.
Coming back to #ManyToOne, you will want to have this if you want to share the same status with multiple projects, but that's not what you want in your case. I have tried to explain mappings in simple way here One-to-One mapping.

Many-to-one unidirectional relation in DataNucleus

For the context, client-side I use the MVP pattern, so the view with the One list knows only the ID, and when my new Many is received on the server, I want to be able to just update the One's foreign key, with a "setOneId" or an empty One object with an ID set to the wanted value.
So I try to create a many-to-one unidirectional in DataNucleus, and I'm struggling a bit. I'm ok to use JDO or JPA, I don't really care. In JPA, I tried this :
#Entity
public class Many {
#Id
String id;
#ManyToOne
#Join(name = "idOne")
One one;
}
#Entity
public class One {
#Id
String id;
}
It's almost what I want. The one-to-many is created but with a join table. I want to have a direct relation. And when I insert/update a Many, I don't want to insert/update the related One, just update the idOne with the good id in my Many object.
I found this blogpost, but it's with Hibernate, and I think it still use a join table :
#Entity
public class Many {
#Id
public String id;
#Column(name="idOne")
private String idOne;
#ManyToOne
#JoinColumn(name="idOne", nullable=false, insertable=false, updatable=false)
private One one;
}
I tried it, but I got exactly this error.
I don't understand how I am struggling with that. My goal is to have a table that keep some reference data (like a list of country as the class One), and a list of "working item" (like a town as the class Many) that I create/update without create/update the reference data, just its foreign key in the Many object.
If its a unidirectional association, and Many is the owning side (as per your second example), you are heading in the wrong direction. It doesn't make much sense to delegate the update and insert responsibility on the owning side of a unidirectional relationship (as done with the insertable=false and updateable=false).
EDIT: updated answer
So what you want is a many-to-one, with a foreign key column on the owning side. Try this
#Entity
public class Many {
#Id
String id;
#ManyToOne
#JoinColumn(name = "foreignKeyColumn")
One one;
}
#Entity
public class A {
#Id
String id;
#OneToOne(cascade=CascadeType.ALL)
B b;
}
#Entity
public class B {
#Id
String id;
}
and then if you persisted initial objects as
tx.begin();
A a = new A("FirstA");
B b1 = new B("FirstB");
B b2 = new B("SecondB");
a.setB(b1);
em.persist(a);
em.persist(b2);
tx.commit();
... (some time later)
tx.begin();
A a = em.find(A.class, "FirstA");
B b2 = em.getReference(B.class, "SecondB");
// update the B in A to the second one
a.setB(b2);
tx.commit();
This updates the FK between A and B. Can't get simpler

Hibernate cascades : Object references an unsaved transient instance - save the transient instance before flushing

#Entity
#Table(name = "parent")
public final class Parent extends Base {
#OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Person person;
and doing (amongst other things) this :
Parent parent = new Parent();
Person person = new Person();
parent.setPerson(person);
session.save(parent);
I get the mentioned exception ?
Do I manually need to call session.save(person) before ? do I have to add a cascade type annotation to the childs class definition(where it references the parent) ?
Or have I missed something else obvious ?
I don't want to use CascadeType.ALL as when a parent is deleted I want to keep the person(child).
Both entities/tables extend a common Base table :
#MappedSuperclass()
public abstract class Base {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
public Integer getId() {
return id;
}
Will this effect which cascade type is required ?
You haven't mentioned the Hibernate version, but this hasn't changed since I ever started using it.
As you can read in the Hibernate reference, to get the Java standard SAVE_UPDATE you need {CascadeType.PERSIST, CascadeType.MERGE} in Hibernate.
EDIT: Seeing the updated info, what you're doing now causes Hibernate to treat it as a bi-directional one-to-one mapping. This basically means that for each object in any of those two tables, there has got to be a counterpart in the other table with the same ID. Therefore, you cannot delete only one of them, you would lose FK integrity.
If you want it to be a unidirectional mapping, e.g., if you want to be able to delete the person but leave the parent -- you have to specify a FK, usually via #JoinColumn, like
#JoinColumn(name="PERSON_ID", unique=false, nullable=true, insertable=true, updatable=true)

JPA #OneToMany join on part of a multipart key

A Profile table has a one to many association with a Privilege table. The privilege table has a multipart key, of a profile_id and a privilege_id. I want to join from the Profile table to the Privilege table only on the profile_id and get back a collection of privileges.
In my Profile class I have
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "PROF_ID")
public List<ProfilePrivilegeEO> getProfilePrivileges()
{
return m_profilePrivileges;
}
My Privilege class has
private ProfilePrivilegeId m_profileId;
#EmbeddedId
public ProfilePrivilegeId getProfileId()
{
return m_profileId;
}
Where ProfilePrivilegeId is
#Embeddable
public class ProfilePrivilegeId
implements Serializable
{
private Integer m_profileId;
private Integer m_privNumber;
#Column(name = "PROF_ID")
public Integer getProfileId()
{
return m_profileId;
}
#Column(name = "PRIV_NUM")
public Integer getPrivNumber()
{
return m_privNumber;
}
.....
}
However, when i do that the static weaver says:
The #JoinColumns on the annotated element [method getProfilePrivileges] from the entity
class [class com.acme.ProfileEO] is incomplete. When the source entity class uses a
composite primary key, a #JoinColumn must be specified for each join column using the
#JoinColumns. Both the name and the referencedColumnName elements must be specified in
each such #JoinColumn.
However, the Profile table has no knowledge of privilege_ids... I don't see why JPA should demand that i specify both keys of the privilege table, that's just an arbitrary decision made by jpa with no valid reason why... What do i need to do to get this to work? (I am using EclipseLInk.)
Create an PrivilegeId class that encapsulate the ids. Make that class #Embedded and put it in the Privilege with #EmbeddedId.
In the PrivilegeId class, put an #OneToMany to the Profile and the privilege id.

Categories

Resources