Spring Jpa: Lazy fetching for ManyToOne - spring

I have Record entity:
#Entity
public class Record implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private User user;
...
and corresponding RecordRepository repository:
public interface RecordRepository extends JpaRepository<Record, Integer> {
List<Record> findByUser(User user);
...
Whenever I call findByUser the resulted records contains users. But I would like to achieve that users will not be fetched from database (record.user == null).
Thanks for any advice!

If the Records are fetching User objects together via this findByUser method, try to rewrite it so it uses just the ID instead:
public interface RecordRepository extends JpaRepository<Record, Integer> {
List<Record> findByUserId(Long id);
}
This should not fetch the User for the Record until you touch it.
Now if you don't want to send the User to the output, you still have to ignore it on that side. E.g. using #JsonIgnore on the Record.getUser() method, or just don't map it in your DTO converter if you are using DTOs (which you should, if you have different representations of the same entity).

Related

Why hibernate entity graph fetch nested lazy collections

I am trying to use entity graph for triggering lazy collections to load but unfortunately entity graph also triggers all nested collections. I am using spring-data-jpa-entity-graph library for creating entity graphs at runtime.
#Entity
public class Brand implements Serializable {
#OneToMany(mappedBy = "brand", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Vehicle> vehicles;
}
#Entity
public class Vehicle implements Serializable {
#ManyToOne
#JoinColumn(name = "brand_id")
private Brand brand;
#OneToMany(mappedBy = "vehicle", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<VehiclePart> parts;
}
#Entity
public class VehiclePart implements Serializable {
#ManyToOne
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
}
Spring service with JPA repository:
public interface BrandsRepository extends EntityGraphJpaRepository<Brand, Long> {
Page<Brand> findAll(Pagable pagable, EntityGraph entityGraph);
}
#Service
public class BrandsService {
public List<Brand> find() {
return repository.findAll(PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "id")), EntityGraphUtils.fromAttributePaths("vehicles")).getContent();
}
}
In this case service also return parts collection for each vehicle but I would like to fetch only list of brands with vehicles collection for each brand.
How can we trigger to load lazy collections just on the first level (only brand's vehicles -- without vehicle's parts)?
I had the same problem. In my case: Spring and hibernate acted correctly, but I can see, that unused (lazy) fields are queried from sql.
When you use the fields, then they will be loaded over sql.
Iam using lombok and #EqualsAndHashCode.Exclude and #ToString.Exclude helps to prevent that.
In your case: Add a DTO-layer. Do not return the entities themself.
Or use #JsonIgnore annotation to ignore fields.

Spring Jpa Entity - EntityManager.getReference

I have a Spring Boot application using Spring JPA, and what I'm trying to do is to save a new entity that has some foreign keys by just providing the IDs of those child entities. So, like:
#Table(name = "PERSON")
public class Person {
#Column(name = "PET_GUID")
public Pet pet;
}
Using this, I'd like to be able to have my PersonRepository that implements CrudRepository save a Person by just providing the guid of the Pet. Using straight up hibernate I can do that using EntityManager.getReference. I know that I can inject an EntityManager into my Entity or Repository and do something that way, but is there an easier way? I tried just doing person.setPet(new Pet(myPetsGuid)), but I get a "foreign key not found" when doing that, so that does not seem to work.
First, you should add #ManyToOne relation to the pet property:
#Entity
#Table(name = "PERSON")
public class Person {
//...
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "pet_guid")
privat Pet pet;
}
It says to Hibernate to use a foreign key to the Pet entity (and its table).
Second, you should use the method getOne of your PersonRepository to get a reference to the Pet entity, for example:
#Service
public class PersonService {
private final PetRepository petRepo;
private final PersonRepository personRepo;
//...
#NonNull
#Transactional
public Person create(#NonNull final PersonDto personDto) {
Person person = new Person();
//...
UUID petId = personDto.getPetId();
Pet pet = petRepo.getOne(perId);
person.setPet(pet);
//...
return person.save(person);
}
}

Fetch List Using DTO projections using a Constructor Expression and JPQL

