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;
Related
I have the following entities:
#Entity
public class TableA {
#Id
#GeneratedValue(...)
private Long id;
private Timestamp updatedDateTime;
#Version
private int version;
}
#Entity
public class TableB{
#Id
#GeneratedValue(...)
private Long id;
#ManyToOne
#JoinColumn(name="fieldTableC")
private TableC paymentsData;
}
#Entity
public class TableC{
#Id
#Column(name = "fieldTableC")
private String fieldTableC;
private String someOtherField;
}
The problem I am facing, is that I cannot find any information on how to do the following scenario:
If either TableB or TableC gets updated, then I need hibernate to automatically increment the version in TableA. Should I add a FK of TableA to TableB & TableC ?
As you can see, I already have a #ManyToOne from TableB to TableC, if that helps in any way.
PS: I am using JpaRepository
I ended up by adding the child reference in parent, and parent reference into child. Whenever I want to save a child, I also update the parent updatedDateTime value. My method is #Transient, so everything will be saved in 1 transaction.
Also added CascadeType.SAVE_UPDATE, so that when saving parent, the child will also be saved
Eg:
Added to TableA:
#OneToMany(mappedBy = "tableA")
#Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private List<TableB> list= new ArrayList<>();
Added to TableB:
#ManyToOne
#JoinColumn(name = "tableA_root_id")
private TableA tableA;
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.
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.
Let's say I have a parent table A and a child table B, they have a foreign key linking A.id and B.id, the problem is this parent table and the FK have been created recently and there are too many places which persisting a child table, so I'm waiting for a simple way to persist the parent table and link it to the child table. Is it possible to handle this task by using Hibernate instruments? I was trying to use cascades but no success, looks they only work if we persist a parent entity.
So the structure is the following:
#Entity
class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "a_id")
long id;
}
#Entity
class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "b_id")
long id; // FK to a_id
}
class BDao {
void store(B b) {
session.save(b); // to many places
}
}
Will appriciate any help, thanks!
Yeah! I've fixed it, just in case somebody wants to know the answer I'm leaving my solution here. Perhaps you guys will spent less time than me solving the same problem.
So first you need to use #Inheritance(strategy= InheritanceType.JOINED) annotation to describe the parent entity:
#Entity
#Inheritance(strategy= InheritanceType.JOINED)
#PrimaryKeyJoinColumn(name = "a_id")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "a_id")
protected Integer id;
}
Then describe the child entity like
#Entity
#AssociationOverride(name="a_id", joinColumns = #JoinColumn(name="b_id"))
#PrimaryKeyJoinColumn(name = "b_id")
public class B extends A {
...
}
I've left here #AssociationOverride because in my case I have different primary key name so perhaps it helps somebody as well.
That's it! Now when you persist a child entity it will create two rows - one for a parent and one for a child tables.
I have a problem removing the parent entity from the database. The code looks like this:
public class Parent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="parentId")
private Set<Child> children = new HashSet<Child>();
}
public class Child implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private String name;
}
Query q = em.createQuery("delete from Parent");
q.executeUpdate();
But I get "ERROR: update or delete on table "parent" violates foreign key constraint "fk2f04da924aeb47d8" on table "child"". Is it not possible to cascade the delete of all children? How should you clear the tables otherwise?
The bulk delete operation is not cascaded. From the JPA 1.0 specification:
4.10 Bulk Update and Delete Operations
(...)
A delete operation only applies to
entities of the specified class and
its subclasses. It does not cascade to
related entities.
(...)
So if you want to use a bulk delete, you'll have to do handle relations "manually" (i.e. to delete related entities first).
The other option would be to loop on the parent entities and to call em.remove() (and cascading would work).
Choosing one option or the other will depend on the number of entities to delete and the expected performances.