Is possible to return a projection when query-by-example? - spring-data-jpa

I need to write a query with many optional parameters. I don't want to write my query with many 'is-null' checking.Is that possible to return a projection when we use query-by-example in spring data jpa?
User user = new User();
user.setName("John");
Example<User> ex = Example.of(name);
repository interface
public interface EnglishNameRepos extends JpaRepository<User,Long>{
//how to custom method?
Page<UserProjection> findXXX(Example<User> ex, Pageable pageable);
}

Related

JPA - How to add same query to a repository twice

I am trying to use the same query more than once, with different options set by annotations. Similar to:
#EntityGraph(attributePaths = {"books"})
Optional<User> findById(long id);
#EntityGraph(attributePaths = {"courses"})
Optional<User> findById(long id);
Optional<A_Projection> findById(long id);
Is there a way to add a prefix or a postfix to the method name, such that I can have different function signatures but it would be interpreted as same JPA query to avoid using #Query? Such as:
#EntityGraph(attributePaths = {"books"})
Optional<User> findByIdQ1(long id); //Q1,Q2,Q3 prefixes help distinguish these methods
#EntityGraph(attributePaths = {"courses"})
Optional<User> findByIdQ2(long id); //but they mess up the JPA syntax
Optional<A_Projection> findByIdQ3(long id);
As an example, for Projection case, this is a valid syntax:
Optional<A_Projection> findByIdByProjection(long id);
You can use EntityGraphJpaSpecificationExecutor to pass different entitygraph based on your method.
#Repository
public interface UserRepository extends JpaSpecificationExecutor<User>, JpaRepository<User, Long>, EntityGraphJpaSpecificationExecutor<User> {
}
In your service class, you can call findOne with entity graph.
List<User> users = userRepository.findOne(specification, new NamedEntityGraph(EntityGraphType.FETCH, "graphName"))
I found what I was looking for. Apparently, JPA searches for find....By in derived queries. Therefore, you can add whatever between these clauses:
Optional<User> findWithBooksById(long id); //Q1,Q2,Q3 prefixes help distinguish these methods
#EntityGraph(attributePaths = {"courses"})
Optional<User> findWithWhateverById(long id); //but they mess up the JPA syntax
Optional<A_Projection> findById(long id);
From Spring documentation:
Any text between find (or other introducing keywords) and By is considered to be descriptive unless using one of the result-limiting keywords such as a Distinct to set a distinct flag on the query to be created or Top/First to limit query results.

How to find top N elements in Spring Data Jpa?

