OneToMany in MappedSuperclass - spring-boot

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.

Related

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: combine JPA Derived query methods and query by example

Is it possible to use JPA derived methods and query by example at the same time?
Let's imagine i have two entities like this:
#Entity
#Data
public class Person {
#Id
#GeneratedValue
Long id
String name;
String surname;
#OneToMany
List<Dog> dogs;
}
#Entity
#Data
public class Dog{
#Id
#GeneratedValue
Long id
String name;
}
I'd like to be able to do something like this (just an example):
Person p = new Person ();
p.setName("Mario");
personRepository.findDistinctByDogsIsNotNull(Example.of(p));
The Example.of(p) only works if i do findAll, but it doesn't work if i define inside the repository a method like this
private interface PersonRepository extends JpaRepository<Person, Long>{
List<Person> findDistinctByDogsIsNotNull(Example<Person> example)
}
The error it gives me is something like this:
Failed to create query for method public abstract java.util.List dev.cele.test.repository.PersonRepository.findDistinctByDogIsNotNull(org.springframework.data.domain.Example)! At least 1 parameter(s) provided but only 0 parameter(s) present in query.
So my question is: is it possible to do a query by example in a JPA derived query method?
And if it's not possible how can i create some sort of parametrizable query that also has a predetermined condition?

LazyInitializationException when fetching #EntityGraph from Hibernate 2-nd level cache

I'm developing a Spring Boot 2.3.4 web application with Spring Data JPA.
I want to use the Hibernate 2-nd level query cache for a repository method with #EntityGraph. However, I get a LazyInitializationException when generating a Thymeleaf view in case data is already in the 2-nd level cache unless I have Spring’s Open Session In View turned on. When fetching data for the first time from the database or without the 2nd level cache everything is OK even with spring.jpa.open-in-view=false. Moreover, if I enable spring.jpa.open-in-view there is no exception when fetching data from the cache without any select to the database.
How can I make Hibernate fetch at once all the associations specified in the #EntityGraph when using Hibernate 2nd level cache?
Here is my repository method:
#org.springframework.data.jpa.repository.QueryHints({#javax.persistence.QueryHint(name = "org.hibernate.cacheable", value = "true")})
#EntityGraph(attributePaths = { "venue.city", "lineup.artist", "ticketLinks" }, type = EntityGraphType.FETCH)
Optional<Event> findEventPageViewGraphById(long id);
and part of the entity:
#Entity
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "venue_id")
private Venue venue;
#OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#OrderBy("orderId")
private Set<TicketLink> ticketLinks = new LinkedHashSet<>();
#OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true)
#OrderBy("orderId")
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<ArtistEvent> lineup = new LinkedHashSet<>();
}
That's a known issue. Hibernate does not check the 2nd level cache for associations when constructing "just proxies". You need to access the objects to initialize them, which will then trigger a 2nd level cache hit.
I would recommend you use 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(Event.class)
public interface EventDto {
#IdMapping
Long getId();
VenueDto getVenue();
#MappingIndex("orderId")
List<TicketLinkDto> getTicketLinks();
#MappingIndex("orderId")
List<ArtistEventDto> getLineup();
#EntityView(Venue.class)
interface VenueDto {
#IdMapping
Long getId();
CityDto getCity();
}
#EntityView(City.class)
interface CityDto {
#IdMapping
Long getId();
String getName();
}
#EntityView(TicketLink.class)
interface TicketLinkDto {
#IdMapping
Long getId();
String getName();
}
#EntityView(ArtistEvent.class)
interface ArtistEventDto {
#IdMapping
Long getId();
ArtistDto getArtist();
}
#EntityView(Artist.class)
interface ArtistDto {
#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.
EventDto a = entityViewManager.find(entityManager, EventDto.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
Optional<EventDto> findEventPageViewGraphById(long id);
Thank you Christian for your answer. I solved the problem by initializing entities with the static method Hibernate.initialize() as described here https://vladmihalcea.com/initialize-lazy-proxies-collections-jpa-hibernate/
#Transactional(readOnly = true)
public Optional<Event> loadEventPageViewGraph(long id) {
Optional<Event> eventO = eventRepository.findEventPageViewGraphById(id);
if(eventO.isPresent()) {
Hibernate.initialize(eventO.get());
Hibernate.initialize(eventO.get().getVenue().getCity());
for (ArtistEvent artistEvent: eventO.get().getLineup()) {
Hibernate.initialize(artistEvent.getArtist());
}
Hibernate.initialize(eventO.get().getTicketLinks());
return eventO;
} else {
return Optional.empty();
}
}
Though, I agree that in general it is better to use DTO's/projections. However, with DTO's there is a problem with fetching projections that include associated collections (#OneToMany properties) as described here https://vladmihalcea.com/one-to-many-dto-projection-hibernate/. In particular in the case when we don't want to select all of the entity properties. I found that Blaze-Persistence Entity Views has a nice solution for that https://persistence.blazebit.com/documentation/1.6/entity-view/manual/en_US/#subset-basic-collection-mapping. I'll check it out.

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

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

Spring data query where column is null

Suppose I have entities (getters/setters and various details omitted for brevity):
#Entity
class Customer{
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "customer")
Collection<Coupon> coupons;
}
#Entity
class Coupon{
...
#Temporal(value = TemporalType.TIMESTAMP)
private Date usedOn;
#ManyToOne(fetch = FetchType.LAZY)
#NotNull
Customer customer;
}
I wish retrieve all Coupons for a given Customer having null usedOn.
I,'ve unsuccessfully defined a method in the CouponRepository as described in docs
#Repository
public interface CouponRepository extends CrudRepository<Coupon, Long> {
Collection<Coupon> findByCustomerAndUsedOnIsNull(Customer);
}
but this leads on a compiler error Syntax error, insert "... VariableDeclaratorId" to complete FormalParameterList.
My fault, the correct definition is
#Repository
public interface CouponRepository extends CrudRepository<Coupon, Long> {
Collection<Coupon> findByCustomerAndUsedOnIsNull(Customer customer);
}
I simply missed the parameter name :-(
You can use IsNull to check null columns in JPA query.
For example for any columnA you can write query like query like
findByColumnAIsNull
In this case you can write queries like
#Repository
public interface CouponRepository extends CrudRepository<Coupon, Long> {
Collection<Coupon> findByCustomerAndUsedOnIsNull(Customer customer);
List<Coupon> findByUsedOnIsNull();
}
Also you can check how this queries will be
Refer this Spring Data JPA Query creation this will help you lot to understand and create different type of JPA query variation.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
Try changing your method to this (assuming Customer.id is a long):
Collection<Coupon> findByCustomer_IdAndUsedOnIsNull(Long customerId);
then use like this:
repo.findByCustomer_IdAndUsedOnIsNull(customer.getId());

Resources