Using Spring Mono and Hibernate initialize not working - java

I've read many answers here on stackoverflow but still haven't found the best solution to load lazy entities with Spring Mono and JPA/Hibernate.
I can fix it easily by eager fetching but I think that is not the best solution.
My problem now is that I cannot have Hibernate.initialize working either and am looking for a solution. My use case is very simple:
#Transactional
public Mono<User> getUserById(String id) {
log.debug("Get user by {}", id);
return Mono
.justOrEmpty(userRepository.findById(id))
.map(user -> {
Hibernate.initialize(user.getCustomer().get());
Hibernate.initialize(user.getCustomer().map(Customer::getCustomerSettings).get());
return user;
});
}
The thing is that the Customer is an optional so I don't know if that has any impact on it.
I have tested with and without transactional, with and without .get(), and don't have many more ideas of how to solve this the correct way at the moment.
Always get this error:
org.hibernate.LazyInitializationException: could not initialize proxy [com.example.project.customer.model.Customer#692e8725-a5fa-4c87-ac2d-b382b1c6bfbc] - no Session
Any ideas?

Related

Disable DB Updates in Spring boot JPA when changing the value of a property after it is fetched from the repository using findall or other queries

I am having a service that gets the data from the database which has a column which is stored with encrypted value.
After fetching from the DAO, i will update the value of the property to decrypted value and then send it as response for the API.
I assume that the entity is having change tracking enabled for select queries also because after i get the data, the data is updated in the DB with the decrypted password. I have googled and found that the use of EntityManager solves the problem, but for this implementation I have to do a lot of code changes in many entities.
from this link, i see that we have to write custom stateless bean and inject to the code, but it looks like not right. Please suggest me the best approach to handle this problem.
My DAO:
#Repository
public interface EnvironmentDao extends JpaRepository<Environment, Long> {
//custom methods go here with native queries
}
My Service
#Override
public List<Environment> getEnvironmentsByIds(List<Long> environmentIds) throws Exception {
if (environmentIds == null || environmentIds.size() < 1) {
return null;
}
return decryptPassword(environmentDao.findAllById(environmentIds));
}
Inside the decryptPassword method, i am just looping through all the records and then setting the decrypted password like
e.setDB_Password(encryptionService.decrypt(e.getDB_Password()));
One case that i noticed yesterday is that for a similar entity on any error, there was a DB save and that time the values got updated, so after fixing the error, this change was not happening.
Please help me as I am not an expert in java and taking more time to analyze and could not understand. In the case of C#, i would use .AsNoTracking(), but i don't know java much and fiddling around.
Tried the following in the Service
#Autowired
EntityManager entityManager;
In the method,
Optional<Environment> environment = environmentDao.findById(id);
entityManager.detach(environment.get());
return managePassword(environment.get(), false);
I would suggest two options to overcome the entity being updated unintentionally:
Instead of returning the entity itself I would suggest creating a DTO class and creating an instance of that class and setting relevant properties on to the DTO instance so that no changes will be made to the entity itself. So the code will be sth like:
public List<EnvironmentDTO> getEnvironmentsByIds(List<Long> environmentIds) throws Exception {
if (environmentIds == null || environmentIds.size() < 1) {
return null;
}
return createEnvironmentDTOs(environmentDao.findAllById(environmentIds));
}
private LisT<EnvironmentDTO> createEnvironmentDTOs(List<Environment> environments) {
return environments.stream().map((env) -> {
EnvironmentDTO envDto = new EnvironmentDTO();
// Copy all relevant fields to DTO (you can even use some Mapper library for this, i.e. http://modelmapper.org/)
envDto.setDB_Password(encryptionService.decrypt(e.getDB_Password()));
})
}
If you want to return the entity no matter what instead of creating a DTO class and instance from it; you can detach the entity so that changes to the entity will not be reflected to database. So what you need to do is detaching entity after you are done with decrypting the password and setting it back to the entity: entityManager.detach(environment)

Is it right approach to insert Many to One entity using Hibernate?

I'm learning Hibernate and Querydsl on spring boot application.
I'm not sure what i'm doing is right approach or not, This is how i insert many to one entity
// BoardMaster.java
public void addBoards(Boards boards){
this.boards.add(boards);
boards.setBoardMaster(this);
}
// Service.java
#Override
#Transactional
public void save(BoardsRequestDto boardsRequestDto) {
BoardMaster boardMaster = boardMasterService.getOne(boardsRequestDto.getBbsId());
Boards boards = boardsRequestDto.toEntity();
boardMaster.addBoards(boards);
boardMasterRepository.save(boardMaster);
}
Boards is the entity has many to one relation and BoardMaster contains Boards entity as List.
So, first i try to get boardMaster and add boards into boardMaster finally run the save method.
I thought i should use or create update method to add boards into boardMaster but i found article that talks about save() can be use for update as well when i fetch entity from persistence context.
As this article says this method works fine but i'm not sure if this is fine. i feel like it's uncommon since i've never used hibernate before.
And i'm using Querydsl to update entity which feels uncommon as well
#Override
public void modify(BoardMasterRequestDto boardMasterRequestDto, Long bbsId){
queryFactory.update(boardMaster)
.set(boardMaster.bbsName, boardMasterRequestDto.getBbsName())
.set(boardMaster.bbsIntro, boardMasterRequestDto.getBbsIntro())
.set(boardMaster.isPossibleToAttachFiles, boardMasterRequestDto.isPossibleToAttachFiles())
.set(boardMaster.amountOfAttachment, boardMasterRequestDto.getAmountOfAttachment())
.set(boardMaster.availableFileSize, boardMasterRequestDto.getAvailableFileSize())
.set(boardMaster.isPossibleToReply, boardMasterRequestDto.isPossibleToReply())
.set(boardMaster.templateId, boardMasterRequestDto.getTemplateId())
.set(boardMaster.templateName, boardMasterRequestDto.getTemplateName())
.set(boardMaster.useAt, boardMasterRequestDto.isUseAt())
.set(boardMaster.bbsUseFlag, boardMasterRequestDto.getBbsUseFlag())
.set(boardMaster.targetId, boardMasterRequestDto.getTargetId())
.set(boardMaster.countOfComment, boardMasterRequestDto.getCountOfComment())
.set(boardMaster.option, boardMasterRequestDto.getOption())
.set(boardMaster.isPossibleToComment, boardMasterRequestDto.isPossibleToComment())
.set(boardMaster.satisfaction, boardMasterRequestDto.getSatisfaction())
.where(boardMaster.bbsId.eq(bbsId))
.execute();
}
This method works fine as well but i'm sure there is more efficient way to update using Hibernate. Please give me some advice 🙏🏻
If you guys need to more info, please let me know. Thank you!

#Transactional annotation Spring boot 2.0 and hibernate LazyInitializationException

I have the following question. From what I understand the #Transactional annotation is supposed to keep the session alive, thus enabling to lazy fetch child entities without the need to performe a specific joining query.
I have the following scenario where I do not understand why I'm still getting a LazyInitializationException.
My app runs a resolver in order to provide the various controller services with a resolved object so that it can be used directly.
Said resolver intercepts a header from the request and using it's value attempts to query the db in order to fetch the object. Now the object in question is quite simple is it's doings albeit it has a list of two sub-entities.
In order to perform the resolving action I'm using an extra service where I basically wrap some JpaRepository methods. The complete is below:
#Service
public class AppClientServiceImpl implements AppClientService {
private static final Logger LOGGER = LoggerFactory.getLogger(AppClientServiceImpl.class.getCanonicalName());
private final AppClientRepository repository;
#Autowired
public AppClientServiceImpl(AppClientRepository repository) {
this.repository = repository;
}
#Override
#Transactional(readOnly = true)
public AppClient getByAppClientId(final String appClientId) {
LOGGER.debug("Attempting to retrieve appClient with id:: {}", appClientId);
return repository.findByAppClientId(appClientId);
}
#Override
#Transactional
public void saveAndFlush(final AppClient appClient) {
LOGGER.debug("Attempting to save/update appClient:: {}", appClient);
repository.saveAndFlush(appClient);
}
}
As you can see both methods are annotated as #Transactional meaning that the should keep the session alive in the context of that said method.
Now, my main questions are the following:
1) Using the debugger I'm seeing even on that level getByAppClientId the list containing on the sub-entities which is lazy loaded has been resolved just fine.
2) On the resolver itself, where the object has been received from the delegating method, the list fails to be evaluated due to a LazyInitializationException.
3) Finally on the final controller service method which is also marked as #Transactional, the same as above occurs meaning that this eventually fails to it's job (since it's performing a get of the list that has failed to initialize.
Based on all the above, I would like to know what is the best approach in handling this. For once I do not want to use an Eager fetching type and I would also like to avoid using fetch queries. Also marking my resolver as #Transactional thus keeping the session open there as well is also out of the question.
I though that since the #Transactional would keep the session open, thus enabling the final service method to obtain the list of sub-entities. This seems not to be the case.
Based on all the above it seems that I need a way for the final service method that gets call (which needs the list on hand) to fetch it somehow.
What would the best approach to handle this? I've read quite a few posts here, but I cannot make out which is the most accepted methods as of Spring boot 2.0 and hibernate 5.
Update:
Seems that annotating the sub-entitie with the following:
#Fetch(FetchMode.SELECT)
#LazyCollection(LazyCollectionOption.TRUE)
Resolves the problem but I still don't know whether this is the best approach.
You initialize the collection by debugging. The debugger usually represents collections in a special way by using the collection methods which trigger the initialization, so that might be the reason why it seems to work fine during debugging. I suppose the resolver runs outside of the scope of the getByAppClientId? At that point the session is closed which is why you see the exception.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(AppClient.class)
interface AppClientDto {
String getName();
}
Querying could look like this
List<AppClientDto> dtos = entityViewManager.applySetting(
EntityViewSetting.create(AppClientDto.class),
criteriaBuilderFactory.create(em, AppClient.class)
).getResultList();

