JPA: Persisting a Map<String,Entity> - java

Could you help to persist a Map when String is not the key of the Entity mapped?
For example:
class A {
#Id
long id;
String code;
}
class B {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#What magical combination of JPA annotations should I use here?!
Map<String,A> mapAByCode;
}
I've tried a lot of combinations of {#JoinTable,#MapKeyColumn,#JoinColumn,#JoinTable} annotations with no success and I'm going crazy...
Thanks!

Since you seem to want to map your entities using the A.code value, #MapKey is what you are after. #MapKey allows you to define the value within the reference to use as the key for the map. As the javadoc states, it is required to be unique though or you will run into problems.
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#MapKey(name="code")
Map<String,A> mapAByCode;

It seems that the problem is related to other map I'm using in the same class:
#Entity
class A {
#Id
long id;
String code;
}
#Entity
class B {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#JoinTable(name = "B_MAPABYID", joinColumns = #JoinColumn(name = "B_ID"))
#MapKey(name="id")
Map<Long,A> mapAById;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#JoinTable(name = "B_MAPABYCODE", joinColumns = #JoinColumn(name = "B_ID"))
#MapKey(name="code")
Map<String,A> mapAByCode;
}
This configuration is not working for me, but if I set mapAById as transient all works fine. Does it makes any sense for you?

Related

Hibernate identity generator issue

I am having a particular issue when trying to save a collection of objects with hibernate. It seems that when I have more than one object of the same type, hibernate fails to generate the identifier, so I get a org.hibernate.NonUniqueObjectException .
Example:
App1 --> urls
{strApplicationId:1;URLTypeEntity{strCode:1,strDescription:Reply},strURL:www.address1.com},
{strApplicationId:1;URLTypeEntity{strCode:1,strDescription:Reply},strURL:www.address2.com},
{strApplicationId:1;URLTypeEntity{strCode:2,strDescription:Home},strURL:www.address3.com}
If I do not have two URLs with the same URLTypeEntity on the collection, the error is not triggered
#Entity
#Table(name = "tbl_urls")
public class URLEntity
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="intCode")
private Integer intCode;
private String strApplicationID;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "intType", referencedColumnName = "intCode")
private URLTypeEntity objURLType;
private String strURL;
}
#Entity
#Table(name = "tbl_applications")
public class ApplicationEntity
{
#OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplicationID")
private List<URLEntity> colURLs;
}
ApplicationEntity must also have an id.
The solution was changing the CascadeType from ALL To Merge
#OneToMany(cascade = CascadeType.ALL, mappedBy = "strApplicationID")
Changed To
#OneToMany(cascade = CascadeType.MERGE, mappedBy = "strApplicationID")

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 #OneToMany #ManyToOne mapping

everyone i have a question with using annotation #OneToMany/#ManyToOne; is it possible to create one user model with two sets of subjects in this model (conducted for the teacher and attending the student) instead of creating separate student and teacher models? I wrote such a code but when I want to get data about item and user, hibernate crashes the "Stack overflow" error.I will add that I use H2 Database.
User Entity:
#Entity
public class User{
#OneToMany(
mappedBy = "student",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Set<Item> items = new HashSet<>();
#OneToMany(mappedBy = "teacher",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Set<Item> carriedItems= new HashSet<>();
}
//id and other data
Item entity:
#Entity
public class Item{
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "student_id", nullable = false)
private User student;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "teacher_id", nullable = false)
private User teacher;
}
//id and other data
Thanks for help #Leviand
Based on your comments, looks like your code and logic needs some rework.
First, use instead of Item pojo two dedicated classes:
#Entity
#Table(name = Studend) // I'm guessing a table name
public class Student{
#JoinColumn(name = "student_id", nullable = false)
private User user;
}
#Entity
#Table(name = Teacher) // I'm guessing a table name
public class Teacher{
#JoinColumn(name = "teacher_id", nullable = false)
private User user;
}
Then since an User can only be connected to a single Teacher or Student, refact that in something like:
#Entity
#Table(name = User) // I'm guessing a table name
public class User{
#OneToOne(
mappedBy = "student",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Student student;
#OneToMany(mappedBy = "teacher",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Teacher teacher;
}
Hope this helps :)

JPA entity relationship with itself and cascade delete

I have entity object which has relationship with itself in two forms:
entity refer to list of that entities
entity refer to entity (mainOrder), which is superior current enitity
Java class for order:
#Entity
#Table(name = "shop_order")
public class ShopOrder {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", columnDefinition = "serial")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "main_id",
nullable = true)
private ShopOrder mainOrder;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id") // I am not sure here should be id or mainOrder
private Set<ShopOrder> subOrders = new HashSet<>();
// some others columns, and getters and setters
}
Can you tell me how to ensure that entity for cascade remove? I need if some order will be removed, also will be removed its suborders (it means orders which have set that removed order as mainOrder)
mappedBy is the attribute name in the class ShopOrder that is the back reference to ShopOrder.
Cascade can be defined in the annotation attribute cascade
#OneToMany(fetch = FetchType.LAZY, mappedBy = "mainOrder", cascade = CascadeType.REMOVE)
private Set<ShopOrder> subOrders = new HashSet<>();
Read more about Cascading in the Hibernate manual:
http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#pc-cascade

What all does getPersistenceManager().refresh(entity) load?

I came across a part of code in my project where getPersistenceManager().refresh(entity); is used. When I checked the Hibernate queries that get fired. There are few other queries which get fired and are child elements of the entity to be refreshed.
Why are those queries fired? Why not queries for all child entities fired?
EDIT:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "entity", cascade = { CascadeType.PERSIST,
CascadeType.REMOVE, CascadeType.REFRESH })
#JoinColumn(name = "ENTITY_TABLE", nullable = false)
private List<Employee> employee; // This is an example. Actual code resembles this example.
As you have failed to post any code it is difficult to say, however the most likely explanation is that the relevant cascade options are not defined on the association.
http://docs.oracle.com/javaee/7/api/javax/persistence/CascadeType.html#REFRESH
#Entity
public class Parent{
// on of the following would have to be defined
//#OneToMany(cascade = CascadeType.ALL)
#OneToMany(cascade = CascadeType.REFRESH)
public Set<Child> children;
}

Categories