Trying to lazy load and return a list inside a #transactional annotated method and getting a no session error - spring-boot

this works, but when I try to remove the courses.size i get no session error.
#Transactional
public List<Course> initiateCourses(Long id) {
Instructor instructor = instructorRepository.findById(2L).get();
List<Course> courses = instructor.getCourses();
courses.size();
return courses;
}
It works, but it feels like a hack.
also I found another way to load lazy collections.
#Repository
public interface InstructorRepository extends CrudRepository<Instructor, Long> {
#Query("SELECT p FROM Instructor p LEFT JOIN FETCH p.courses WHERE p.id = ?1")
Optional<Instructor> findByIdAndFetchCourseEagerly(Long id);
}
which one should I use performance wise? or is there a better way of fetching lazy initialized objects.

You can work with #EntityGraph to specify which parts of the object hierarchy should be fetched:
#Repository
public interface InstructorRepository extends CrudRepository<Instructor, Long> {
#EntityGraph(type = EntityGraphType.FETCH, attributePaths = {"courses"})
Optional<Instructor> findById(Long id);
}
See: EntityGraph (Spring Data JPA)

This proxy will not be initialized till you retrieve element from it or call size() method.
There are several ways to solve your problem without "magic":
Explicitly call Hibernate.init() and pass in it your collection-proxy before returning it from method.
Use entity graph as Peter Walser suggested

Related

Spring Data: can I create a method in the #Repository with a name what return list of object if String equals to constant?

