Pattern for accessing data outside of transaction - spring

I have a Spring Boot App with Spring Data JPA with hibernate and MySQL as the data store.
I have 3 layers in my application:
API Service
Application Service
Domain Service ( with Repository)
The role of Application Service is to convert hibernate-backed POJOs to DTOs given some business logic.
POJO
SchoolClass.java
#Column
Long id;
#Column
String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "schoolClass")
List<Book> books;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "schoolClass")
List<Student> students;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "schoolClass")
List<Schedule> schedules;
Domain Service - My transaction boundary is at the Domain Service layer.
SchoolClassService.java
#Autowired
private SchoolClassRepository repository;
#Transactional(readOnly = true)
public SchoolClass getClassById(Long id) {
return repository.findById(id);
}
Application Service
SchoolClassAppService.java
#Autowired
private SchoolClassService domainService;
public SchoolClassDto getClassById(Long id) {
SchoolClass schoolClass = domainService.getClassById(id);
// convert POJO to DTO;
return SchoolClassDto;
}
My problem is that at times the child entities on SchoolClass are empty when I try to access them in SchoolClassAppService. Not all of them, but out of the three, two would work fine but the third one would be empty. I tried to mark the children lists to be eagerly fetched, but apparently only two collections can be eagerly fetched before Hibernate starts throwing exceptions and it also does not sound like good practice to always load all the objects. I do not get LazyInitializationException, just the list is empty.
I have tried to just call the getter on all lists in the domain service method before returning it just to load all data for the POJO but that does not seem like a clean practice.
Are there any patterns available which keep the transaction boundaries as close to the persistence layer as possible while still make it viable to process the data even after the transaction has been closed?

Not sure why your collections are sometimes empty, but maybe that just how the data is?
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(SchoolClass.class)
interface SchoolClassDto {
String getName();
List<BookDto> getBooks();
}
#EntityView(Book.class)
interface BookDto {
// Whatever data you need from Book
}
Querying could look like this
List<SchoolClassDto> dtos = entityViewManager.applySetting(
EntityViewSetting.create(SchoolClassDto.class),
criteriaBuilderFactory.create(em, SchoolClass.class)
).getResultList();
Just keep in mind that DTOs shouldn't just be copies your entities but should be designed to fit your specific use case.

Related

Is there functionality in JPA/Spring/Hibernate/etc similar to JPA's #PrePersist that would allow changes to related entities?

I need to store normalized (i.e. without special characters etc.) variants of some of the String fields of some entities.
An example:
#Entity
public class Car {
#Id
private Long id;
private String make;
private String model;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "CAR_ID")
private Set<NormalizedField> normalizedFields = new HashSet();
private Set<NormalizedField> createNormalizedFields(Car car) {
Set<NormalizedField> normalized = normalize(car);
this.normalizedFields.clear();
this.normalizedFields.addAll(normalized);
}
// I would use this approach, but it doesn't allow
// changes to related entities.
// #PreCreate
// public void onCreate() {
// createNormalizedFields();
// }
}
#Entity
public class NormalizedField {
#Id
private Long id;
private String fieldName;
private String normalizedValue;
}
It would be convenient if the normalized values were automatically (re)created whenever the Car entity is persisted. Is there a way to trigger the creation method automatically?
Using #PrePersist, #PreUpdate... is obviously not an option as it doesn't allow changes to related entities.
Spring AOP is not used in the project, so I would rather avoid introducing it for now. But it's an option anyways.
The application is huge, and managing the normalized values 'manually' would require quite a bit of work, hence I leave it as the last option.
Going to post this half-answer here ('half' because it provides a workaround with restrictions).
In some cases org.hibernate.Interceptor can be used to manage child entities whenever the parent entity is changed.
But there are restrictions: the javadoc says Session is not to be used in the Interceptor. JPA repository methods, JPQL or HQL calls are intercepted by the same Interceptor in a loop. Even native queries get intercepted unless you set FlushMode.COMMIT or FlushMode.MANUAL (and maybe some other).
The above means you'll probably have to use the datasource directly. I don't remember exactly how, but Spring provides means to execute queries using datasource directly and within current transaction. In my case it was enough as I had to manage some technical child entities that didn't need a representation as an Entity.

Spring Data findById vs Join Query performance

