Spring and Hibernate: DAO Pattern. How to solve LazyInitializationException - spring

I'm wondering: What is the point of FetchType.LAZY in one(many)-to-many using DAO Pattern?
It is basically useless? As soon as you are outside of your DAO (eg. were the actual work is done) you can't fetch the related data as you are not in a hibernate session anymore.
Lets make an Example:
Student and Class. A student takes many classes. he now logs into the system and his Student entity object is retrieved from the system.
application layer -> Service Layer -> DAO
Now the Student wants to see which classes he takes and oops a LazyInitializationException occurs as we are outside of DAO.
What are the options to prevent this? I've like googled hours and not found a solution for this except to actually fetch everything before leaving the DAO which defeats the purpose of lazy in the first place. (Have read about OpenSessionViewFilter but this should work independent of the application layer)
How do you solve this issue in a good way? What are alternative patterns that don't suffer from this?
EDIT:
I get no LazyInitializationException with following settings:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.compound",
cascade = CascadeType.ALL, orphanRemoval = true)
#Fetch(FetchMode.JOIN)
Funny thing is it must be exactly like this.
removing #Fetch -> LazyInitializationException
Even stranger is if I remove orphanRemoval = true, then LazyInitializationException also occurs even with #Fetch. So both of those are required.
Maybe someone could enlighten me why this is the case. Currently I'm tending to ditch hibernate completely as with pure JDBC I would have reached the desired behavior hours ago...

You can always fetch foreign-key relation data without the same ession. Since your session does not exists outside your Application Layer you just fetch it manually in the method where you retrieve the data and set it.
Application Layer:
public List<SchoolClass> getSchoolClassesByStudent(Serializable identifier)
{
List<SchoolClasses> schoolClasses = // get classes by student using criteria or hql
return schoolClasses;
}
Client Layer:
public void loadSchoolClassesByStudent(Student student)
{
student.setSchoolClasses(application.getSchoolClassesByStudent(student.getId()));
}
I myself choosed not to support any collections in my hibernate entities.
I fetch all child relations with very generic methods that my server provides to my client similar to this one.
Edit: Or create some logic (interceptor?) that can check outside the DAO if data is uninitialized before accessing it and initialize it using a generic method.
This would also assume that Hibernate jar's are on the client level, which depends if this is a good idea (Same if the uninitialized data is not set to null).

One way to solve the problem is to use OpenSessionInViewFilter Filter.
<filter>
<filter-name>hibernateSessionFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
</filter>

Related

FindOne brings me the value from the cache memory and not the one from the database in Spring Data

