Can I select specific fields from entity with EntityGraph? - spring

I have an entity, which has multiple(lets say more than 5) fields in it. I want to list only 2 of the fields in entity. I managed to do it with Entity Manager and JPA Query. In the code below, I added how I did it with entity manager, but it may not be optimal solution. What I want to ask is, can I do that with using the EntityGraph?
List<Object[]> test = entityManager.createQuery("SELECT c.a, c.b FROM TestClass c WHERE c.id = :id", Object[].class)
.setParameter("id", id)
.getResultList();
TestClassResult testClassResult = new TestClassResult();
for (Object[] row : test) {
testClassResult.setA((BigDecimal) row[0]);
testClassResult.setB((BigDecimal) row[1]);
}

As far as I know, an implementation is allowed to fetch only what you specify when registering an entity graph as fetch graph (different from a load graph), but at least Hibernate does not support this.
Anyway, DTO projections are usually the way to in such a case anyway and 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(TestClass.class)
public interface TestClassResult {
#IdMapping
Integer getId();
BigDecimal getA();
BigDecimal getB();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TestClassResult a = entityViewManager.find(entityManager, TestClassResult.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<TestClassResult> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

as far as I know, entity graphs define which attributes to fetch from the database so you can fetch the 2 attributes eagerly and the other 3 lazily, "Thorben Janssen" has a good article on his website about graphs, another way to exclusively fetch selected attributes is to use DTO Projections, he also does have a good article touching the subject.

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 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();
}

JpaRepository: Fetch specific lazy collections

If I have an Entity Person with some lazy-collections (Cars, Bills, Friends, ...) and want to write a JpaRepository-method that gives me all persons indluding eagerly fetched Cars, is this possible?
I know that one can do this on single objects, but is this somehow possible with collections of persons?
Yes, there is a very convenient #EntityGraph annotation provided by Spring Data JPA. It can be used to fine tune the used entitygraph of the query. Every JPA query uses an implicit entitygraph, that specifies, which elements are eagerly or lazy fetched depending on the relations fetchtype settings. If you want a specific relation to be eagerly fetched you need to specify it in the entitygraph.
#Repository
public interface PersonRepository extends CrudRepository<Person, Long> {
#EntityGraph(attributePaths = { "cars" })
Person getByName(String name);
}
Spring Data JPA documentation on entity graphs
Use the following JPA query to get the both tables data. Here used jpa query to fetch the cars.
A "fetch" join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection. It effectively overrides the outer join and lazy declarations of the mapping file for associations and collections.
See this for more explanation on join fetch
Use the "join fetch", to fetch object eagerly.
public interface CustomRepository extends JpaRepository<Person, Long> {
#Query("select person from PersonModel as person left join fetch person.cars as cars")
public PersonModel getPersons();
}

How do I migrate my JPA DAO to Spring Data with second level cache?

I have bunch of JPA DAOs im looking to migrate to Spring Data JPA. Some of my DAOS have second-level / query caching set up.
I have a process where I only retrieve the ID in my queries, and then look up the entity using findByID(). This way, only the id's are multiplied in the different query caches, and the entire entities are in the second level cache.
Example:
#NamedQuery(name = "SystemUser.findByEmail",
query = "SELECT u.id FROM SystemUser u WHERE email=:email"),
…
public SystemUser findByEmail(String email) {
TypedQuery<Long> q = getEntityManager().createNamedQuery("SystemUser.findByEmail", Long.class);
q.setParameter("email", email);
q.setHint("org.hibernate.cacheable", true);
q.setHint("org.hibernate.cacheRegion", "query.systemUser");
List<Long> res = q.getResultList();
if (res != null && res.size() > 0) {
return findById(res.get(0));
}
return null;
}
I have several more findBy…-methods, all doing it like this. It feels like a good way to keep cache memory consumption down.
I'm kind of new to the Spring Data JPA business, but I can't see how I would go about realizing this here? The #Cacheable annotations seems only to deal with query caches, which to me would duplicate the entities in each query cache?
Is there any way to do this with Spring Data? Pointers would be much appreciated.
In Spring Data JPA just create a findByEmail method and either Spring Data JPA will found your named query or create one itself.
public class SystemUserRepository extends CrudRepository<SystemUser, Long> {
SystemUser findByEmail(String email);
}
Should be all you need to get the query executed and the desired result. Now with the #QueryHints you can add the hints you are setting now.
public class SystemUserRepository extends CrudRepository<SystemUser, Long> {
#QueryHints(
#QueryHint(name="org.hibernate.cacheable", value="true"),
#QueryHint(name="org.hibernate.cacheRegion", value="query.systemUser") )
SystemUser findByEmail(String email);
}
The result will be cached and still the user will come from the 2nd level cache (if available, else created). Assuming of course your entity is #Cacheable.
A nice read on how the 2 different caches work (together) can be found here. A small snippet on how the query cache works.
The query cache looks conceptually like an hash map where the key is composed by the query text and the parameter values, and the value is a list of entity Id's that match the query:
If you want more complex logic (and really implement the optimization you did) you can always implement your own repository.

