Springboot JPA entity is auto loading lazy join entities - java

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...

Related

Best Way to SELECT subset of properties for associated #Entity

I am trying to choose the properties of associated entities that will be loaded.
For example:
#Entity
#Getter #Setter
public class Book {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "first")
private String first;
#Column(name = "second")
private String second;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
private List<Page> pages = new ArrayList();
}
#Entity
#Getter #Setter
public class Page {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "number")
private Integer number;
#Column(name = "content")
private String content;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "book_id")
private Book book;
}
I would like to have just a SELECT on the Book's first and the content of all associated Page entities
book
- first
- pages
- content
For example, in PostgreSQL this might look something like
SELECT book.first,
array_agg(page.content || ' ')
FROM book
LEFT JOIN page
ON page.book_id = book.id
GROUP BY book.first
I've done my research on how you could go about doing this, most mentioned solution is to use Spring DATA JPA Projections (ref)
I did the following:
public interface FindAllBookProjection {
String getFirst();
List<PageProjection> getPages();
interface PageProjection {
Integer getNumber();
}
}
The issue with projections is that they cause the famous N+1 select problem as the pages are loaded lazily.
I also couldn't find a way to use projections with #Query in the #Repository for the nested properties.
There are other mentions online to use #EntityGraph. From what I understand #EntityGraph will not work as it also selects all properties, specifying only what Entity associations should be loaded.
There are also suggestions on using other libraries like Blaze Persistence (ref) or Hibernate's ResultTransformer (ref) . I would prefer to use only Spring DATA JPA instead of introducing another library for this functionality and writing ResultTransformers seems like adding a lot of boilerplate code to the mix.
To summarize my question is what is the best way to choose what properties are selected for #Entity associations. The main goal is to avoid pulling unnecessary amount of data from the database. The given example above is for demonstration, the data that I am working on includes over 10 columns and spans across 3-4 entities. Having control over the data means better performance.
I am the creator of Blaze-Persistence and I can tell you that if there were an easy way to do this, I would not have created Entity-Views. You can read some of the articles I wrote about this and you will realize that doing this yourself, like you already figured, will require lots of boilerplate code. Doing this efficiently, will require even more code and in the end, you will probably end up with a solution that is inferior to Blaze-Persistence Entity-Views in one way or another and has probably no documentation.
Spring Data Projections is just limited and as I tried to outline many times before, Blaze-Persistence Entity-Views is like Spring Data Projections on steroids.
If you have "just" one or two simple mappings, you might be able to get this done by introducing special #Immutable #Entity, maybe even with #Subselect in this particular case to model what you need, but believe me, this only works good on a small scale. Apart from that, Blaze-Persistence which works on top of JPA/Hibernate enables the use of a lot of advanced SQL features, which you usually can't use from within plain JPA/Hibernate.
In my opinion, ORM libraries should use whole objects, that means loading all data into the program and then transforming/filtering according to logic.
For specific use-cases, where performance is really hindered, I'd use entities in
the database, such as Views/Procedures.
In your case, i'd create a View:
CREATE VIEW book_content as
SELECT book.first as [first],
array_agg(page.content || ' ') as [content]
FROM book
LEFT JOIN page
ON page.book_id = book.id
GROUP BY book.first
And then create a #Repository and #Entity for it in spring.

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.

How to take decision while developing web application using microservices in spring-boot for common code and relationship