I have a problem with spring data, when executing FindOne when I am updating a product, to compare the value of an attribute with the same one in the database. The FindOne process brings me the same object that I have in memory and not the one from the database, someone knows how I do to bring the one from the database, I know it's because of the hibernate cache, but I can't make it work in repository
#Override
public CuentaDetalle findOne(Long id) {
return cuentaDetalleRepository.getOne(id);
}
It can be done. You should detach the entity.
If everything is well set in your spring-boot project you can easily autowire EntityManager, and then later in your method, you can use entityManager.detach()
#Autowired
private EntityManager entityManager;
...
public someMethod(CuentaDetalle cuentaDetalleToDetach) {
entityManager.detach(cuentaDetalleToDetach);
Then later when using return cuentaDetalleRepository.getOne(id); where id is equal to the cuentaDetalleToDetach.id, the fresh veriosn from db will be returned.
Although I strongly advise using this approach carefully.
There are some drawbacks to detached objects, for example, you cannot use lazy fetch on collection properties (one-to-many).
Then the detached entity will not be saved at the end of the transaction. To save it you should explicitly use cuentaDetalleRepository.save(cuentaDetalleToDetach).
I will stop here, there is a lot to write on this topic.
But I hope, I did answer your question.

Spring Data problem - derived delete doesn't work

I have a spring boot application (based off spring-boot-starter-data-jpa. I have an absolute minimum of configuration going on, and only a single table and entity.
I'm using CrudRepository<Long, MyEntity> with a couple of findBy methods which all work. And I have a derived deleteBy method - which doesn't work. The signature is simply:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
Long deleteBySystemId(String systemId);
// findBy methods left out
}
The entity is simple, too:
#Entity #Table(name="MyEntityTable")
public class MyEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MyEntityPID")
private Long MyEntityPID;
#Column(name="SystemId")
private String systemId;
#Column(name="PersonIdentifier")
private String personIdentifier;
// Getters and setters here, also hashCode & equals.
}
The reason the deleteBy method isn't working is because it seems to only issue a "select" statement to the database, which selects all the MyEntity rows which has a SystemId with the value I specify. Using my mysql global log I have captured the actual, physical sql and issued it manually on the database, and verified that it returns a large number of rows.
So Spring, or rather Hibernate, is trying to select the rows it has to delete, but it never actually issues a DELETE FROM statement.
According to a note on Baeldung this select statement is normal, in the sense that Hibernate will first select all rows that it intends to delete, then issue delete statements for each of them.
Does anyone know why this derived deleteBy method would not be working? I have #TransactionManagementEnabled on my #Configuration, and the method calling is #Transactional. The mysql log shows that spring sets autocommit=0 so it seems like transactions are properly enabled.
I have worked around this issue by manually annotating the derived delete method this way:
public interface MyEntityRepository<Long, MyEntity> extends CrudRespository<> {
#Modifying
#Query("DELETE FROM MyEntity m where m.systemId=:systemId")
Long deleteBySystemId(#Param("systemId") String systemId);
// findBy methods left out
}
This works. Including transactions. But this just shouldn't have to be, I shouldn't need to add that Query annotation.
Here is a person who has the exact same problem as I do. However the Spring developers were quick to wash their hands and write it off as a Hibernate problem so no solution or explanation to be found there.
Oh, for reference I'm using Spring Boot 2.2.9.
tl;dr
It's all in the reference documentation. That's the way JPA works. (Me rubbing hands washing.)
Details
The two methods do two different things: Long deleteBySystemId(String systemId); loads the entity by the given constraints and ends up issuing EntityManager.delete(…) which the persistence provider is about to delay until transaction commits. I.e. code following that call is not guaranteed that the changes have already been synced to the database. That in turn is due to JPA allowing its implementations to actually do just that. Unfortunately that's nothing Spring Data can fix on top of that. (More rubbing, more washing, plus a bit of soap.)
The reference documentation justifies that behavior with the need for the EntityManager (again a JPA abstraction, not something Spring Data has anything to do with) to trigger lifecycle events like #PreDelete etc. which users expect to fire.
The second method declaring a modifying query manually is declaring a query to be executed in the database, which means that entity lifecycles do not fire as the entities do not get materialized upfront.
However the Spring developers were quick to wash their hands and write it off as a Hibernate problem so no solution or explanation to be found there.
There's detailed explanation why it works the way it works in the comments to the ticket. There are solutions provided even. Workarounds and suggestions to bring this up with the part of the stack that has control over this behavior. (Shuts faucet, reaches for a towel.)

How come Spring Data Rest only allows updates from the owner side of ManyToOne/ManyToMany?

I downloaded a sample project from here https://www.baeldung.com/spring-data-rest-relationships
I then ran it and did some test REST calls. As far as I can tell you can only update association from the owner side using SDR. What I mean is
public class Book {
#ManyToOne
#JoinColumn(name = "library_id")
private Library library;
}
and
public class Library {
#OneToMany(mappedBy = "library")
private List<Book> books;
}
You can't actually make post/put calls to /libraries/1/books. Server return 204 but no effect on the db whatsoever.
You can however, make post/put calls to /books/1/library and everything works as intended including keeping the other entity in sync.
Is this normal? It's the same behaviour for #ManyToMany as well. Is there a way to allow for updates from both sides? If I write my own API I can certainly make this the case. Why does SDR not do this?
Is this normal?
In a sense, yes. That's exactly how pure JPA would behave when you added a Book to the collection of Library.books with your current mapping - it would make no changes whatsoever.
My guess is that Spring Data Rest doesn't know (or care) which side of the association is the owner side, and just doesn't go the extra mile to make sure updating the inverse side works as well.
Is there a way to allow for updates from both sides?
A workaround could be to simply pretend both sides of the associations were the owning side, i.e.:
public class Library {
#OneToMany
#JoinColumn(name = "library_id")
private List<Book> books;
}
Be advised that this makes Hibernate treat Library.books and Book.library as two completely separate associations. In some corner cases, your entities may not behave the way you would expect. You have been warned.
Updating bidirectional relations from both sides is very tricky and will potentially cause side effects considering Springs endpoint authorization or Spring Data RESTs BeforeLinkSaveEvent and AfterLinkSaveEvent (https://docs.spring.io/spring-data/rest/docs/current/reference/html/#events).
There has to be a strict parent child relation. I don't think that you can configure the desired behaviour.

Autowire two Neo4j GraphRepository in Spring

I'm new to using Spring with Neo4j and I have a question about #Autowire for a GraphRepository.
Most examples I've seen use one #Autowire per Controller, but I have two Nodes I need to modify at the same time when a particular method is called in the controller. Should I simply #Autowire the repositories for both nodes (eg per the code below)? Is there any impact if I do this in a second controller with the same repositories as well (so if I had a ChatSessionController which also #Autowired ChatMessageService and ChatSessionService)?
ChatMessageController.java
#Controller
public class ChatMessageController {
#Autowired
private ChatMessageService chatMessageService;
#Autowired
private ChatSessionService chatSessionService;
#RequestMapping(value = "/message/add/{chatSessionId}", method = RequestMethod.POST)
#ResponseBody
#Transactional
public void addMessage(#RequestBody ChatMessagePack chatMessagePack,
#PathVariable("chatSessionId") Long chatSessionId) {
ChatMessage chatMessage = new ChatMessage(chatMessagePack);
chatMessageService.save(chatMessage);
// TODO: Make some modifications to the ChatSession as well
}
}
Any help would be much appreciated! I've been googling and looking through Stackoverflow to understand this better but I haven't found anything yet. Any pointers in the right directions would be great.
Another underlying question is, should I be (and can I?) modifying other Nodes in a GraphRepository that handles a particular node? Eg Should my GraphRepository be able to modify my GraphRespository?
Thanks!
I'm not convinced that this is a SO question, it's not really a Neo4J or Spring question either, it is more about the architecture of your application. However assuming that you understand the negatives of class fan out, and how to use the #Transactional annotation to achieve what you want then the answer to your question is that it is just fine to have many Repositories (Neo4J or otherwise, autowired or otherwise) in your class and in as many classes as you want.
Neo4J transactions default to Isolation level READ_COMMITTED and if you need anything else, you need to add the guards/locks yourself. Nested transactions are consideredd tobe the same transaction. The Spring #Transactional annotation relies on proxies that you should be aware of as they have implications when calling methods from within the same class.
I would go through this tuotorial over at Spring Data and get your head around how real world vs domain vs node models differ, there will be cases where one repository impacts another node type but I would think it is often transparent to you (i.e adding relationships). You can do what you like in each repository (the generic nature of them is largely confined to all of the built in CRUD and queries derived from finder-method names (see documentation ) using the #Query annotation, and some queries have side effects, but largely you should avoid it.
As you start adding multiple repositories to multiple controllers I think that your code will begin to smell bad and that you should consider encapsulating this business logic off on its own somewhere, neatly unit tested. I also wouldn't tie myself to one controller per data object, it would be fine to have a single ChatController with a POST/chat/ to create a new session and POST /chat/{sessionId} to add a message. Intersting questions on Programmers:
How accurate is "Business logic should be in a service, not in a model?"
Best Practices for MVC Architecture
MVC Architecture — How many Controllers do I need?

JPA #ManyToMany Writing On Create But Not Update

My team has two classes, User and Store, related by JPA #ManyToMany annotations. The relevant code is below.
When creating a new User object and setting its stores, life is good. But we're seeing some unexpected behavior when trying to update User objects through our Struts 2 / Spring web UI. (These problems do not occur when running simple automated integration tests through JUnit).
Simply put, when saving a User, its stores collection is not updated -- the database shows no changes, and reloads of the User object in question shows it to have an unchanged set of stores.
The only solution that we have found to this problem -- and we don't like this solution, it makes all of the developers on our team a little queasy -- is to create a new Set of Stores, then do user.setStores(null), then do user.setStores(stores).
We are using the OpenEntityManagerInViewFilter; we are using Hibernate as our JPA provider; we are using Spring's JpaTransactionManager. We don't have any #Transactional annotations in our code -- and adding them breaks the existing code due to proxy behavior described elsewhere.
Any insight anyone might provide as to how we might solve this problem in a non-queasy manner is welcome.
Relevant part of User.java:
#ManyToMany
#JoinTable(name = "company.user_store_access",
joinColumns = #JoinColumn(name = "userid"),
inverseJoinColumns = #JoinColumn(name = "storeid"))
public Set<Store> getStores() {
return stores;
}
Relevant part of Store.java:
#ManyToMany(mappedBy = "stores")
public List<User> getUsers() {
return users;
}
Relevant parts of UserDetailAction.java:
(pass-throughs down a layer or two, and then:)
entity = getEntityManager().merge(entity);
The problem is that you have a bi-directional relation, and in this case there is one entity that controls the other, and from your mapping the controlling one is the Store entity and not the User, so adding a user to a store would work (since the Store is the one who controls) but adding a store to a user will not.
try inverting the situation, by making the User object the one who controls the relation, by putting the #ManyToMany(mappedBy = "users") annotation on the getStores() method and changing the getUsers() one.
Hope it helped.
P.S. the entity that controls the relation is always the one that doesn't have the mappedBy attribute.
I just got stuck to same problem and found solution on another page.
Here is what has worked for me
calling getJpaTemplate().flush() before calling merge has worked for me.
I did not get the reason why it worked.
Please try the following annotation in your User object:
#ManyToMany(cascade = {CascadeType.ALL})

Resources