In Spring Data Jpa to get first 10 rows I can do this findTop10By...(). In my case the number or rows is not defined and comes as a parameter.
Is there something like findTopNBy...(int countOfRowsToGet)?
Here is another way without native query. I added Pageable as a parameter to the method in the interface.
findAllBySomeField(..., Pageable pageable)
I call it like this:
findAllBySomeField(..., PageRequest.of(0, limit)) // get first N rows
findAllBySomeField(..., Pageable.unpaged()) // get all rows
I don't know of a way to do exactly what you want, but if you are open to using #Query in your JPA repository class, then a prepared statement is one alternative:
#Query("SELECT * FROM Entity e ORDER BY e.id LIMIT :limit", nativeQuery=true)
Entity getEntitiesByLimit(#Param("limit") int limit);
Did it by using pagination, as described in the first answer. Just adding a more explicit example.
This example will give you the first 50 records ordered by id.
Repository:
#Repository
public interface MyRepository extends JpaRepository<MyEntity, String> {
Page<MyEntity> findAll(Pageable pageable);
}
Service:
#Service
public class MyDataService {
#Autowired
MyRepository myRepository;
private static final int LIMIT = 50;
public Optional<List<MyEntity>> getAllLimited() {
Page<MyEntity> page = myRepository.findAll(PageRequest.of(0, LIMIT, Sort.by(Sort.Order.asc("id"))));
return Optional.of(page.getContent());
}
}
Found the original idea here:
https://itqna.net/questions/16074/spring-data-jpa-does-not-recognize-sql-limit-command
(which will also link to another SO question btw)

Spring Data Rest with Spring Security - find all by current user

Is it possible to use Spring Data Rest and Spring Security to return current user related entities, using the findAll() method without specifying this user in the GET query parameter?
My only solution is to pass user as a parameter, but maybe it's another option to get him from SpringSecurityContext
public interface InvoiceRepository extends CrudRepository<Invoice, Long> {
#RestResource
#PreAuthorize("hasRole('ROLE_ADMIN') or user?.username == authentication.name")
List<Invoice> findAllByUser(#Param("user") User user);
You can use SpEL EvaluationContext extension that makes security properties and expressions available in SpEL expressions in the #Query annotations. This allows you to get only those business objects that relate to the current user:
interface SecureBusinessObjectRepository extends Repository<BusinessObject, Long> {
#Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
List<BusinessObject> findBusinessObjectsForCurrentUser();
}
More details are here.

How to shorten names of query methods in Spring Data JPA Repositories?

Consider a Spring Data Jpa Repository:
public interface UserRepository extends JpaRepository<User, Long> {
User findOneByDeletedIsFalseAndActivationKey(String activationKey);
List<User> findAllByDeletedIsFalseAndActivatedIsFalseAndCreatedDateBefore(DateTime dateTime);
User findOneByDeletedIsFalseAndLogin(String login);
User findOneByDeletedIsFalseAndEmail(String email);
}
Notice each method has "DeletedIsFalse" in it. Is there a simple way to make method names shorter? Like i.e.:
#FullMethodName("findOneByDeletedIsFalseAndEmail")
User findOneByEmail(String email);
Use default Java 8 feature for wrapping, just like so:
interface UserInterface extends JpaRepository<User, Long> {
// use findOneByEmail instead
User findOneByDeletedIsFalseAndEmail(String email);
default User findOneByEmail(String email) {
return findOneByDeletedIsFalseAndEmail(email);
}
}
See an example.
With Kotlin, you can use extension functions, for example:
interface UserRepository : JpaRepository<User, Long> {
// use findOneByEmail instead
fun findOneByDeletedIsFalseAndEmail(email: String): User
}
fun UserRepository.findOneByEmail(email: String) =
findOneByDeletedIsFalseAndEmail(email)
Now you can use Java 8 default interface methods as #Maksim Kostromin described. But there is no such a feature in Spring.
-- Old answer
There is no such a way. You can specify any name for a method and add an annotation #Query with parameter value which holds desired query to database like this:
#Query(value="select u from User u where u.deleted=false and u.email=:email")
User findOneByEmail(#Param("email")String email);
or, with native sql query:
#Query(value="SELECT * FROM users WHERE deleted=false AND email=?1", nativeQuery=true)
User findOneByEmail(String email);
You can also use names that follow the naming convention for queries since #Query annotation will take precedence over query from method name.
#Query docs
Upd:
from Spring docs:
Although getting a query derived from the method name is quite convenient, one might face the situation in which ... the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention ... or rather annotate your query method with #Query.

Spring data mongo pagination

I want to implement pagination with Spring Data Mongo. There are many tutorials and docs suggest to use PagingAndSortingRepository, like this:
StoryRepo extends PagingAndSortingRepository<Story, String>{}
And so because PagingAndSortingRepository provides api for query with paging, I can use it like:
Page<Story> story = storyRepo.findAll(pageable);
My question is where actually is this findAll method here implemented? Do I need to write its implementation by myself?
The StoryRepoImpl which implements StoryRepo needs to implement this method?
You do not need to implement the method as when you autowired the Spring object PagingAndSortingRepository, it automatically implements the method for you.
Please note that since you are using Mongodb, you can extend MongoRepository instead.
Then in Spring, enable pagination using this:
#RequestMapping(value="INSERT YOUR LINK", method=RequestMethod.GET)
public List<Profile> getAll(int page) {
Pageable pageable = new PageRequest(page, 5); //get 5 profiles on a page
Page<Profile> page = repo.findAll(pageable);
return Lists.newArrayList(page);
I got it working by writing my own implementations, something like this:
List<Story> stories = null;
Query query = new Query();
query.with(pageable);
stories = getTemplate().find(query, Story.class);
long total = getTemplate().count(query, Story.class);
Page<Story> storyPage = new PageImpl<Story>(stories, pageable, total);
return storyPage;
I'm working with spring data & mongodb, using mongo template to query data.
In Spring Data, you create an interface and add a method using the naming conventions used by Spring data and the framework will generate the implementation of that method.
To implement pagination, i create this method declaration in my repository:
public interface PersonRepository extends MongoRepository<Person, ObjectId> {
Page<Person> findByName(String name, Pageable pageable);
}
Then, in my service i call this method like this:
Page<Person> persons = personRepository.findByName("Alex", PageRequest.of(0, 100));
Here, the page will contain 100 element.
To paginate a query, you can use something like below:
public interface PersonRepository extends MongoRepository<Person, String> {
Page<Person> findByFirstname(String firstname, Pageable pageable);
}
For more details, please refer to the second query in Example 6.6 in https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html
The method is implemented by a store-specific class. For the Spring Data JPA module, it's SimpleJpaRepository. You usually let a DI container create instances for these repository interfaces. With Spring you'd activate Spring Data repositories by either using #EnableJpaRepository on a JavaConfig class or
<jpa:repositories base-package="com.acme.repositories" />
This will create a proxy instance for the repo, so that you can get it injected into your clients:
class MyClient {
#Inject
public MyClient(PersonRepository repository) {
…
}
}
Query query1 = new Query();
Integer startIndex = page * size;
Integer endIndex = (page * size) + size;
List<dto> totalRecord = mongoOperation.find(query1, dto.class);
query1.limit((endIndex > totalRecord.size() ? totalRecord.size() : endIndex));
List<dto> responseList = mongoOperation.find(query1, dto.class);
int end = (endIndex > (totalRecord.size() - 1) ? totalRecord.size() - 1 : endIndex);
if (totalRecord.size() > 0 && end == 0)
end = 1;
if (totalRecord.size() > 0)
responseList = responseList.subList(startIndex, end);
int totalPages = totalRecord.size() / size + (totalRecord.size() % size == 0 ? 0 : 1);
As other answer says: you don't need to implement the repository interface classes. The generating magic will handle it.
I wrote this answer because of say implementation way of PageRequest deprecated
-the Pageable pageable = new PageRequest(fromIndex, toIndex); one.
If you want to implement a pageable request nowadays, you need to use like following:
PageRequest page = PageRequest.of(pageNum, pageSize);
List<MyEntity> result =myEntityRepository.findAll(page).toList();