Equivalent of IQueryable in Spring Data - java

I'm used to .Net and LINQtoEntities, especially the IQueryable part which allows to carry a request through different functions before fetching the results.
Does anything like this exist in spring data ? Or any other java ORM ?
Basic example of what I'd like to be able to do :
private IQueryable<Todo> GetAll(){
context.Todos.Where(t => !t.Deleted);
}
public IEnumerable<Todo> GetDoneTodos(){
GetAll().Where(t => t.Done).ToList();
}

You can use Spring Data's QueryDSL integration. Basically, you extend the QueryDslPredicateExecutor in your repository interface and it add a findAll method that gets a QueryDSL Predicate and filter all the results based on that Predicate. Suppose we have domain object, say Greeting, then we'd have repository like this:
public interface GreetingRepository extends QueryDslPredicateExecutor<Greeting> {}
Then you can use generated QModels generated by QueryDSL to create a Predicate and pass it to our greetingRepository. Suppose we're going to filter all the Greetings by one specific user:
Predicate filterByUser = greeting.user.eq(someUser);
greetingRepository.findAll(filterByUser);
greeting is a metamodel generated by QueryDSL based on our Greeting model.
Note 1: You can see how you can integrate Spring Data and QueryDSL here and see more examples for QueryDSL's Predicates here.
Note 2: QueryDslPredicateExecutor also provides findOne(Predicate predicate), count(Predicate predicate) and exists(Predicate predicate) methods which are useful and self-explanatory, of course.
Note 3: You can achieve almost the same thing with Specifications but in my opnion QueryDSL has more elegant and readable Predicates.

Related

filter Data in Spring Boot Data REST

