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
Related
In a Spring Boot application using a #Repository with derived queries is somewhat easy.
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
If, however, the queries are getting more complex (joins, subqueries, etc.) then we're using Querydsl like in the following pseudo-code example.
JPAQuery<User> query = new JPAQuery<>(entityManager);
List<User> deletableUsers = query
.select(user)
.from(user)
.join(someTable)
.on(user.id.eq(someTable.user.id))
.where(user.notIn(createSubquery(compareDate))
.and(condition1)
.and(condition2)
.and(condition3)
.distinct()
.fetch();
private JPQLQuery<User> createSubquery(Date compareDate) {
return JPAExpressions
.select(user)
.from(user)
.join(someOtherTable)
.on(user.id.eq(someOtherTable.user.id))
.where((condition4
.and(condition5)
.and(condition6)))
.distinct();
}
Currently, we're using this query from within a #Service class but I wonder if it might make sense to move that to the Spring repository, so that all "query related" methods are located in the repository.
Is such an approach feasible, i.e. do you see any impediments like injecting the EntityManager into the repository?
Would you rather recommend to use the #Query annotation despite the query string getting long and complex?
I've just started learning Spring Boot and am using a H2 database, I've got mostly everything working but I'm running into trouble trying to make a slightly more complex request. I've got 2 tables 'User' and 'Purchase', and I want to create and end point that returns all purchases that contain a given users ID. This seems simple if I used an SQL join or some similar query but I have no idea how to implement one.
I have a repository (CrudRepository) for both user and purchases, and then a service for each that gets the relevant data from database. This works perfect for the basic needs such as get, getById, etc. But I have no idea how to specify queries such as join and what not.
public interface UserRepo extends CrudRepository<User, Integer> {}
public interface ReceiptRepo extends CrudRepository<Receipt, Integer> {}
#Service
public class UserService {
#Autowired
UserRepo userRepo;
public User getUser(int id) { return userRepo.findById(id).get(); }
}
#RestController
public class UserController {
#Autowired
UserService userService;
#GetMapping("/user/{id}")
private User getUser(#PathVariable("id") int id) {
return userService.getUser(id);
}
}
That's basically the set up for both entities, and I'm not sure where and how I'd write more specific queries. Any help would be greatly appreciated.
Yoy can use #Query() annotation in order to write query.
You need to declare a method in your repo and on that method you can put this annotation.
Eg:
#Query("SELECT u FROM User u WHERE u.status = 1")
Collection<User> findAllActiveUsers();
You can take some more idea about this from here
In my legacy application, I have a country table, state table and a mapping table for country and state with few additional columns.
I have created an entity class like this.
class CountryStateMapping {
#Id
private long id;
private Long countryId;
#OneToOne
#JoinColumn(name="state_id")
private State state;
//getters seters
}
My repository.
public interface CountryStateMapping extends JpaRepository<CountryStateMapping, Long>{
Optional<CountryStateMapping> findByStateId(long stateId);
Optional<CountryStateMapping> findByState(State state);
}
I would like to check if the state exists in the mapping table. Both of the below approaches do not work.
countryStateMapping.findByStateId(long stateId)
countryStateMapping.findByState(State state)
What is the right way?
Its not the correct way i feel.The correct way for doing this will be
public interface CountryStateMappingRepository extends JpaRepository<CountryStateMapping, Long> {
Optional<CountryStateMapping> findByStateId(long stateId);
#Query("select s.something from State s" )
Optional<CountryStateMapping> findByState(State state);
}
This implies two things
By extending JpaRepository we get a bunch of generic CRUD methods to create, update, delete, and find
2.It allows Spring to scan the classpath for this interface and create a Spring bean for it.
Also you need some configuration.For that you need to create a configuration class to be used with your data source.You can find many examples to do the same and one such is https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa.
You can also use custom queries and simple queries using the #Query annotation.
Thanks
Try with an underscore for id like below;
public interface CountryStateMapping<CountryStateMapping, Long>{
Optional<CountryStateMapping> findByState_Id(long stateId);
Optional<CountryStateMapping> findByState(State state);
}
I am able to get the property value in Spring classes like below:
#Value("${database.name}")
private String databaseName;
I have to execute a native query by joining different tables which are in different databases.
#Query(value="select t1.* FROM db1.table1 t1 INNER JOIN db2.table2 t2 ON t2.t1_id1 = t1.id1")
Instead of hard coding database names i.e., db1 and db2 here, I have to get them from properties file.
how to get the property value inside the #Query annotation in Spring Data JPA Repository ?
I don't know if it is possible, but if not, you can consider this approach:
Instead of using properties in Repository's #Query directly, you can use params in the query but when you call the actual method - you can provide values from .properties.
Imagine you have simple repository:
public interface UserRepository extends JpaRepository<User, Long> {
// query with param
#Query("select u from User u where u.lastname = :lastname")
User findByLastname(#Param("lastname") String lastname);
}
Then, let's say you have some Service or Controller where you need to use your Repository - you can inject properties there and pass them to your method:
#Service
public class UserService {
// this comes from .properties
#Value("${user.lastName}")
private String userLastName;
#Autowired
private UserRepository userRepository;
public User getUser() {
// you pass it as param to the repo method which
// injects it into query
return userRepository.findByLastname(userLastName);
}
}
This is just an example. But I believe it may be useful.
Happy hacking :)
I have service:
#Service
public class MessageServiceImpl implements MessageService {
private final MessageRepository smevMessageRepository;
private final Environment environment;
public MessageServiceImpl(MessageRepository messageRepository, Environment environment) {
this.messageRepository= messageRepository;
this.environment = environment;
}
#Override
public List<Message> findReadyToSend() {
if (environment.acceptsProfiles("postgre")) {
return messageRepository.findReadyToSendPostgre();
}
return messageRepository.findReadyToSendOracle();
}
And It is my repository:
#Repository
public interface MessageRepository extends JpaRepository<Message, String> {
#Query(value = "select sm.* from MESSAGES sm ...", nativeQuery = true)
List<Message> findReadyToSendOracle();
#Query(value = "select sm.* from MESSAGES sm ...", nativeQuery = true)
List<Message> findReadyToSendPostgre();
If I start spring boot server with oracle profile I call findReadyToSendOracle method and if postgre profile - findReadyToSendPostgre method. It work. But this solution is bad. I think. Because I write hardcode for profile check. and my repository has 2 methods for different DB.
How to implement this correctly?
What are the problems you are facing while adapting to JPQL? Using native/custom functions? It might look way too difficult, but you might find a way using criteria + the function function from JPA 2.1+, take a look at this article.
On the other hand, I found an old workaround of mine here that might help. There is a simple way to solve that using a few shortcuts with the #Profile annotation and some extra interfaces.
If you provide an interface with your expected native query method that extends JpaRepository, like this:
#NoRepositoryBean
public interface MessageRepository extends JpaRepository<Message, String>{
List<Message> findByReady();
}
Note the #NoRepositoryBean, avoiding duplicate beans with profile specialization.
Then, just provide your implementations according to your needs:
#Repository
#Profile("oracle")
public interface MessageOracleRepository extends MessageRepository {
#Query(value = "select m.* from Message m where m.ready = false", nativeQuery = true)
List<Message> findByReady();
}
... and ...
#Repository
#Profile("mysql")
public interface MessageMySQLRepository extends MessageRepository {
#Query(value = "select m.* from Message m where m.ready = true", nativeQuery = true)
List<Message> findByReady();
}
Now you will only need to provide the desired profile, inject and use the correct native queries.
As you can see I simplified the queries, for the sake of simplicity. Take a look at this repository with the adapted code.