How to avoid unwanted queries hibernate query data when import data from entity to DTO - spring

I have some entities below
#Entity
#Table("processitem")
public class Processitem {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="task")
public Task task;
#ManyToOne
#JoinColumn(name="user")
public User user;
//... and some more relationship to other
}
#Entity
#Table(name="task")
public class Task {
...
#OneToMany(mappedBy="task",cascade = CascadeType.ALL,orphanRemoval = true)
private Set<Processitem> processitem;
...
}
Now I import a List to List (I use a loop for to import data from entity to DTO), (around more 200 records) the hibernate execute a lots queries and performance is not good. Is there any solution to avoid that ? I tried using Entity Graph but it still doesn't improve (some time 2 queries is better 1 query with left join)

This is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Task.class)
interface TaskDto {
#IdMapping
Long getId();
String getName();
#Mapping(fetch = MULTISET)
Set<ProcessitemDto> getProcessitem();
}
#EntityView(Processitem.class)
public interface ProcessitemDto {
#IdMapping
Long getId();
#Mapping("user.name")
String getUserName();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TaskDto task = entityViewManager.find(entityManager, TaskDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
You can make use of the various fetching strategies provided by Blaze-Persistence Entity-Views but the best one is usually the MULTISET fetch strategy. Here you can read more about it: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#anchor-fetch-strategies

Related

Mapping a DTO to Entity with #Verion column-Spring Boot Hibernate

I have a DTO which need to be mapped to Entity with a #Version column in DB, and then to do update.
Before mapping I get the Entity from database (I need it because of some validations and comparations) and then use the mapper.
So, the code is like this:
Entity fromDB = getEntity(eDto.getId());
Entity forUpdate = mapper.toEntity(fromDB, eDto);
Mapper:
Entity toEntity(#MappingTarget Entity e, EntityDto eDto);
In EntityDto I have few columns and Version also. But after getting the Entity from DB it is in PersistenceContext and the version can not be changed, so even if I use the wrong Version number, I never get the Optimistic Lock Failure exception.
Any suggestion how can I resolve this issue?
UPDATE:
(here is an example)
user1 get the entity with id 1 and version 1 on UI
user1 make some changes on entity
in the meantime some other user have changed and saved the object to DB (now version is 2)
user1 call updateEntity with version 1 and it comes to my method for update
I take the entity from DB (and now the version is 2)
I map it to forUpdate (BUT HERE VERSION IS NOT MAPED because fromDB is in PersistenceContext and it is not allowed to change version)
the changes are made, and they should NOT be made because versions are not the same!
Not sure how helpful this is for you, but I think Blaze-Persistence Entity Views would be the perfect fit for your situation.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#UpdatableEntityView
#EntityView(Entity.class)
public interface EntityDto {
#IdMapping
Long getId();
String getName();
Set<SubDto> getRoles();
#EntityView(SubEntity.class)
interface SubDto {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
EntityDto a = entityViewManager.find(entityManager, EntityDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<EntityDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
With the Spring WebMvc integration you can even materialize the entity view like this:
#RequestMapping(path = "/my-endpoint", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> updateCat(#RequestBody EntityDto dto) {
myRepository.save(dto);
return ResponseEntity.ok(dto.getId().toString());
}
It will be persisted/updated just as you would expect it!

Spring hibernate orderBy on list element

#Entity
class Person{
private int id;
#OneToMany(mappedBy=owner)
private List<Pet> pets;
}
#Entity
class Pet{
private name;
private ZonedDateTime birthDate;
#ManyToOne
#JoinColumn(name="owner_id")
private Person owner;
}
I want to find all the persons and order them by their oldest pet birthday
The only way I can solve this is through #Formula , something like
#Entity
class Person{
private int id;
private List<Pet> pets;
#Formula("(SELECT p.birth_date FROM pet p WHERE p.owner_id = id order by p.birth_date ASC LIMIT 1)")
private ZonedDateTime oldestPetBirthday;
}
then
public List<Person> findPersonByOrderByOldestPetBirthdayAsc
But I don't want to touch raw sql, I am looking for something like
public List<Person> findPersonByOrderByPetsTop1OrderByBirthDateAsc
OR by using pageable something like:
PageRequest.of(page,pageSize,Sort.by(ASC, "pets.sort(BirthDateComparator).get(0)"))
is that possible?
Try to use #OrderBy annotation from #javax.persistence.OrderBy package on your one to many collection object.
#OrderBy("birthDate")
private List<Pet> pets;
Your solution with the formula is ok but suffers from some issues. Anyway, since you don't want to write SQL, you will have to use something like Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Person.class)
public interface PersonDto {
#IdMapping
Long getId();
#Limit(limit = "1", order = "birthDate desc)
#Mapping("pets")
OldestPetDto getOldestPet();
#EntityView(Pet.class)
interface OldestPetDto {
#IdMapping
Long getId();
ZonedDateTime getBirthDate();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
PersonDto a = entityViewManager.find(entityManager, PersonDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<PersonDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
Also, you can add a Sort for oldestPet.birthDate and it will work just like you would like it to!

Spring JPA Two Entities for same DB table

I am writing Spring Boot Data JPA application and I have following situation:
I have 2 database queries fetching from same table but they are fetching different columns and data based on their WHERE clauses. For example:
SELECT CAR_TYPE, CAR_MODEL, CAR_YEAR, ACCIDENT_YEAR, BUY_DAY FROM CAR WHERE ACCIDENT_YEAR IS NULL
, and
SELECT CAR_MODEL, CAR_YEAR FROM CAR WHERE CAR_YEAR >= CURRENT_YEAR
As you can see these 2 queries (whose results will be exposed through 2 different API points) reference the same table CAR, but return different fields and have different WHERE clauses.
I know in JPA, I have to create an entity CarEntity like:
#Entity
#Table(name = "CAR")
public class CarEntity {
// I can only have fields from one or the other query
// here, so I guess I have to have 2 of these
}
, but my problem is that this entity needs to apply for the 2 different queries (with different fields returned, different data, different WHERE clauses).
So, it looks like I have to have actually 2 CarEntity classes. But, I am not sure how to make these 2 CarEntities so they both reference the same table CAR?
You can do by using projection that basically you define an interface with field methods which you want to get them. Projections
#Entity
public class Car implement CarSummary { // if you want you can implement JIC
private UUID id;
private String carType;
private String carModel;
private LocalDateTime carYear;
//getters and setters
}
public interface CarSummary {
String getCardModel();
String getCarYear();
}
Then on your query.
public interface CarRepository extends Repository<Car, UUID> {
Collection<CarSummary> findByCarYearGreaterThan(LocalDateTime now);
Collection<Car> findByAccidentYearIsNull();
}

OneToMany in MappedSuperclass

I was trying to use #MappedSuperclass to share the same table between two entities following this article here (How to map multiple JPA entities to one database table with Hibernate),
So I have these 3 classes:
#MappedSuperclass
abstract class UserDao {
#Id
#Column(name = "username", nullable = false, unique = true)
var username: String? = null
#OneToMany(fetch = FetchType.EAGER)
var groups: Set<GroupDao>? = null
}
then:
#Entity(name = "basic_user_auth")
#Table(name = "users")
class BasicUserDao : UserDao() {
}
and:
#Entity(name = "full_auth_user")
#Table(name = "users")
class FullUserDao : UserDao() {
#OneToOne
#PrimaryKeyJoinColumn
var profileJpa: ProfileDao? = null
}
what I was trying is to save some queries overhead of loading the user profile when its not needed, but now when i try to run the app i get the following error:
could not execute statement; SQL [n/a]; constraint [full_auth_user_username" of relation "users_groups];
not sure why Hibernate creates this relation since they both share the same table.
I would recommend you don't share types on the entity level. Sharing a one-to-many association will probably not work as you expect with respect to flushing/synchronization when multiple such entities are involved. IMO you should try out a DTO approach instead.
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface BasicUserDao {
#IdMapping
String getUsername();
Set<GroupDao> getRoles();
#EntityView(Group.class)
interface GroupDao {
#IdMapping
Long getId();
String getName();
}
}
#EntityView(User.class)
public interface FullUserDao extends BasicUserDao {
#Mapping("profileJpa")
ProfileDao getProfile();
#EntityView(Profile.class)
interface ProfileDao {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
BasicUserDao a = entityViewManager.find(entityManager, BasicUserDao.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
#Repository
interface UserRepository {
List<BasicUserDao> findAll();
}
The best thing about it is, that it will only fetch the data that is actually needed.

Spring Data JPA DistinctBy projections

Good day fellow hibernators!
I have a question on how the DistinctBy clause works in conjunction with Spring Data's projection
Assume I have 3 classes:
public class Task {
Long id;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "project_id")
private Project project;
#OneToOne
#JoinColumn(name = "contact_id")
private Contact assigned;
Boolean deleted;
// ...
}
public class Contact {
Long id;
// ...
}
public class Project {
Long id;
#OneToMany(fetch = LAZY, mappedBy = "project")
private Set<Task> tasks;
// ...
}
These would be my domain classes. Notice, Project does have a "One2Many" to Tasks, Contact does not. Now, I have 2 interfaces for my projections and the basic TaskRepo with 2 methods:
public interface JustProject {
Project getProject();
}
public interface JustAssignee {
Contact getContact();
}
public class TaskRepo extends CrudRepository<Task, Long>, JpaSpecificationExecutor<Task> {
List<JustAssignee> findDistinctByDeletedFalse();
List<JustProject> findDistinctByDeletedFalseAndDeletedFalse();
}
The way it works for me right now is that, findDistinctByDeletedFalse returns as many instances as there are distinct contacts for tasks (e.g. if there are 10 tasks but only 3 contacts, the method will return just 3 objects containing all the 3 distinct contacts). Same for findDistinctByDeletedFalseAndDeletedFalse but on project level.
Now I have a few questions here and would love to get some help in understanding how this works exactly.
is the distinct clause applied after the search is done?
my initial assumption was that this behavior would not work as it does now. I assumed that the distinct clause is applied before the result is fetched, meaning that it would be DISTINCT based on the underlying task model, not the returned JustContact or JustProject model.
is there any way I could somehow not abuse the ...AndDeletedFalse redundant appendix? I need both the two methods from the repo but I feel like I had to cheat just to obtain that result...
... am I doing something wrong? I wanted to get "all distinct contacts/projects assigned to all tasks" as elegant of a way as possible. I ended up thinking about this distinctby exactly because I was unsure on how it works and wanted to try mu luck out. I really didn't think it would work this way, but now that it does I would really want to understand why it does!
Many thanks <3
The DISTINCT keyword is applied to the query and therefore it's effect depends on the select list which in turn is controlled by the projection. Therefore if you have only project or only contact in your projection the DISTINCT will get applied to those values only. Note though, that this relies somewhat on the boundaries of the JPA specification and I wouldn't be surprised if you see different behaviour with different implementations. See https://github.com/eclipse-ee4j/jpa-api/issues/189 and https://github.com/eclipse-ee4j/jpa-api/issues/124 for somewhat related issues raised against the specification.
In oder to differentiate methods that otherwise only differ in the return value you might add any additional string between find and By in the method name. For example you might want to rename your methods to findDistinctContactsByDeletedFalse and findDistinctProjectsByDeletedFalse
I guess this is the best that you can get with Spring Data JPA. You might be able to use just a single method by using the dynamic projections approach, but I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Task.class)
public interface TaskAggregateDto {
// A synthetic "id" to get a grouping context on object level
#IdMapping("1")
int getGroupKey();
Set<ProjectDto> getProjects();
Set<ContactDto> getContacts();
#EntityView(Project.class)
interface ProjectDto {
#IdMapping
Long getId();
String getName();
}
#EntityView(Contact.class)
interface ContactDto {
#IdMapping
Long getId();
String getName();
}
}
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
public interface TaskRepo extends CrudRepository<Task, Long>, JpaSpecificationExecutor<Task> {
TaskAggregateDto findOneByDeletedFalse();
}

Resources