I use Spring Boot Data REST, yes, I write something like below:
#RepositoryRestResource
public interface ExerciseRepository extends JpaRepository<Exercise, Integer> {}
then I open 127.0.0.1/exercises. It will show all exercises.
But I want only show some appointed exercises(eg. exercise id < 100, or other complicated logic) on the 127.0.0.1/exercises.
I know I can use #RestController, but how can I do this with Spring Boot Data REST?
#RepositoryRestResource(path="exercises",collectionResourceRel = "exercises")
can you edit this according to your own code ? I think this will work for you
You can declare an interface method, for example:
#RepositoryRestResource
public interface ExerciseRepository extends JpaRepository<Exercise, Integer> {
List<Exercise> findByIdLessThan(#Param("id") Integer id);
}
In this case, the query is derived from the method name directly, but you can also write a query manually using #Query, for more details check the documentation.
To invoke the method use the following API request:
GET http://localhost:8080/exercises/search/findByIdLessThan?id=100
For reference, Spring Data REST - Useful notes.
EDIT:
If you use Hibernate as your persistence provider, you can use #Where for static filtering, and #Filter for dynamic filtering where filters are defined and configured at runtime, according to Hibernate User Guide.
For example, you can annotate the entity with #Where and define a condition that will be applied to all queries related to that entity:
#Where(clause = "id<100")
#Entity
public class Exercise{
//...
}

JpaSpecificationExecutor, how to change the field in SELECT

dears.
Currently, I am working with JpaSpecificationExecutor API, cause I need to create dynamic queries based on a lot of optional search criteria, for this I am starting with a root entity, which is included in the generic type in the JpaSpecificationExecutor interface.
The code to make the build in the Specification (which is included in the findAll method that belongs to repository interface) is the following:
public Specification<T> build(){
return (root, query, criteriaBuilder) -> {
Predicate filters = null;
//createPredicate is a method to create predicates (it does not matter in this code)
filters = createPredicate(root, criteriaBuilder, searchCriterias);
return filters;
};
}
Now each is working fine, but there is a little detail, in the query I am using joins to make relation with other entities, but when I get the response, like in this code:
List<EXAMPLE> examples = repository.findAll(filters.build());
I am not able to see the filter data in other entities, just I can see the field in the root entity (It is EXAMPLE entity) because these ones are appearing in the query formed. so, I would like to include in the SELECT the other entities, in order to see the filter data by means of the JOINs, but until now I have not been able to do it.
I have seen in any inputs, that I can use these codes in my build code, I have tried, but the new files are not appearing in the generated query:
query.select, or query.multiselect
Ref:
Ref. 1
Also, I have found that this is not working with JpaSpecificationExecutor, because the list of attributes in the SELECT is assigned by the entity Ref. 2
My question is, does someone know if exists another way to change the field in the SELECT with JpaSpecificationExecutor.
Regards.

Spring Repository without #Query

I am working on a desktop application built using spring framework and one of the part of the application is not working. I found that the repository class does not have any queries with #Query annotation. I haven't encountered it before.
When I try to open the form that uses this, I get an error that the application is not able to connect to the database. The application has 3 databases specified in the application.properties. I have the following questions:
1) How does the following code work without a query specified with #Query annotation. Or where is the query written.
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
List<Account> findAccountsByActiveIsTrueAndAccountTypeEquals(String accountType);
List<Account> findAccountsByAccountTypeLike(String type);
}
2) How do we specify which of the database to search for. For example: I have 3 mysql databases currently connected to my application. I wish to access data from DB1 through my Spring boot application through the usual flow of
UI model-> BE Controller/ Service layer -> Repository(Interface) which (usually) has the query written with #Query. How we specify which database this query goes for ?
For your first question I can answer that the JpaRepository has an internal system that analyses the method name you have written and then generates the query that has to be executed to the database.
The #Query annotation is used when the method name and the generated query is not returning the result you wanted to so you specifically tell the compiler which query should be executed.
As mentioned here: https://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/jpa.repositories.html
2.3.1 Query lookup strategies.
The JPA module supports defining a query manually as String or have it being derived from the method name.
Declared queries
Although getting a query derived from the method name is quite convenient, one might face the situation in which either the method name parser does not support the keyword one wants to use or the method name would get unnecessarily ugly. So you can either use JPA named queries through a naming convention (see Section 2.3.3, “Using JPA NamedQueries” for more information) or rather annotate your query method with #Query (see Section 2.3.4, “Using #Query” for details).
So basically using a naming convention will do the magic.
Also an interesting question and perfect answer can be found here:
How are Spring Data repositories actually implemented?
For your second question you can refer to this example:
https://www.baeldung.com/spring-data-jpa-multiple-databases
It might be a bit complicated in the beginning but eventually it will work.
He use JPA, JpaRepository has CRUD methodes
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference
In your application.properties, you can put your mysql DB info
Why this works without #Query?
Because you are using JpaRepository which provides an easy way to get data based on your entity and it's fields.
Here your Account will have active, accountType etc fields. You can use JPA's query creation keywords such as AND, OR, Equals, Like and many more.
Derived queries with the predicates IsStartingWith, StartingWith, StartsWith, IsEndingWith", EndingWith, EndsWith, IsNotContaining, NotContaining, NotContains, IsContaining, Containing, Contains the respective arguments for these queries will get sanitized. This means if the arguments actually contain characters recognized by LIKE as wildcards these will get escaped so they match only as literals. The escape character used can be configured by setting the escapeCharacter of the #EnableJpaRepositories annotation.
How do we specify which of the database to search?
You can create configuration classes based on your databases and define data sources based on that using #PropertySource.
For more details see example here
#Configuration
#PropertySource({ "classpath:persistence-multiple-db.properties" })
#EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)

How to make dynamic queries at run-time in Spring Boot and Data?

I am new to Java and started with Spring Boot and Spring Data JPA, so I know 2 ways on how to fetch data:
by Repository layer, with Literal method naming: FindOneByCity(String city);
by custom repo, with #Query annotation: #Query('select * from table where city like ?');
Both ways are statical designed.
How should I do to get data of a query that I have to build at run time?
What I am trying to achieve is the possibility to create dynamic reports without touching the code. A table would have records of reports with names and SQl queries with default parameters like begin_date, end_date etc, but with a variety of bodies. Example:
"Sales report by payment method" | select * from sales where met_pay = %pay_method% and date is between %begin_date% and %end_date%;
The Criteria API is mainly designed for that.
It provides an alternative way to define JPA queries.
With it you could build dynamic queries according to data provided at runtime.
To use it, you will need to create a custom repository implementation ant not only an interface.
You will indeed need to inject an EntityManager to create needed objects to create and execute the CriteriaQuery.
You will of course have to write boiler plate code to build the query and execute it.
This section explains how to create a custom repository with Spring Boot.
About your edit :
What I am trying to achieve is the possibility to create dynamic
reports without touching the code. A table would have records of
reports with names and SQl queries with default parameters like
begin_date, end_date etc, but with a variety of bodies.
If the queries are written at the hand in a plain text file, Criteria will not be the best choice as JPQL/SQL query and Criteria query are really not written in the same way.
In the Java code, mapping the JPQL/SQL queries defined in a plain text file to a Map<String, String> structure would be more adapted.
But I have some doubts on the feasibility of what you want to do.
Queries may have specific parameters, for some cases, you would not other choice than modifying the code. Specificities in parameters will do query maintainability very hard and error prone. Personally, I would implement the need by allowing the client to select for each field if a condition should be applied.
Then from the implementation side, I would use this user information to build my CriteriaQuery.
And there Criteria will do an excellent job : less code duplication, more adaptability for the query building and in addition more type-checks at compile type.
Spring-data repositories use EntityManager beneath. Repository classes are just another layer for the user not to worry about the details. But if a user wants to get his hands dirty, then of course spring wouldn't mind.
That is when you can use EntityManager directly.
Let us assume you have a Repository Class like AbcRepository
interface AbcRepository extends JpaRepository<Abc, String> {
}
You can create a custom repository like
interface CustomizedAbcRepository {
void someCustomMethod(User user);
}
The implementation class looks like
class CustomizedAbcRepositoryImpl implements CustomizedAbcRepository {
#Autowired
EntityManager entityManager;
public void someCustomMethod(User user) {
// You can build your custom query using Criteria or Criteria Builder
// and then use that in entityManager methods
}
}
Just a word of caution, the naming of the Customized interface and Customized implementating class is very important
In last versions of Spring Data was added ability to use JPA Criteria API. For more information see blog post https://jverhoelen.github.io/spring-data-queries-jpa-criteria-api/ .

