Collection persists but doesn't appears in other side - java

I have a many-to-many relationship in Hibernate. When i relate one objectA with two objectsB, the objectA.getObjectB() returns the two elements sucessfully (including database), but objectB.getObjectA() doesn´t return objectA. Only after a new session that is going to work.
#Entity
public class ObjectA implements java.io.Serializable {
private List<ObjectB> objectsB;
...
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "db_objectA_objectB", joinColumns = {
#JoinColumn(name = "idObjectA", updatable = false)}, inverseJoinColumns = {
#JoinColumn(name = "idObjectB", updatable = false)})
public List<ObjectB> getObjectsB() {
return objectsB;
}
public void setObjectsB(List<ObjectB> objectsB) {
this.objectsB = objectsB;
}
}
#Entity
public class ObjectB implements java.io.Serializable {
private List<ObjectA> objectsA;
...
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "objectsB", cascade = CascadeType.ALL)
public List<ObjectA> getObjectsA() {
return objectsA;
}
public void setObjectsA(List<ObjectA> objectsA) {
this.objectsA= objectsA;
}
}

Keeping both sides of the relationship consistent is responsibility of application code, not the responsibility of Hibernate. In JPA 2.0 specification this is told with following words:
Note that it is the application that bears responsibility for
maintaining the consistency of run- time relationships—for example,
for insuring that the “one” and the “many” sides of a bidirectional
relationship are consistent with one another when the application
updates the relationship at runtime.

Related

Hibernate orphanRemoval removes my parent entity

I have a tree structure that I call a graph, that uses the adjacency list. Class Graph has
#OneToMany(mappedBy = "graphEntry", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#OptimisticLock(excluded = true)
public List<GraphNode> getStartNodes() {
return startNodes;
}
public void addStartNode(GraphNode graphNode){
startNodes.add(graphNode);
graphNode.setGraphEntry(this);
}
public void removeStartNode(GraphNode graphNode){
startNodes.remove(graphNode);
graphNode.setGraphEntry(null);
}
and GraphNode has:
#ManyToOne
#JoinColumn(name = "graphEntry")
public Graph getGraphEntry() {
return graphEntry;
}
#ManyToOne(fetch = FetchType.LAZY, targetEntity = GraphNode.class)
#JoinColumn(name = "parent")
#OptimisticLock(excluded = true)
public GraphNode getParentNode() {
return parentNode;
}
#OneToMany(mappedBy = "parentNode", cascade = CascadeType.ALL, orphanRemoval = true)
#OptimisticLock(excluded = true)
public List<GraphNode> getChildNodes() {
return childNodes;
}
When I replace startNode with different node, I get
PersistentObjectException: detached entity passed to persist: ...GraphNode
When I change the order of 2 nodes my data are correctly saved in DB.
I tried to change cascade type to PERSIST and MERGE instead of having ALL but it doesn't help. It looks like hibernate is deleting Graph when the old startNode does not point there any more.
How can I ensure that my Graph is not being removed and node replacement works?
That is the correct behavior when you set orphanRemoval to true; it tells if the child is orphaned. it should also be removed from the database.
Let's understand this with an example, Let's say you have Employee entity and Employee can have multiple accounts.
#Entity
#Table(name = "Employee")
public class Employee
{
#Id
private Integer employeeId;
#OneToMany(orphanRemoval = true, mappedBy = "employee")
private Set<Account> accounts;
}
It essentially means that whenever we remove ‘account from accounts set’(which means I am removing the relationship between that account and Employee); the account entity that is not associated with any other Employee on the database (i.e. orphan) should also be deleted.

Spring Data JPA - Delete many to many entries