Avoid N+1 with DTO mapping on Hibernate entities

In our Restful application we decided to use DTO's to shield the Hibernate domain model for several reasons.
We map Hibernate entities to DTO and vice versa manually using DTOMappers in the Service Layer.
Example in Service Layer:
#Transactional(readOnly=true)
public PersonDTO findPersonWithInvoicesById(Long id) {
Person person = personRepository.findById(id);
return PersonMapperDTOFactory.getInstance().toDTO(person);
}
The main concept could be explained like this:
JSON (Jackson parser) <-> Controller <-> Service Layer (uses Mapping Layer) <-> Repository
We agreed that we retrieve associations by performing a HQL (or Criteria) using a left join.
This is mostly a performant way to retrieve relations and avoids the N+1 select issue.
However, it's still possible to have the N+1 select issue when a developer mistakenly forgets to do a left join. The relations will still be fetched because the PersonDTOMapper will iterate over the Invoices of a Person for converting to InvoiceDTOs. So the data is still fetched because the DTOMapper is executed where a Hibernate Session is active (managed by Spring)
Is there some way to make the Hibernate Session 'not active' in our DTOMappers? We would face a LazyInitializationException that should trigger the developer that he didn't fetch some data like it should.
I've read about #Transactional(propagation = Propagation.NOT_SUPPORTED) that suspends the transaction. However, I don't know that it was intended for such purposes.
What is a clean solution to achieve this? Alternatives are also very welcome!
Usually I use the mapper in the controller layer. From my prspective, the service layer manages the application business logic, dtos are very useful if you want to rapresent data to the external world in a different way. In this way you may get the lazy inizitalization excpetion you are looking for.
I have one more reason to prefer this solution: just image you need to invoke a public method inside a public method in the service class: in this case you might need to call the mapper several times.
If you are using Hibernate, then there are specific ways that you can determine if an associated object has been lazy-loaded.
For example, let's say you have an entity class Foo that contains a #ManyToOne 'foreign' association to entity class Bar which is represented by a field in Foo called bar.
In you DTO mapping code you can check if the associated bar has been lazy-loaded using the following code:
if (!(bar instanceof HibernateProxy) ||
!((HibernateProxy)bar).getHibernateLazyInitializer().isUninitialized()) {
// bar has already been lazy-loaded, so we can
// recursively load a BarDTO for the associated Bar object
}
The simplest solution to achieve what you desire is to clear the entity manager after querying and before invoking the DTO mapper. That way, the object will be detached and access to uninitialized assocations will trigger a LazyInitializationException instead.
I felt your pain as well which drove me to developing Blaze-Persistence Entity Views which allows you to define DTOs as interfaces and map to the entity model, using the attribute name as default mapping, which allows very simple looking mappings.
Here a little example
#Entity
class Person {
#Id Long id;
String name;
String lastName;
String address;
String city;
String zipCode;
}
#EntityView(Person.class)
interface PersonDTO {
#IdMapping Long getId();
String getName();
}
Querying would be as simple as
#Transactional(readOnly=true)
public PersonDTO findPersonWithInvoicesById(Long id) {
return personRepository.findById(id);
}
interface PersonRepository extends EntityViewRepository<PersonDTO, Long> {
PersonDTO findById(Long id);
}
Since you seem to be using Spring data, you will enjoy the spring data integration.

Resources