I have problem with unidirectional mapping and need help.
I have 2 Entities with the same unidirectional mapping.
The first one:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "massnahme_id", nullable = false)
private Set<VerortungDAO> verortungen = new LinkedHashSet<>();
The second one:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "massnahmen_verbund_id", nullable = false)
private Set<VerortungDAO> verortungen = new LinkedHashSet<>();
If I try to save one Entity, hibernate throws an Exception because of the second Entity definition (not null).
org.hibernate.PropertyValueException: not-null property references a
null or transient value
If I change the JoinColumn to nullable = true, then the unidirectional mapping not working and the list is not saved in DB.
What can I do to make it work?
Make the associations bidirectional and map the to-one association in VerortungDAO, or if you don't want that, at least map the FK-columns. If you map it bidirectional, use #OneToMany(mappedBy = "..."). Either way, you will have to initialize the two to-one associations or FK-columns on the VerortungDAO objects.
PS: An entity isn't a DAO (data access object), so the naming xxDAO is quite confusing for an entity.
Related
Imagine having an JPA entity with different lazy loaded fields
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "organisation_teams",
joinColumns = #JoinColumn(name = "id", referencedColumnName = "id", table = "usr_organisation"),
inverseJoinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id", table = "usr_teams")
)
private Set<Team> teams = new LinkedHashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "organisation_associations",
joinColumns = #JoinColumn(name = "parent_id", referencedColumnName = "id", table = "usr_organisation"),
inverseJoinColumns = #JoinColumn(name = "child_id", referencedColumnName = "id", table = "organisation")
)
private Set<Organisation> children = new LinkedHashSet<>();
This entity is now requested by a second service, so we need to convert into a DTO and return via REST for example. Now there can be different scenarios, where for example only the team field is required, only the children field or both.
One could map them always into the DTO, in this case using lazy loading is pointless in my opinion. Are there different elegant options aside of creating different DTOs (containing only the team or only the children fields) and different REST endpoints? I don`t think this would be an elegant solution as well, because you would end up with a bunch of endpoints and dtos.
Additionally Team and Organisation again have lazy loaded fields and the same issue applies again.
I think that what you are looking for might be https://graphql.org/. But for plain REST endpoints I would say different DTOs is the way to go, use inheritance to avoid code duplication.
If you want to improve performance and not lazy load related entities you can specify different named entity graphs and depending on the DTO select the one that eagre loads the required data. See the documentation here: https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs001.htm.
Any lazy loaded field that is not mapped to a DTO won't be loaded. So basically you use the same entity with different graphs and different DTOs.
I have following classes and on annotating #BatchSize annotation it is not working and I am getting n+1 select query.
Class Shipment{
#OneToMany(fetch = FetchType.LAZY, mappedBy = order.shipment, cascade = CascadeType.ALL,
orphanRemoval = true)
#BatchSize(size=20)
Set<Orders> orders = new Hashset(); <---- Batch size annotation not working
}
Order.class
class Order{
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "item_fk")
Item item;
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "shipment_fk")
Shipment shipment; }
Item.class
class Item{
String id;
String name;
}
What is mistake in implementation that i am getting n+1 queries?
Try to use List<Orders> instead of Set<Orders>.
Please note as it's mentioned in the documentation:
However, although #BatchSize is better than running into an N+1 query issue, most of the time, a DTO projection or a JOIN FETCH is a much better alternative since it allows you to fetch all the required data with a single query.
Your N + 1 query issue is due to the fact that you do eager fetching of Item in Order. Change to LAZY there and you should be good to go.
I have create mapping between Paper and Mcq question as below.
public class Paper {
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "paper_mcq",
joinColumns = {#JoinColumn(name = "paper_id")},
inverseJoinColumns = {#JoinColumn(name = "mcq_id")})
#JsonIgnore
private Set<Mcq> mcqs = new HashSet<>();
}
When I'm updating Paper entity it's deletes all MCQ.
SQL Output:
Hibernate: delete from paper_mcq where paper_id=?
I believe your paper object in paperRepo.save(paper) don't have mcqs at this time, and the cascading sees that as a deletion. I'm just assuming that you're receiving your object from json and the #JsonIgnore simply ignores the deserialization.
So there are multiple options to solve that:
- Query the mcqs and set them before updating
- remove #JsonIgnore and add those in your json
- remove the cascading and set it manually
Currently I am following this URL & implemented the similar kind of code at my end.
But it gives an error at my end something likewise,
null value in column "file_id" violates not-null constraint
Here, category_id is one of my parent_entities primary key.
Following lines where parent entity I am passing properly & checked through Debug,
EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
entityManager.persist(new FileHistory(target, action));
UPDATE -
Here, instead of the following config,
#ManyToOne
#JoinColumn(name = "file_id", foreignKey = #ForeignKey(name = "FK_file_history_file"))
I've used,
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "file_id")
Also, I used #PostPersist instead of #PrePersist these are the changes only I did against this article.
I am using Hibernate Implementation of JPA with Spring.
Class Country{
#OneToMany(mappedBy="Country", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
List<State> stateList;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "Current_State_ID")
State currnetState;
}
Class State{
#ManyToOne
#JoinColumn(name="Country_ID")
private Country country;
}
State stateObj = new State();
country.getStateList().add(stateObj);
country.setCurrnetState(stateObj);
countryRepository.saveAndFlush(country);
countryRepository is a JPA Repository Implemenntation.
This creates 2 entries for in State Table, which messes up my logic. Can someone please point me what I am doing worng.
I am not sure why but changing my code to following works for me.
List<State> stateList = new ArrayList<State>();
stateList.add(state);
country.setStateList(stateList);
Creating a New List instance and setting it to country.