Testing a massive use of method with #Cacheable

i am trying to solve an issue from work that consists in a method that is called several times in production and breaks, here is the interface's method:
#Cacheable("CategoryDao.findAllLocale")
Set<Category> findAllLocale(String locale);
And here is the implementation:
public Set<Category> findAllLocale(final String locale) {
final Set<Category> localeCategories = this.findAllLocaleRootCategories(locale);
for (final Category rootCategory : localeCategories) {
final Set<Category> localeChildCategories = this.findAllLocaleByParent(locale, rootCategory.getCatId());
rootCategory.setCategories(localeChildCategories);
}
return localeCategories;
}
Is a simple DAO method but the problem is that returns a lot of data and in server productions throws this exception:
01-01-15 10:09:47:984 - {ERROR} categories.GetAllCategoriesAction - User:5007660771072025 - Unexpected exception executing the action
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.company.app.data.Category.categories, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
I think that the exception is something about that the #Cacheable overloads, because the app runs a few hours and works fine but then crash and log that fragment, I want make a "massive" use of this test so i will know is that or something else, any suggestions?
PD: #Cacheable is from ehcache framework
PD2: Sorry for my english
The issue comes from the fact that let Hibernate/JPA entities escape, making them live longer than the session they were attached to.
So the issue comes at a later point when you use one of them (from the cache but it may not even be directly related) and try to access a lazy loaded collection. And that last point fails because you are already outside of the bounds of your Hibernate/JPA session.
As a rule of thumb, you should not cache Hibernate/JPA entities.

Java query db issues. using hibernate and struts2

I am getting into java here. Fun and frustrating all at the same time :)
I have a simple method called showUsernames():
public String showUsernames(){
TimesheetUserDAO su = new TimesheetUserDAO();
Session session = su.getSession();
setUsers(su.findByUsername(_users));
session.close();
return SUCCESS;
}
...however, I am having a time getting just the usernames out of the database. It is possible with the Hibernate DAO to get this correct? I am able to use su.findAll() and return everything.
Any thoughts? Need more code? Thanks :)
The DAO probably executee a request like
select u from User u where ...
Change the query to
select u.name from User u where ...
Of course, instead of having a List<User> as a result, you'll have a List<String>.
This is basic stuff described in the Hibernate reference documentation. Have you read it?
Also, getting the session from the DAO and closing it manually like this shows a design problem. This should be encapsulated by the service layer or, even better, by the declarative transaction handling.

Categories

Resources