JPA does not refresh related entity after insert - spring-boot

I have issues with some two entities, that have relation to same entity.
So, I have Transaction (do not confuse it with session transaction), TransactionItem, Customer and Subscription. Both, Transaction and Customer has relation Subscription.
Transaction.java
#OneToMany(
mappedBy = "transaction",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true
)
private List<TransactionItem> transactionItems;
TransactionItem.java
#OneToOne(mappedBy = "transactionItem", cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
private Subscription subscription;
Customer.java
#OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true
)
private List<Subscription> subscriptions;
Subscription.java
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id", referencedColumnName = "guid")
private Customer customer;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "transaction_id", referencedColumnName = "guid")
private TransactionItem transactionItem;
I add TransactionItem to Transaction and then Subscription to TransactionItem. Nothin special really.
When Transaction is being saved hibernate save all dependant relations as well, which is fine and expected.
The issue is when I try to refresh Customer, that have one more Subscription attached, I get old entity. I tried to flush, evict, clear ... but nothing seems to work. I tried to search for the solution but I found nothing, that could help me out with it.
I am working with spring boot 2.1.1
Thanks for help!

What I had to do was to clear the persistence context of EntityManager. Usually with using jpa you do not want to do this manualy but in this case I had no other choice.
//aquire instance of EntityManager in your bean
#Autowired
private EntityManager entityManager;
//then when everything is saved we need to flush the changes and then clear context
entityManager.flush();
/**
*
* From the javax.persistence.EntityManager documentation:
*
* Clear the persistence context, causing all managed
* entities to become detached. Changes made to entities that
* have not been flushed to the database will not be
* persisted.
*/
entityManager.clear();

Related

Spring/JPA twice uniderectional not working, because of #JoinColumn(nullable = false)

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.

#Batchsize annotation not working for OneToMany

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.

"could not initialize proxy - no Session" For Multiple ManyToMany relationships in the parent

I have a Parent User Class that has multiple ManyToMany Relationships.
#Table(name = "user")
public class User {
..
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "user_address",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "address_id")}
)
#JsonIgnore
private final List<Address> addresses = new ArrayList<Address>();
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "reports",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "reports_id")}
)
#JsonIgnore
private final List<Reports> reports = new ArrayList<Reports>();
}
When I access the FIRST ManyToMany property, everything works fine. However, immediately after
accessing the first, when I try to access the SECOND ManyToMany Property I get the "could not initialize proxy - no Session" exception:
#Component
public class Combiner {
public void combineData() {
...
List<Address> addresses = user.getAddress(); // This works
List<Reports> reports = user.getReports(); // Get the error here
..
}
}
The Address and Reports classes have the inverse relationship as many ManyToMany back to the User Entity Above.
public class Address {
#ManyToMany(mappedBy = "addresses", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
public class Reports {
#ManyToMany(mappedBy = "reports", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
I tried searching SO for the same error where there are MULTIPLE relationships like mine and the first passes but second fails, but could'nt find a post (or google couldn't understand the search terms, if anyone knows a pre-existing one - please let me know).
Could someone assess what else Im missing?
I've tried these so far to no avail:
Added #Transactional to the parent Service class that calls Combiner above
Made the second failing relationship EAGER. (as i understand it you cant make BOTH EAGER since i get a multiple bags error probably because of Cartesian join)
AM Using SpringBoot (2.2.4) with Hibernate Core {5.4.10.Final}
Approach one:
Make #ManyToMany uni-directional. The exception clearly says it can not initialize the collection of role you have in User class.
As you asked in the comment section Why can't this use case be Bi Directional - You can make this bi-directional as well.
Approach two: make collection of role EAGER or use Hibernate.initialize() to initialize the collection.
Bonus: you can make both collection EAGER by using Set not List.

Hibernate #OneToMany don't remove child from list when updating parent

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

Hibernate 2 records being inserted for Single JAVA Object

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.

Resources