I have a #OneToMany relation between an Entity say Class with Student. Now for each class there can be atleast 100 students. This is how my relationship with Student is defined in Class entity
#OneToMany(mappedBy = "classDataEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<StudentDataEntity> studentDataEntities;
Just to check the performance of fetching class by ID (PK) we use two methods
Optional findById(ID id); // with fetchType Eager with Students
create a new method in repository with #Query joining the two tables in classId
We are calling both methods from the same service class method , e.g
#Transactional
public ClassDataEntity fetchClassEntity(Long classId){
ClassDataEntity classDataEntityJOined = repo.fetchClassWithStudents(id);
ClassDataEntity classDataEntity = repo.findById(id);
}
My understanding is with lot of Students , the join should perform better since its less call to DB , hence less network calls. But in the above case we are seeing findById performing much better
Is it because the data with the id is already in session? Also when are Hibernate sessions created and destroyed when invoked via Crud Repositories
Yeah, it's because the data is already in the persistence context. If you remove #Transactional you should see that two queries are executed because then the persistence context would not be shared (unless you have open-session-in-view enabled in spring).

Multiple Repositories for the Same Entity in Spring Data Rest

Is it possible to publish two different repositories for the same JPA entity with Spring Data Rest?
I gave the two repositories different paths and rel-names, but only one of the two is available as REST endpoint.
The point why I'm having two repositories is, that one of them is an excerpt, showing only the basic fields of an entity.
The terrible part is not only that you can only have 1 spring data rest repository (#RepositoryRestResource) per Entity but also that if you have a regular JPA #Repository (like CrudRepository or PagingAndSorting) it will also interact with the spring data rest one (as the key in the map is the Entity itself).
Lost quite a few hours debugging random load of one or the other. I guess that if this is a hard limitation of spring data rest at least an Exception could be thrown if the key of the map is already there when trying to override the value.
The answer seems to be: There is only one repository possible per entity.
I ended up using the #Subselect to create a second immutable entity and bound that to the second JpaRepsotory and setting it to #RestResource(exported = false), that also encourages a separation of concerns.
Employee Example
#Entity
#Table(name = "employee")
public class Employee {
#Id
Long id
String name
...
}
#RestResource
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
}
#Entity
#Immutable
#Subselect(value = 'select id, name, salary from employee')
public class VEmployeeSummary {
#Id
Long id
...
}
#RestResource(exported = false)
public interface VEmployeeRepository extends JpaRepository<VEmployeeSummary, Long> {
}
Context
Two packages in the monolithic application had different requirements. One needed to expose the entities for the UI in a PagingAndSortingRepository including CRUD functions. The other was for an aggregating backend report component without paging but with sorting.
I know I could have filtered the results from the PagingAndSorting Repository after requesting Pageable.unpaged() but I just wanted a Basic JPA repository which returned List for some filters.
So, this does not directly answer the question, but may help solve the underlying issue.
You can only have one repository per entity... however, you can have multiple entities per table; thus, having multiple repositories per table.
In a bit of code I wrote, I had to create two entities... one with an auto-generated id and another with a preset id, but both pointing to the same table:
#Entity
#Table("line_item")
public class LineItemWithAutoId {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
...
}
#Entity
#Table("line_item")
public class LineItemWithPredefinedId {
#Id
private String id;
...
}
Then, I had a repository for each:
public interface LineItemWithoutId extends Repository<LineItemWithAutoId,String> {
...
}
public interface LineItemWithId extends Repository<LineItemWithPredefinedId,String> {
...
}
For the posted issue, you could have two entities. One would be the full entity, with getters and setters for everything. The other, would be the entity, where there are setters for everything, but only getters for the fields you want to make public. Does this make sense?

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.

Spring, JPA -- integration test of CRUD of entity which has many transitive dependencies of other entities

I have entity e.g. Product which aggregates other entities such as Category. Those entities can also aggregate other entities and so on. Now I need to test my queries to database.
For simple CRUD I would create mock of EntityManager. But what if I have more complex query which I need to test for correct functionality. Then I probably need to persist entity (or more of them) and try to retrieve/update, whatever. I would also need to persist all entities on which my Product depends.
I don't like such approach. What is the best way to test such queries?
Thanks for replies.
Update -- example
Lets assume following entity structure
This structure is maintained by JPA implementation. For example Product class would look like this
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private Category category;
#ManyToOne
private Entity1 something;
}
So now if I want to test any query used in DAO I need to create Product in database, but it is dependent on Category and Entity1 and there is #ManyToOne annotation so values cannot be null. So I need to persist those entities too, but they have also dependencies.
I'm considering pre-creating entities such Category, Entity1 and Entity2 before test using SQL script or dbunit (mentioned by #chalimartines) which would save large amount of code, but I don't know whether it is good solution. I would like to know some best practices for such testing.
you can use #TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) as
#ContextConfiguration(locations={"classpath:/path/to/your/applicationContextTest.xml"})
#RunWith( SpringJUnit4ClassRunner.class)
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class YourClassTest {
#Test
public void test() {
//your crud
}
}
update
You cant set the dependecies to null in order to avoid to persist them
I don't know other way, but for persisting Product and its dependencies you can use testing framework DBunit that helps you setup database data.

Resources