I am going to develop small web application to get an idea of micro-services architecture, using spring-boot and spring-data-jpa.
At initial stage, I have only 2 entities (1) CountryEntity and (2) StateEntity. In monolithic structure which already I have, there is a relationship between these 2 entities like :
CountryEntity:
#Entity
#Data
#EqualsAndHashCode(callSuper=false)
public class CountryEntity extends BaseEntity{
private String countryCode;
private String country;
#OneToMany(mappedBy = "country", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<StateEntity> stateEntities;
}
StateEntity :
#Entity
#Data
#EqualsAndHashCode(callSuper=false)
public class StateEntity extends BaseEntity {
private String stateCode;
private String state;
#ManyToOne
private Country country;
}
Also, please notice that I have extends BaseEntity which contains shared fields like :
BaseEntity :
#MappedSuperclass
#Data
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Date createdDateTime;
private Date updatedDateTime;
private Boolean isActive;
private Boolean isDelete;
#PrePersist
private void setCreatedDateTime() {
this.createdDateTime = new Date();
this.isActive = true;
this.isDelete = false;
}
#PreUpdate
private void setUpdatedDateTime() {
this.updatedDateTime = new Date();
}
}
Now, I have 2 queries while converting above monolithic structure to microservices as describe here :
1. Should I create shared library (separate microservice for shared code and then add dependency in all different microservices app) for BaseEntity defined above and other classes like Constants, Utility etc.? or I should repeat all the code in all microservices? Please guide because some sites are suggesting for shared library and some experts are advice not to use shared library and suggesting to repeat all the code. What should I do for better architecture.
2. How can I give relationship between above 2 entities in microservices architecture? I gone through SO questions and some feedback says that it is not possible. Then I have an application, in which all entities are in a relation directly or indirectly. Here, I have just take an example from original application. I know it must be some structure or some way, with which I can apply relation-ship. Please guide me.
Thank you so much in advance.
When you are working in Microservices architecture, you should put all common code into a shared module. This module can be deployed as a library to your Maven or any other repository and injected to your microservices on demand.
If you make a copy of code into your microservices - it will be harder to maintain it further. Imagine 100 microservices, which have a common class inside, and you want to change it. It will be even harder when you decide to split microservices into different repositories.
But you should be very careful when using Spring REST repository. When you inject into your microservice common repository for any entity, it will automatically expose it to REST, so you'll have the same REST points on different microservices, that's not good. There are may be troubles with Spring Security (if you forgot to hide it somewhere).

jpa - Multiple #ElementCollection in two different #Embeddable classes

I have an entity with two Embedded classes of the same type and which one has an ElementCollection of the same type two. The business logic is apparently correct, but I am experiencing some problems with lack of knowledge in JPA, I guess.
Let's check my classes:
#Entity
public class Etapa extends EntidadeBase {
#Embedded
private CronogramaDeDesembolso cronogramaDeReceita;
#Embedded
private CronogramaDeDesembolso cronogramaDeDespesa;
}
#Embeddable
public class CronogramaDeDesembolso {
#ElementCollection
private List<Parcela> parcelas;
}
I am receiving the following error log.
Caused by: org.hibernate.HibernateException: Found shared references
to a collection:
nexxus.convenioestadual.dominio.planodetrabalho.etapa.Etapa.cronogramaDeReceita.parcelas
Do you guys have any clue of what is wrong and how can I fix it?
EDIT:
Due comments I did this edit and it do not worked too
#Entity
public class Etapa extends EntidadeBase {
#Embedded
#AttributeOverride(name = "parcelas", column = #Column(name = "parcelasReceita"))
private CronogramaDeDesembolso cronogramaDeReceita;
#Embedded
#AttributeOverride(name = "parcelas", column = #Column(name = "parcelasDespesa"))
private CronogramaDeDesembolso cronogramaDeDespesa;
}
Is there any reason why you have decided to use this structure ? Typically when converting an object to an RDBMS you would need to model the relationships. When you use an embeddable it will add the column (or columns) associated with it to the table. So when you do this normally (not collections) it is fine.
When you do a collection it runs into issues. Mainly there is no way to represent a collection in a single row (since this is an entity you could have many of them so effectively for each object you only have one row) & one column. So when you represent a collection you actually have to have a second table with a column referencing it back to the first. It's really the opposite thinking of a normal object. The collection entries need to know what collection they were associated with instead of the collection being knowledgeable of its entries.
So in some POJO you could have and these....
MyListObject {
//Some implementation of things you want to collect
}
MyClass {
List<MyListObject> myListObject;
}
But to model this in JPA you would need to have these represented by two tables.
Your object that will be in the list.
#Entity
MyListObject {
#ManyToOne
#JoinColumn(name = "MY_CLASS_KEY")
private MyClass myClass;
}
Your object/entity that will have the list.
#Entity
MyClass {
#Id
#Column(name = "MY_CLASS_KEY")
private Long myClassKey;
#OneToMany(mappedBy = "myClass")
private List<MyListObject> myString;
}
I hope this helps.
A quick search on Google turned up this in StackOverflow:
JPA Multiple Embedded fields
It would seem as though you have to do some explicit annotation overriding over the fields within the embeddable class. There are some code examples in the linked answer as well that should give you a good idea of where to go.
Cheers,