Inject parameter into spring-data dynamic query-build methods

I want to know if it's possible to inject some functionality into the dynamically generated CrudRepository query methods to provide an additional filterBy criteria for all methods in a repository.
The short version, I want
Page<Collection> findByName(#Param("name") String name);
to work as if it were named
Page<Collection> findByNameAndGroup(#Param("name") String name);
where I add the AndGroup criteria into the generated code for the repo's methods at impl creation time.
I don't want to have to add this everywhere, as it will be on all methods, and the parameter is known already.
My initial thought was to extend the repo class, and add some new methods that called the generated ones with the added parameter, but then I realized that the repo is defined only as an interface, so one cannot extend it, and adding any impl would impact the spring-data impl generation. At least, I think that's how it works..
Detail:
I have a MongoRepository that has a lot of API methods generated using the spring-data query-builder syntax. (http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.query-methods.query-creation)
Main repo class:
#RepositoryRestResource
public interface CollectionRepository extends MongoRepository<Collection, String> {
Page<Collection> findByName(#Param("name") String name);
Page<Collection> findByNameRegex(#Param("name") String name);
// lots more findBy methods here...
}
The repo objects, collections, are just an object with a name and a group Id:
#Document
public class Collection {
private String name;
private String group;
}
My authenticated user objects are:
#Document
public class Collection {
private String username;
private String password;
private String group;
}
The user's group needs to filter what collections a user can see, in all cases.
Every one of the above findBy...() needs to become a findBy...AndGroup(), which is possible because the group is an element in the collection.
The easy way is to simply add the new parameter to every one of the methods, ie:
#RepositoryRestResource
public interface CollectionRepository extends MongoRepository<Collection, String> {
Page<Collection> findByNameAndGroup(#Param("name") String name);
Page<Collection> findByNameRegexAndGroup(#Param("name") String name);
// lots more methods here...
}
This also makes the client needlessly pass in a parameter that is already known from the user's context.
If I wasn't using spring-data and I had to actually write code for the controllers I'd call a method from all API handlers that read the user's group:
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String group = user.getGroup();
and added it to calling method's filterBy criteria.
Is it possible to wire this functionality into the spring-data infrastructure such that the generated find...() methods for this interface add the "...ByGoup" criteria to all methods, and pass in a method to use to extract this group parameter?
To say it another way, can I alter the repository instance to affect all the generated methods to extract the user's group using a provided method, add this ByGroup filter to the initial criteria that spring generates based on the method name?
Could one use aspectJ to wire in a a pre/post method handler to do this?
Currently there's no direct support for that. Spring Data JPA already has support to use SpEL expressions in manual query definitions so that e.g. Spring Security specific functions can be used to in JPQL queries. This is shown in this example project but requires manual definition of the queries.
We're currently working on a mechanism to provide means to augment all queries executed by Spring Data. Be sure to follow DATACMNS-293 for the fundamental infrastructure as well as DATAJPA-307 for an example implementation of soft-deletes on top of that.
For DATACMNS-293 we already provide feature branch builds so that you could try to build your own QueryAugmentor. AnnotationBasedQueryAugmentor is probably type you want to look into.

Categories