How to use Hibernate lazy loading and reactive streams? - java

I am currently building a REST service with Micronaut Data. I have defined two JPA entities, bounded by a bidirectional #OneToMany relationship, and lazy loading.
#Entity
#Getter
#Setter
public class ScoringEntity {
#Id
private String id;
#OneToMany(mappedBy = "scoring", fetch = FetchType.LAZY)
private Set<ContributionEntity> contributions;
}
#Entity
#Getter
#Setter
public class ContributionEntity {
#Id
private String id;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId
private ScoringEntity scoring;
}
#Repository
public interface ScoringRepository extends GenericRepository<ScoringEntity, String> {
Page<ScoringEntity> findAll(Pageable pageable);
}
In the controller, I return a Mono which is set to call the repository, then perform a mapping to a DTO (Scoring).
#Get
public Mono<Page<Scoring>> getScoring(Pageable pageable) {
return Mono.fromCallable(() -> scoringRepository.findAll(pageable))
.map(scoringEntities -> scoringEntities.map(scoringMapper::mapScoring));
}
Everything works fine in the findAll() method, but things go south when the mapper
tries to access the contributions set :
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ScoringEntity.contributions, could not initialize proxy - no Session
While I understand why this happens (the transaction probably ends with the repository method), I can't find a satisfying solution. Setting the relationship to eager loading works, but it
significantly decreases performance (and I've read elsewhere it would be a code smell, which I tend to believe).
Besides I can't imagine that reactive streams be incompatible with hibernate and lazy loading.
What would be the best practice in this situation ?

There are a few options:
Add #Join("contributions") annotation to your repository method
Add #EntityGraph ... annotation to your repository method
Do fetching and mapping in one method annotated #ReadOnly or #Transactional so the session is open when the mapper is called
In this case, having reactive streams doesn't make much sense. Just return Page from the controller annotated #ExecuteOn(TaskExecutors.IO).

Mapping the association as EAGER is usually a code smell. But if you have situations where you always need to load an association, you can do it via a query.
Instead of findAll, you would call:
#Repository
public interface ScoringRepository extends GenericRepository<ScoringEntity, String> {
#Query("select se from ScoringEntity se left fetch join se.contributions")
Page<ScoringEntity> findScoringWithContributions(Pageable pageable);
}
Besides I can't imagine that reactive streams be incompatible with hibernate and lazy loading.
In this case the problem is that you are trying to lazy load the association after the session has been closed.

Related

OneToMany lazy initialization when needing collection data

What's a workaround if I have a relation OneToMany and would like to access the collection that is lazy loaded? Currently I get LazyInitializationException having this:
Club entity:
#Entity
public class Club {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "club", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JsonBackReference
private List<Player> players;
Player entity:
Is it a good idea to have two methods, where one fetches data without players and the second one that fetches also players?
#Override
List<Club> findAll();
#Query("Select clubs from Club clubs left join fetch clubs.players")
List<Club> getAllClubsWithPlayers();
What I'm thinking of is that it is a bad idea, because if I have a situation where I have for example 4 properties that are lazy loaded and I'd need at once 3 of them, I'd have to have a query like: getAllClubsWithPlayersAndSponsorsAndCoaches, so I'd have to have a lot of combinations of such queries.
I don't want to use EAGER, so could you tell me if it's a common way to do this if I need access to players from the Club sometimes which is undoable with the findAll() method that throws LazyInitializationException?
Don't get me wrong - I know where the LazyInitializationException comes from, but I just don't know what's the best way to get access to players if I sometimes need them when fetching clubs. Is my way of doing it correct?
There are 3 choices:
Access all the lazy fields inside a #Transactional method. You don't show your code, but there's usually a Service Facade layer which is responsible for being #Transactional. It invokes Repositories.
Write a query that fetches all the required data. Then you'd have to create a method specifically to fetch all the lazy fields required for that logic.
Use OpenSessionInViewFilter or OpenSessionInViewInterceptor so that Session/EntityManager are started before the execution even reaches the Controller. The Session then would be closed by the same high-level layer at the end of the request processing.
In addition to what Stanislav wrote, I'd like to elaborate on his 2nd point, because I think that this is often the best approach - that's simply because it saves unnecessary calls to the database which results in better performance.
Apart from writing separate JPQL query in your repository for each use-case, you could do one of the following .:
Make your repository extend JpaSpecificationExecutor and programmatically describe what needs to be fetched as described in this answer
Use Entity Graph either described using annotations, or programmatically, and fetch your entities using EntityManager as described in this tutorial
To optionally load what you want you can use EntityGraph.
Declare #NamedEntityGraph at your entity
#Entity
#NamedEntityGraph(name = "Club.players",
attributeNodes = #NamedAttributeNode("players")
)
public class Club {
Then you should annotate your findAll() method with this graph using it's name
#EntityGraph(value = "Club.players")
List<Club> findAll();
However that would override your basic findAll() method.
To avoid this (to have both implementations) you can follow this way:
Add dependency from https://mvnrepository.com/artifact/com.cosium.spring.data/spring-data-jpa-entity-graph/<version>
Then replace your repository with
#Repository
public interface ClubRepository extends JpaSpecificationExecutor<Club>, JpaRepository<Club, Long>, EntityGraphJpaSpecificationExecutor<Club> {
}
And then you'll have basic method findAll() and also from your sevice you can call
List<Club> clubs = clubRepository.findAll(specification, new NamedEntityGraph(EntityGraphType.FETCH, "Club.players"))

Springboot JPA entity is auto loading lazy join entities

I have an entity as
#Getter
#Setter
#Entity
#Table(name = "feature")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Feature {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#OneToMany(mappedBy = "featureId", fetch = FetchType.LAZY)
private transient Collection<FeatureComponent> components;
}
While in its Repository(Dao) file, I have
public interface FeatureDao extends JpaRepository<Feature, Integer> {
#Query("SELECT e FROM Feature e")
public List<Feature> getAll();
#Query("SELECT e FROM Feature e LEFT JOIN e.components fc WHERE e.id= :id")
public Feature getWithDetail(#Param("id") Integer id);
}
When I'm calling featureDao.getAll(); it returns all features but including components list filled and because of that, my response it being too large to load on client-side.
I'm unable to understand why it is happening when I'm using Lazy fetch mode and didn't mentioned joining with components in my getAll method.
Please help to resolve that issue,
Thanks in advance.
Just like #spOOm already mentioned I also suspect this is the side effect of Jackson Feature entity serialization into JSON triggering the load of all the components.
That is why using DTOs instead of plain Entities is usually advisable when returning data via a Controller. With DTOs, you clearly define whatever you want to include in the response to the caller. You can even reorganize your model so that it fits better the needs of the clients. You decouple your inner model and the model your API consumers know, making it possible to rework your inner model and still keep the same public model. You could have the following DTO.
public class FeatureSimpleDto {
private Integer id;
private String name;
private String description;
}
Then, in your Controller or Service (here you can find different opinions) you would basically convert your Feature entity into a FeatureSimpleDto that would be returned by your Controller. Here you can use mapping libraries such as MapStruct or you can do it on your own (I usually tend to prefer doing it on my own, one less dependency to rely on).
Using Lombok may be a problem, depending on the relationship between tables, try to create getters manually in entity classes.
Thanks to everyone for providing workarounds... But every work item requires lots of changes which were not possible for me...
Luckily I found a solution that is working fine for me... Better to post here...
Step-1: Add dependency in pom.xml file
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
Step-2: Add a 'Bean' for Hibernate Module
We can add bean in any file having #Configuration annotation... Even, we can add in Springboot main application file(where we have main method).
#Bean
public Module datatypeHibernateModule() {
return new Hibernate5Module();
}
That's it, Happy Coding...

JPA Projection #OneToMany not working 'Unable to locate appropriate constructor'

I have a large entity-class with many, many fields and a projection-class which should be a part of the large one.
Everything works fine, except the #OneToMany field. The #OneToMany field should be a list of addresses, but when converting it to the projection-class I always get the error "Unable to locate appropriate constructor [...] Expected arguments are: long, [...], ***.entity.Address".
The converter is searching for a single address Object instead of a List of Address Objects, and I don't understand why. I use lombok #Data and #AllArgsConstructor, so Getter and Setter should be there.
#Entity
#Data
public class House implements Serializable {
#Id
#Column(name = "ID", precision = 5)
private Long id;
#OneToMany
#JoinColumn(name = "HouseID")
private List<Address> identAdressen;
}
/// ----------------
#Data
#AllArgsConstructor
public class HouseView {
private Long objnr;
private List<Address> identAdressen;
}
When I remove the "List" in the HouseView-class it works as long as there are only single addresses, but when there are multiple it crashes too.
I don't get it why he tries to find a HouseView-Constructor with an single address-object, when in both classes there are Lists of Addresses.
Let trying not using lombok and see if it works
Spring Data Projections do not support collections 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(House.class)
public interface HouseView {
#IdMapping
Long getObjnr();
Set<AddressView> getIdentAddressen();
#EntityView(Address.class)
interface AddressView {
#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.
HouseView a = entityViewManager.find(entityManager, HouseView.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<HouseView> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
What you can do is to write custom query on HouseRepository as
#Query("SELECT new com.packagename.HouseView(hos) from House hos")
then house view class can be as follows
public class HouseView {
private Long objnr;
private List<Address> identAdressen;
public HouseView(House house) {
identAdressen = house.identAdressen;
}
}
Hope it will work :) You can add more fields into constructor from join tables if required. It might help others coming late on post.

Spring JPA repository interface and default methods use case

I am currently wondering whether a particular use case could be elegantly addressed by using a default interface method inside a JPA repository.
Suppose we have the following entity and supporting types:
public enum Status {
STATUS_1,
STATUS_2,
STATUS_3
}
#Entity
#Getter // from lombok
#Setter // from lombok
public class SomeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "status")
private Status status;
}
I need to be able to query a list of SomeEntity based on combination of Status enumeration values.
With Spring JPA, its respective repository could look like this:
#Repository
public interface SomeRepository extends JpaRepository<SomeEntity, Integer> {
#Query("select s.id, ... from SomeEntity s where s.status in (:statuses)")
List<SomeEntity> getByStatuses(#Param("statuses") List<Status> statuses);
}
The particular combinations of statuses that must be passed, however is very much dependent on the domain of SomeEntity, and I'd like to "bake-in" those statuses in the repository call. Why in the repository -- currently the same repository is used from multiple services, and SomeEntity is updated as part of transactions dealing with other entities updated together according to the service's specifics. The particular getByStatuses calls are the same for all services, and we have duplicated code fragments looking roughly like this:
List<Status> statuses = Arrays.asList(Status.STATUS_1, Status.STATUS_3);
List<SomeEntity> entities = someRepository.getByStatuses(statueses);
....
I was wondering if I could extract the above fragment in a default method inside the repository and change my services to use that method instead:
#Repository
public interface SomeRepository extends JpaRepository<SomeEntity, Integer> {
#Query("select s.id, ... from SomeEntity s where s.status in (:statuses)")
List<SomeEntity> getByStatuses(#Param("statuses") List<Status> statuses);
default List<SomeEntity> getByRelevantStatuses() {
List<Status> relevantStatuses = Arrays.asList(Status.STATUS_1, Status.STATUS_3);
return this.getByStatuses(relevantStatuses);
}
}
My concerns are the "magic" behind the scenes which Spring will do, could it cause issues with an approach like the above?
It appears that the default interface method usage in the described scenario is working. I tried it and so far I have not seen any problems.
I'll still be happy to know if there are things to worry about, and if I find anything useful myself, I will update this post.

How to design JPA polymorphic relationships in java?

I am designing a product catalogue. I would like to have a category tree, where products can be connected only to LeafCategories, that can have one parent Category.
I am using Spring Boot, Spring Data and Hibernate 4 and H2 database(for now).
Base entity for the task is AbstractCategory (Is it there a better way to inherit relationships ?) (Getters and Setters omitted, NamedEntity is a #MappedSuperclass with String name and Long id)
public abstract class AbstractCategory extends NamedEntity{
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "parentId")
Category parent;
}
Category entities - they are not leafs and cannot have Products connected to them:
#Entity
public class Category extends AbstractCategory {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "parent")
Collection<AbstractCategory> subcategories;
}
LeafCategory it can be used as a property for my Product entity.
#Entity
public class LeafCategory extends AbstractCategory {
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "category")
Collection<Product> products;
}
I have a very simple CrudRepository for Category and an identical for LeafCategory
#Repository
#Transactional
public interface CategoryRepository extends CrudRepository<Category, Long> {}
When I load a Category from CategoryRepository and access getSubcategories() I get following exception:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: uj.jg.domain.products.Category.subcategories, could not initialize proxy - no Session
First of all - How can I improve the design? Second and more concrete question is why is #Transactional not keeping a session open? I know I could just use FetchType.EAGER, but it's a recursive structure - if my understanding of Hibernate is correct it would mean loading the whole subtree, and I don't want that. I don't want to use Hibernate.initialize either.
I do not have any config for database or hibernate. I am using devtools from spring.boot:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
How can I improve the design?
It looks reasonable to me.
Why is #Transactional not keeping a session open?
You have placed #Transactional on the repository. The DB session is open only for the time of running the query, which returns the categories with their subcategories marked as lazy-loaded. Then, the session is closed (once the repository method returns) and you are trying to access the subcategories after that, when there's no session any more. Move the #Transactional annotation higher up the call stack - to the service layer if you are using a 3-layer architecture (cf. this post).
Since the repository methods run just a single query, there's no need to mark them as #Transactional - they will run within a transaction anyway. It only makes sense to have #Transactional when you run several queries or run a query and some other processing (which could throw an exception and you'd want the query to be rolled back because of it). That's why, again, if you want to explicitly mark anything as #Transactional it would rather be in the service layer.
First of all, you get the LazyInitializationException because the Session got closed and not all children were initialized.
Even if you used EAGER (which is often a bad decision), you'd only fetch a single level in your nested children tree.
You can use recursion to traverse all children and force their initialization before returning the result from the DAO method, which requires you to provide a custom implementation for the find method:
public Category findOne(Long id) {
Category category = entityManager.find(Category.class, id);
fetchChildren(category);
return category;
}
public void fetchChildren(Category category) {
for (Category _category : category.getSubcategories()) {
fetchChildren(_category);
}
}

Categories

Resources