Mapping JSON object to Hibernate entity

I'm going to start a project of a REST application managed with Spring and with Hibernate for my model.
I know that Spring allows you to get Java object from the HTTP Request (with #Consumes(JSON) annotation). Is there any conflict if this Java object is also a Hibernate entities? And is nested object working (like #ManyToOne relation)?
Maven dependency
The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
Domain model
Now, if you are using PostgreSQL, you need to use the JsonType from Hibernate Types.
In order to use it in your entities, you will have to declare it on either class level or in a package-info.java package-level descriptor, like this:
#TypeDef(name = "json", typeClass = JsonType.class)
And, the entity mapping will look like this:
#Type(type = "json")
#Column(columnDefinition = "json")
private Location location;
If you're using Hibernate 5 or later, then the JSON type is registered automatically by the Postgre92Dialect.
Otherwise, you need to register it yourself:
public class PostgreSQLDialect extends PostgreSQL91Dialect {
public PostgreSQL92Dialect() {
super();
this.registerColumnType( Types.JAVA_OBJECT, "json" );
}
}
The JsonType works with Oracle, SQL Server, PostgreSQL, MySQL, and H2 as well. Check out the project page for more details about how you can map JSON column types on various relational database systems.
Yes, this wouldn't be a problem and is actually a fairly common practice.
In the recent years I have come to realize that sometimes, however, it is not a good idea to always build your views based on your domain directly. You can take a look at this post:
http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/
It is also known as "Presentation Model":
http://martinfowler.com/eaaDev/PresentationModel.html
The idea behind that is basically the following:
Imagine you have the domain entry User, who looks like that :
#Entity
#Data
public class User {
#Id private UUID userId;
private String username;
#OneToMany private List<Permission> permissions;
}
Let's now imagine you have a view where you wanna display that user's name, and you totally don't care about the permissions. If you use your approach of immediately returning the User to the view, Hibernate will make an additional join from the Permissions table because event though the permissions are lazily loaded by default, there is no easy way to signal to the jackson serializer or whatever you are using, that you don't care about them in this particular occasion, so jackson will try to unproxy them (if your transaction is still alive by the time your object is put for json serialization, otherwise you get a nasty exception). Yes, you can add a #JsonIgnore annotation on the permissions field, but then if you need it in some other view, you are screwed.
That a very basic example, but you should get the idea that sometimes your domain model can't be immediately used to be returned to the presentation layer, due to both code maintainability and performance issues.
We were using such approach to simplify design and get rid of many dtos (we were abusing them too much). Basically, it worked for us.
However, in our REST model we were trying to do not expose other relations for an object as you can always create another REST resources to access them.
So we just put #JsonIgnore annotations to relations mappings like #OneToMany or #ManyToOnemaking them transient.
Another problem I see that if you still like to return these relations you would have to use Join.FETCH strategy for them or move transaction management higher so that transaction still exists when a response is serialized to JSON (Open Session In View Pattern).
On my opinion these two solutions are not so good.
You can map the json request without using any library at REST web-services (Jersy)
this sample of code:
This hibernate entity called book:
#Entity
#Table(name = "book", schema = "cashcall")
public class Book implements java.io.Serializable {
private int id;
private Author author; // another hibernate entity
private String bookName;
//setters and getters
}
This web-services function
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String addBook(Book book) {
String bookName=book.getName();
return bookName;
}
This is sample json request:
{
"bookName" : "Head First Java",
"author" : {
"id" : 1
}
}
Since you are just starting, perhaps you could use Spring Data REST?
This is the project: http://projects.spring.io/spring-data-rest/
And here are some simple examples:
https://github.com/spring-projects/spring-data-book/tree/master/rest
https://github.com/olivergierke/spring-restbucks
As you can see in the examples, there are no extra DTOs beyond the #Entity annotated POJOs.

Categories

Resources