Perform a search on DisabScreenRequest and fetch its child details also. Using DTO projections using a Constructor Expression and JPQL.
The parent entity with a child table.
#Entity
#Table(name = "SCREEN_REQUEST")
public class DisabScreenRequest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long requestId;
#Column(name = "CIVILID")
private Long civilId;
#ManyToMany()
#JoinTable(name = "_DISAB_SCREEN_REQ_DETAILS", joinColumns = {
#JoinColumn(name = "REQUEST_ID") }, inverseJoinColumns = { #JoinColumn(name = "DISABILTY_TYPE_ID") })
private Set<DisabMaster> disabilities = new HashSet<DisabMaster>();
public DisabScreenRequest() {
}
}
This is the disability table.
#Entity
#Table(name="DISAB_MASTER")
#Immutable
public class DisabMaster implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="DIS_TYPE_ID")
private long disabilityTypeId;
#Column(name="DIS_TYPE_DESC")
private String disTypeDesc;
public DisabMaster() {
super();
}
}
Had to fetch all the requests along with the disability for each request.
Search DTO(using this I had other joins to add other than one mentioned here).
public class RequestSearchDto {
private long requestId;
private Long civilId;
private Set<DisabMaster> disabilities;
public RequestSearchDto() {
super();
}
public RequestSearchDto(long requestId, Long civilId) {
super();
this.requestId = requestId;
this.civilId = civilId;
}
public RequestSearchDto(long requestId, Long civilId, Set<DisabMaster> disabilities) {
super();
this.requestId = requestId;
this.civilId = civilId;
this.disabilities = disabilities;
}
}
This is my JPQL query
public interface ReposJPQL {
public String GET__REQUEST = "SELECT DISTINCT new org.test.RequestSearchDto "
+ "(dsr.requestId, dsr.civilId, dsr.disabilities)"
+ " FROM DisabScreenRequest dsr WHERE 1=1 ";
}
This will get an
org.hibernate.exception.SQLGrammarException: could not extract ResultSet.
What Iam I doing wrong here, how can I fetch the child table data ?
Let me know if you need any info
Stack trace :
Caused by: java.sql.SQLException: ORA-00936: missing expression
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:113)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:754)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:219)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:813)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1051)
at oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:854)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1156)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3415)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3460)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:76)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)
If you need to fetch parent entity with a collection of its nested child entities you can use this simple approach using #EntityGraph annotation or JPQL with join fetch:
#Entity
public class Parent {
//...
#OneToMany
private List<Child> children;
}
#Entity
public class Child {
//...
}
interface ParentRepo extends JpaRepository<Parent, Integer> {
// with #EntityGraph
#EntityGraph(attributePaths = "children")
#Override
List<Parent> findAll();
// or manually
#Query("select distinct p from Parent p left join fetch p.children")
List<Parent> findWithQuery();
}
Note to use distinct in your query to avoid duplicate records.
Example: duplicate-parent-entities
More info: DATAJPA-1299
AFAIK, you can't use constructor expression which take a Collection.
See the JPA 2.2 Spec, section 4.14 BNF, read about the constructor expression:
constructor_expression ::=
NEW constructor_name ( constructor_item {, constructor_item}* )
constructor_item ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable
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 defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
A mapping for your model could look as simple as the following
#EntityView(DisabScreenRequest.class)
interface RequestSearchDto extends Serializable {
#IdMapping
long getRequestId();
Long getCivilId();
Set<DisabMaster> getDisabilities();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
RequestSearchDtodto = entityViewManager.find(entityManager, RequestSearchDto.class, id);
But the Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/1.4/entity-view/manual/en_US/#spring-data-features

Directionality in JPQL joins for Spring Boot JPA?

Spring Boot here. I have the following two JPA entities:
#Entity
#Table(name = "accounts")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "account_id")
private Long id;
// lots of stuff
#OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinColumn(name = "profile_id", referencedColumnName = "profile_id")
private Profile profile; // CAN be null
// Getters, setters & ctors
}
#Entity
#Table(name = "profiles")
public class Profile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "account_id")
private Long id;
// doesn't really matter lots of stuff in here
// Getters, setters & ctors
}
It is possible for some Accounts to have Profiles, and some will not (their Profiles will be null). I would like to create a CrudRepository impl that essentially does this query:
SELECT *
FROM profiles p
INNER JOIN accounts a
WHERE a.profile_id = null
Essentially, get me all the Profiles that are "orphaned" and are associated with any accounts here.
I'm confused as to whether I need an CrudRepository<Long,Account> impl or a CrudRepository<Long,Profile> impl and on what that impl would look like. My best pseudo-attempt thus far looks like:
public interface ProfileRepository extends CrudRepository<Profile, Long> {
#Query("FROM Account act WHERE act.profile = null")
public Set<Profile> findOrphanedProfiles();
}
Can anyone help fill in the gaps for me?
First, JPQL NULL check syntax is IS NULL, not == NULL (see JPQL docs - the link is for ObjectWeb, but applies to any JPA implementation)
Second, if you want to check for orphaned records, you definitely don't want to join in the table they're orphaned from.
Your last attempt at it,
public interface ProfileRepository extends CrudRepository<Profile, Long> {
#Query("FROM Account act WHERE act.profile = null")
public Set<Profile> findOrphanedProfiles();
}
was actually pretty close, just replace == null with is null and you should be all set.
EDIT: if you're looking for profiles that don't have accounts associated with them, EXISTS query is what you're looking for:
public interface ProfileRepository extends CrudRepository<Profile, Long> {
#Query("FROM Profile p WHERE NOT EXISTS (FROM Account a WHERE a.profile = p)")
public Set<Profile> findDisassociatedProfiles();
}
EDIT: if your Profiles maintain a list of associated accounts as a property (it's not included in the code you posted, but maybe it was omitted), you can query for disassociated profiles even shorter:
FROM Profile p WHERE p.accounts IS EMPTY

Play Framework + Spring Data JPA : LazyInitializationException

These are the following classes:
#Entity
public class Question {
#Id
public Long id;
public String name;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "OWNER_ID", referencedColumnName = "QUES_ID")
public List<Choice> choices = new ArrayList<>();
}
#Named
#Singleton
public interface QuestionRepository extends CrudRepository<Question , Long> {
Question findByName(String name);
}
And in the Controller file I have this following File
#Transactional
public Result getQuestion() {
List<Choices> list = this.questionRepository.findByName("name").choices;
list.size();
return ok();
}
list.size() in getQuestion() throws me a LazyInitializationException because there is not open sessions
I know that changing the fetch type to EAGER or using a JPQL query above the function definition in QuestionRepository might solve it, but there are part in my application where those wont help and I would require to lazy fetch.
How would make the entire code in getQuestion() function use a single session/transaction or even better my entire request to take place in an single session/transaction?
From Spring Data JPA reference documentation
4.7.1. Transactional query methods
To allow your query methods to be transactional simply use #Transactional at the repository interface
you define.
Example 100. Using #Transactional at query methods
#Transactional(readOnly = true)
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByLastname(String lastname);
#Modifying
#Transactional
#Query("delete from User u where u.active = false")
void deleteInactiveUsers();
}
Typically you will want the readOnly flag set to true as most of the query methods will only read data. In contrast to that deleteInactiveUsers() makes use of the #Modifying annotation and overrides the transaction configuration. Thus the method will be executed with readOnly flag set to false.
So just add #Transactional annotation to your repository interfaces.

Resources