I am attempting to remove entries from a many to many relationship using Spring Data JPA. One of the models is the owner of the relationship and I need to remove entries of the non-owner entity. These are the models:
Workflow entity
#Entity(name = "workflows")
public class Workflow {
#Id
#Column(name = "workflow_id", updatable = false, nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID workflowId;
#ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "workflow_data",
joinColumns = #JoinColumn(name = "workflow_id", referencedColumnName = "workflow_id"),
inverseJoinColumns = #JoinColumn(name = "data_upload_id", referencedColumnName = "data_upload_id"))
private Set<DataUpload> dataUploads = new HashSet<>();
// Setters and getters...
}
DataUpload entity
#Entity(name = "data_uploads")
public class DataUpload {
#Id
#Column(name = "data_upload_id")
private UUID dataUploadId;
#ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, mappedBy = "dataUploads")
private Set<Workflow> workflows = new HashSet<>();
// Setters and getters...
}
DataUpload repository
#Repository
public interface DataUploadsRepository extends JpaRepository<DataUpload, UUID> {
#Transactional
void delete(DataUpload dataUpload);
Optional<DataUpload> findByDataUploadId(UUID dataUploadId);
}
To delete data uploads, I am trying to execute a couple of query methods of the repository:
First version
dataUploadsRepository.deleteAll(workflow.getDataUploads());
Second version
workflow.getDataUploads().stream()
.map(DataUpload::getDataUploadId)
.map(dataUploadsRepository::findByDataUploadId)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(dataUploadsRepository::delete);
Problem is that Spring Data JPA is not removing DataUploads nor entries of the association table workflow_data.
How can I tell Spring Data to remove from both data_uploads and workflow_data (association table)?
I would appreciate any help.
I found the solution for this problem. Basically, both entities (in my case) need to be the owner of the relationship and the data from the association table must be deleted first.
Workflow entity (relationship owner)
#Entity(name = "workflows")
public class Workflow {
#Id
#Column(name = "workflow_id", updatable = false, nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID workflowId;
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(name = "workflow_data",
joinColumns = #JoinColumn(name = "workflow_id", referencedColumnName = "workflow_id"),
inverseJoinColumns = #JoinColumn(name = "data_upload_id", referencedColumnName = "data_upload_id"))
private Set<DataUpload> dataUploads = new HashSet<>();
// Setters and getters...
}
DataUpload entity (relationship owner)
#Entity(name = "data_uploads")
public class DataUpload {
#Id
#Column(name = "data_upload_id")
private UUID dataUploadId;
#ManyToMany
#JoinTable(name = "workflow_data",
joinColumns = #JoinColumn(name = "data_upload_id", referencedColumnName = "data_upload_id"),
inverseJoinColumns = #JoinColumn(name = "workflow_id", referencedColumnName = "workflow_id"))
private Set<Workflow> workflows = new HashSet<>();
// Setters and getters...
}
Notice that Workflow has ALL as cascade type, since (based on the logic I need), I want Spring Data JPA to remove, merge, refresh, persist and detach DataUploads when modifying workflows. On the other hand, DataUpload does not have cascade type, as I do not want Workflow instances (and records) to be affected due to DataUploads deletions.
In order to successfully delete DataUploads, the associate data should be deleted first:
public void deleteDataUploads(Workflow workflow) {
for (Iterator<DataUpload> dataUploadIterator = workflow.getDataUploads().iterator(); dataUploadIterator.hasNext();) {
DataUpload dataUploadEntry = dataUploadIterator.next();
dataUploadIterator.remove();
dataUploadsRepository.delete(dataUploadEntry);
}
}
dataUploadIterator.remove() deletes records from the association table (workflow_data) and then the DataUpload is deleted with dataUploadRepository.delete(dataUploadEntry);.
It has been a while since I have to fix these kind of mappings so I'm not going to give you a code fix, instead maybe give you another perspective.
First some questions like, do you really need a many to many? does it make sense that any of those entities exist without the other one? Can a DataUpload exist by itself?
In these mappings you are supposed to unassign the relationships on both sides, and keep in mind that you could always execute a query to remove the actual values (a query against the entity and the intermediate as well)
A couple of links that I hope can be useful to you, they explain the mappings best practices and different ways to do the deletion.
Delete Many, Delete Many to Many, Best way to use many to many.

Hibernate find method sometimes returns proxy

Lately my project encountered a really odd issue with JPA findOne(id) returning a proxy object instead of a full object.
Here is the scenario. Consider the entities and their connections shown below.
#Table(name = "HOUSE")
#Entity
#EqualsAndHashCode
#Setter
#ReadPermission(expression = "user has rights for template snapshots AND has filter")
public class HouseEntity extends VersionedEntity {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "house", fetch = FetchType.LAZY, orphanRemoval = true)
public List<RoomEntity> getRooms() {
return rooms;
}
#OneToMany(cascade = CascadeType.ALL, mappedBy = "template", fetch = FetchType.LAZY, orphanRemoval = true)
public List<TableEntity> getTables() {
return tables;
}
}
#Entity
#Table(name = "ROOMS")
public class Room {
#ManyToOne(fetch = FetchType.LAZY)
public HouseEntity getHouse() {
return house;
}
#OneToMany(mappedBy = "room", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
public List<TableEntity> getTables() {
return tables;
}
}
#Entity
#Table(name = "TABLES")
public class TableEntity{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "HOUSE_ID")
public HouseEntity getHouse() {
return template;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ROOM_ID")
public RoomEntity geRoom() {
return room;
}
As you can see, House has Tables and Rooms, Rooms also has Tables, and each child entity has a connection to its parents.
Add a table to the HouseEntity
Remove the table from the HouseEntity immediately after.
For 1, the houseRepository.findById gets my HouseEntity wrapped in proxy, as if it is lazy loaded.
The weird part is that if I do:
Add a table to a RoomEntity, which is a child of HouseEntity.
Remove the table from RoomEntity.
Then houseRepository.findById returns the HouseEntity without the proxy.
My question here is, why would this happen? Why would the findById method return a proxyed entity in this case? I need to have access to the normal entity without the proxy directly, even if the proxy has the entity populated in the target.

Hibernate ManyToMany results in cartesian product

I am a newbie in hibernate/JPA and apologies for this noob question. I have follow relations with in entities
Manager has many to many relation with Worker
Worker has many to many relation with Task
Following is the ERD
Following are my java classes
#Entity
#table(name="manager")
public class Manager {
private long id;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "manager_worker", joinColumns = { #JoinColumn(name = "manager_id") },
inverseJoinColumns = { #JoinColumn(name = "worker_id") })
private ArrayList<Worker> workers = new ArrayList<Worker>();
// ......getter setter
}
#Entity
#Table(name = "worker")
public class Worker {
#Id
private long id;
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "worker_task", joinColumns = { #JoinColumn(name = "worker_id") },
inverseJoinColumns = { #JoinColumn(name = "task_id") })
private ArrayList<Task> tasks = new ArrayList<Task>();
...........................
}
#Entity
#Table(name = "task")
public class Task {
#Id
private long id;
private String title;
.................
}
I have following data:
Manager M1 has worker W1 and W2
W1 has tasks TW1, TW2, TW3
W2 has tasks TW2 and TW2
When I get Manager object for Id M1, the result has cartesian product of Worker and Task i.e. W1 data is repeated for 3 times and W2 data is repeated for 2 times i.e. Manager.worker array list have 5 worker object instead of 2.
To load the data I am using Session.get() method
public E find(final K key) {
return (E) currentSession().get(daoType, key);
}
Can anyone please tell me how I can fix this and point me to any best practices that I should use in this case.
Thanks
This happens because both #ManyToMany relationships are eager loaded.
This will cause an outer join over all three tables. The result of this query are five rows where W1 appear three times and W2 two times. So far everything is correct.
But for some reason Hibernate just puts all workers in the collections even if they are duplicates. It's even described in the FAQ.
There are multiple options to resolve this issue:
change one of the relationships to lazy loading
use a Set instead of a List (you shouldn't use ArrayList as type for variables anyway especially when using Hibernate)
use #Fetch(FetchMode.SELECT) for tasks
Where is your targetEntity in the #ManyToMany relationship?
you should have something like:
#ManyToMany(targetEntity = Task.class)
#JoinTable(name = "worker_task", joinColumns = { #JoinColumn(name = "worker_id") },
inverseJoinColumns = { #JoinColumn(name = "task_id") })
private ArrayList<Task> tasks = new ArrayList<Task>();
I suggest you to change to #OneToMany and #ManyToOne relationship anyways. That concept is more compatible with the database design and is more understandable when a person looks at the ERD. So Manager has one-to-many relationship with Manager_Worker thus manager_worker has many-to-one relationship with manager. Keep the same for the rest of the entities.

Hibernate #ManyToMany multiple entities

I have the following scenario:
Base Domain class:
#MappedSuperclass
public class BaseDomain {
#Id
protected UUID id;
}
Media Object class:
#Entity
public class MediaObject extends BaseDomain {
#ManyToMany
#JoinTable(
joinColumns = {
#JoinColumn(name = "BaseDomain_id", referencedColumnName = "id"
}
inverseJoinColumns = {
#JoinColumn(name = "Media_id", referencedColumnName = "id")
}
private List<BaseDomain> holders;
}
"Holder" A:
#Entity
public class A extends BaseDomain {
#ManyToMany
private List<MediaObject> media;
}
"Holder" B:
#Entity
public class B extends BaseDomain {
#ManyToMany
private List<MediaObject> media;
}
What I want to achieve is, to store a MediaObject and multiple entities may "hold" this object. My approach would be a using a JoinTable that stores the relation between the MediaObject and an arbitrary BaseDomain object (as above). The issue I'm facing is that the persistence provider (in my case Hibernate) would not be able to decide which actual table to join.
I'm thinking about using a unidirectional #OneToMany which is possible in JPA 2.1.
However, I want to ask, if there are some kind of best practices to approach such a situation.
Following snippet is used by me in production environement, it implements ManyToMany assiciation mapping for Hibernate.
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "printed_mails_logo",
joinColumns = {
#JoinColumn(name = "mails_id", nullable = false, updatable = false)
},
inverseJoinColumns = {
#JoinColumn(name = "logo_id", nullable = false, updatable = false) })
private Set<Logo> printedLogos;
printer_mails_logo is additinal associative table in database.
#JoinColumn(name='x') is the actual name of column in associative table.
This works well for me. I can fetch without no problem all logos that has been printed already.

Categories