Guess easier if I show you my example:
#Entity
class User {
Long id;
Status status;
}
enum Status {
NEW("N"), DELETED("D")
}
I have an AttributeConverter on Status so in DB the enum is stored with one character.
In my database I have entities like:
Table user
------------
Id Status
1 N
2 N
3 D
4 N
5 D
I want a method that list the Users with Status D. Something like this:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByStatusEqualsD();
or
List<User> findByStatusEqualsDeleted();
problem is these are not working
}
I could write this:
List<User> findByStatus(Status status);
And call it as repo.findByStatus(Status.DELETED) but I want a method what returns only the deleted users.
If I call it as repo.findByStatus(Status.NEW) then it will return the new users.
I prefer to not write a #Query, I hope it is possible what I'm asking without doing it...
Thanks in advance.
Such behavior is not supported.
Method name is translated into JPQL expression (which is the same as used in #Query) with parameters in it (if needed) so you have to provide these. (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation)
If you want query parameters to be hardcoded - #Query is what you need.
Alternatively you can have default method in your repository calling the parametrized one as mentioned here JpaRepository with Enum: findAllByOfferState_ACTIVE. NoSuchElementException
Easy,
You don't need a repo for that. Create a Service instead:
public interface UserDAOService{
List<User> getAllDeletedUsers();
}
And then just implement it with hardcoded findByStatus method from repo:
#Service
public class UserDAOServiceImpl implements UserDAOService{
private final UserRepository userRepository;
public UserDAOServiceImpl(UserRepository userRepository) {
this.userRepository= userRepository;
}
#Override
public List<Author> getAllDeletedUsers();
return userRepository.findByStatus(Status.DELETED);
}

Spring JPA: find by multiple IDs with Pagination

How is it possible to apply the pagination to the below query:
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
#Query("select b from Building b where b.id in :ids" )
Page<Post> findByIds(#Param("ids") List<Long> postIdsList);
...
}
All the existing examples are based on the standard findAll method that accepts a Pageable object: public Page findAll(Pageable pageable);.
The questions are:
what the controller method signature should be
what the repository method parameters should be
how and what parameters should be passed into the controller method
should I always split the post IDs for every request
will Spring make a single query and keep all the found posts in memory or it will hit a query every time for every next/previous page? If so, how can it figure out the IDs to use to find the next/previous posts?
The initial implementation was as follows:
#RestController
class PostsController {
#Autowired
private PostService postService;
#GetMapping("/posts", params = "ids")
public List<Post> getPaginatedPosts(#RequestParam List<Long> ids) {
return postService.findPaginatedPosts(ids);
}
}
#Repository
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
#Query("select b from Building b where b.id in :ids" )
Page<Post> findByIds(#Param("ids") List<Long> postIdsList);
...
}
I omitted the code from the PostServiceImpl qui implements the PostService and just calls the PostRepository#findByIds method.
Try this:
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
#Query( "select o from Building b where id in :ids" )
Page<Post> findByIds(#Param("ids") List<Long> postIdsList,Pageable pageRequest);
...
}
In controller ask for pageSize and pageNo, if it is empty set a default value like pageNo = 0, pageSize=10.
pass these values to to service layer service should create pageable object call findByIds(ids, pagable); and return the page to controller.
you can refer this:
https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-seven-pagination/
Here is the solution I came to you coupled with the above comments suggestions.
Define a repository either extending JpaRepository or PagingAndSortingRepositoryas follows:
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
#Query("select p from Post p where p.id in :ids" )
Page<Post> findByIds(#Param("ids") List<Long> postIdsList);
...
}
Create a service class and its implementation:
public interface PostService {
List<PostDTO> getPostsList(List<Long> ids, Pageable pageable);
...
}
#Service
#Slf4j
public class PostServiceImpl implements PostService {
...
#Autowired
private PostRepository postRepository;
...
#Override
public List<PostDTO> getPostsList(List<Long> ids, Pageable pageable) {
List<PostDTO> resultList = new ArrayList<>();
Page<Post> paginatedPosts = postRepository.findByIds(ids, pageable);
List<Post> posts = paginatedPosts.getContent();
posts.forEach(post -> resultList.add(convertToPostDTO(post)));
return resultList;
}
And finally, the PostsController part:
#RestController
#RequestMapping("/api")
class PostsController {
#Autowired
private PostService postService;
...
#GetMapping(value = "/posts", params = "ids")
public ResponseEntity <List<PostDTO>>getPostsList(#RequestParam List<Long> ids, Pageable pageable) {
List<PostDTO> postsList = postService.getPostsList(ids, pageable);
return new ResponseEntity<>(postsList, HttpStatus.OK);
}
The request should contain page and size URL parameters (by default, page is 0 and size is 20):
http://localhost:8080/api/posts?ids=1050,1049,1048,1043,1042,1041,1040,1039,1038&size=5&page=1&sort=id
In the above example, I had 9 records total and I put the parameters explicitly to limit the result list to 5 and display the second page only as well as to sort them by id.
If you don't provide them, the default values will be used (page = 0, size = 20).
To anyone coming here looking to pass a list of ids as a url-parameter like the question asker wants to do and the answer of belgoros explains:
Be aware of the url-max-length of 2048 characters.
So if your list of ids is long enough to require pagination, you probably also want to make the ids a body-parameter. This answer explains how to create body-parameters with spring: https://stackoverflow.com/a/22163492/7465516
I think this is important, because solutions that work on small data but unexpectedly fail on big data are the kind of thing that gets through testing and fails in production.
(I do not have the reputation to make this a comment, I hope this post is acceptable)
#Query( "select o from Building b where id in :ids", nativeQuery=true )
Page findByIds(#Param("ids") List postIdsList,Pageable pageRequest);

Unable to initialize lazy-loaded relationship inside of `#Transactional` method

I have a set of simple models like this (getters and setters omitted for brevity):
#Entity
public class Customer {
#Id
private Integer id;
}
#Entity
public class Order {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id")
private Customer customer;
}
I am trying to load an Order using a Spring JPA repository with a findById method, including the customer.
First I tried this:
#Transactional
Optional<Order> findById(Integer id) {
return repository.findById(id);
}
But when I tried to access Customer I got a LazyInitializationException: could not initialize proxy - no Session. So after referring to some other questions, I updated my method to be a bit uglier, but to explicitly call Hibernate.initialize:
#Transactional
Optional<Order> findById(Integer id) {
return repository.findById(id)
.map( order -> {
Hibernate.initialize(order.getCustomer());
return order;
);
}
But I still get org.hibernate.LazyInitializationException: could not initialize proxy - no Session. repository is a regular CrudRepository which provides the findById method out-of-the-box.
How can I initialize this lazily loaded child entity? My understanding is that the #Transactional indicates that I should still be within the transaction for the entirety of this method call. The only thing further downstream is the repository itself, which is just an interface, so I'm not sure how else to go about forcing the load of this child entity.
The Order entity and everything else in it is retrieved properly from the database; it's only when I try to get the lazy-loaded child entities that we start having issues.
The only way I managed to get this working was to write a custom hql method in the Repository using a left join fetch. While that works, it clutters up my repository with a method that is pretty much a duplicate of another and which I'm pretty sure I'm not actually supposed to need (so I would rather not do it this way.)
Spring-Boot 2.1.4.RELEASE, Spring 5.1.6.RELEASE, Hibernate 5.3.7.Final.
You have to define the method as public. See "Method visibility and #Transactional" in the spring docs.
This should work:
#Transactional
public Optional<Order> findById(Integer id) {
Optional<Order> order = repository.findById(id);
order.ifPresent(o -> Hibernate.initialize(o.getCustomer()));
return order;
}

How can I use Spring's pagination (using Pageable) while writing a dynamic query using QueryDSL?

I am trying to use pagination with QueryDSL - using the com.mysema.querydsl package.
All my Querydsl query types look like this -
#Generated("com.mysema.query.codegen.EntitySerializer")
public class QCountry extends EntityPathBase<Country> {...}
Currently, my repository implementation class looks something like this -
#Override
public Page<Country> findPaginatedCountries(String country, Optional<String> status, Pageable pageable) {
QCountry qCountry= QCountry.someObject;
QActiveCountry qActiveCountry = QActiveCountry.activeCountry;
JPAQuery jpaQuery = new JPAQuery(entityManager);
QueryBase queryBase = jpaQuery.from(qCountry).innerJoin(qActiveCountry).fetch()
.where(qCountry.codeLeft.country.upper().eq(country.toUpperCase()))
.where(qCountry.codeRight.country.upper().eq(country.toUpperCase()));
if(status.isPresent()){
queryBase = queryBase.where(qActiveCountry.id(qCountry.active.id))
.where(qActiveCountry.status.upper().eq(status.get().toUpperCase()));
}
.......}
Now, I want this dynamic query to return a paginated response. I want to use Spring's pagination to do that and not manually set offset, size etc.
I know I can use QueryDslRepositorySupport class - as implemented here - https://github.com/keke77/spring-data-jpa-sample/blob/master/spring-data-jpa/src/main/java/com/gmind7/bakery/employee/EmployeeRepositoryImpl.java
Sample code from the above link -
#Override
public Page<Employees> QFindByOfficeCode(long officeCode, Pageable pageable) {
//JPAQuery query = new JPAQuery(em);
JPQLQuery query = from(QEmployees.employees).where(QEmployees.employees.officeCode.eq(officeCode));
query = super.getQuerydsl().applyPagination(pageable, query);
SearchResults<Employees> entitys = query.listResults(QEmployees.employees);
return new PageImpl<Employees>(entitys.getResults(), pageable, entitys.getTotal());
}
However, to do that -
I need to pass JPQLQuery object to the applyPagination method. How can I do that without changing my code (Ofcourse, the repository class will extend QueryDslRepositorySupport class). Currently, I am using JPAQuery as you can see.
OR
I probably need to change my QueryDSL types by having them extend EntityPath instead of EntityPathBase so that I can use JPQLQuery.from() to generate the query and then use the applyPagination method, which requires a JPQLQuery object. However, my Q classes are extending EntityPathBase class instead. Should I be use com.querydsl package instead of com.mysemsa.querydsl package to generate query types?
OR
Other option is to use the following - http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/querydsl/QueryDslPredicateExecutor.html#findAll-com.querydsl.core.types.Predicate-org.springframework.data.domain.Pageable-
Code snippet below -
Page<T> page = QueryDslPredicateExecutor.findAll(org.springframework.data.querydsl.Predicate predicate, Pageable pageable)
However, I am making joins between two tables and then filtering results with a where clause (as you can see above in my code). How can I pass a predicate object in the findAll method above? Not sure how to include a join in it.
Please let me know if the problem is not clear, I can add more details.
EDIT: There is a many to one relationship between Country and ActiveCountry.
Country class has an ActiveCountry reference. And we have to do a join between both ids. Is is possible that Country can have null ActiveCountry. Therefore, we want an inner join - only non null values for active country
#ManyToOne
#JoinColumn(name="id")
ActiveCountry active;
Step 1: Annotate the entity class with #QueryEntity
#Entity
#QueryEntity
public class Country {}
This seems to have been addressed already since the question shows Q classes.
Step 2: Have the repository interface extend QueryDslPredicateExecutor
public interface CountryRepository
extends PagingAndSortingRepository<Country, Long>
, QueryDslPredicateExecutor<Country> {
}
Step 3: Invoke the Page<T> findAll(Predicate query, Pageable page) method provided by QueryDslPredicateExecutor
public Page<Country> getCountries(String country, Optional<String> status, Pageable page) {
QCountry root = QCountry.country;
BooleanExpression query = root.codeLeft.country.equalsIgnoreCase(country);
query = query.and(root.codeRight.country.equalsIgnoreCase(country));
if (status.isPresent()) {
query = query.and(root.active.status.equalsIgnoreCase(status));
}
return countryRepository.findAll(query, page);
}

#NamedQuery override findAll in Spring Data Rest JpaRepository

Is there a way to override the findAll query executed by Spring Data Rest?
I need a way of filtering the results based on some specific criteria and it seems that using a #NamedQuery should be along the lines of what I'm looking for so I setup a test.
#Entity
#Table(name = "users")
#NamedQueries({
#NamedQuery(name = "User.findAll", query="SELECT u FROM User u WHERE u.username = 'test'"),
#NamedQuery(name = "User.findNameEqualsTest", query="SELECT u FROM User u WHERE u.username = 'test'")
})
public class User implements Serializable, Identifiable<Long> { }
With this in place I would expect SDR to utilize my findAll() query (returning 1 result) but instead it executes the same old findAll logic (returning all results).
In my Repository I added:
#Repository
#RestResource(path = "users", rel = "users")
public interface UserJpaRepository extends JpaRepository<User, Long> {
public Page<User> findNameEqualsTest(Pageable pageable);
}
and in this case it DOES pick up the provided #NamedQuery. So...
How should I go about overriding the default findAll() logic? I need to actually construct a complex set of criteria and apply it to the result set.
In the upcoming version 1.5 (an RC is available in our milestone repositories) of Spring Data JPA you can simply redeclare the method in your repository interface and annotate it with #Query so that the execution as query method is triggered. This will then cause the named query to be looked up just as you're already used to from query methods:
interface UserJpaRepository extends PagingAndSortingRepository<User, Long> {
#Query
List<User> findAll();
Page<User> findNameEqualsTest(Pageable pageable);
}
A few notes on your repository declaration:
You don't need to annotate the interface with #Repository. That annotation doesn't have any effect at all here.
Your #RestResource annotation configures the exporter in a way that will be the default anyway in Spring Data REST 2.0 (also in RC already). Ging forward, prefer #RestRepositoryResource, but as I said: the pluralization will be the default anyway.
We generally don't recommend to extend the store specific interfaces but rather use CrudRepository or PagingAndSortingRepository.
Yes, you can create your Implementation of your Repository interface, there is acouple section in
http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/repositories.html#repositories.custom-implementations
Repository
#Repository
public interface PagLogRepository extends JpaRepository<PagLogEntity, Long>, PagLogCustomRepository {
Custom Interface
public interface PagLogCustomRepository {
PagLogEntity save(SalesForceForm salesForceForm) throws ResourceNotFoundException;
Custom implementation
public class PagLogRepositoryImpl implements PagLogCustomRepository {
#Override
public PagLogEntity save(final SalesForceForm salesForceForm) throws ResourceNotFoundException {
query = emEntityManager.createNamedQuery("findItemFileByDenormalizedSku", ItemFileEntity.class);
query.setParameter("skuValue", rawSku);
Instead of override save make it with findAll, then you can create complex customization

Resources