Spring Data MongoDB add common criteria - spring

I'm using Spring Data MongoDB repository abstraction to access mongoDB database:
public interface CustomerRepository extends MongoRepository<Customer, String> {}
...
#Autowired
private CustomerRepository customerRepository;
List<Customer> customers = customerRepository.findAll();
this will fetch all customers from mongoDB collection.
I'd like to add criteria for all CustomerRepository methods to filter customers by{"active": true} (the actual filter is more complicated if it's matter) transparently (without changing interface).
I'm thinking about overriding some base Spring Data MongoDB classes but I'm not sure how to do it.
Thanks.

Related

What is the difference between #Entity and #Document in spring boot?

Can you use both annotations on your database tables?
id, just like some clarification on there differences. thank you
#Entity is used to map a class to a relational database, it represents a database table.
#Document is used to map a class to noSQL database (specifically mongoDB), it represents a MongoDB documents.
You can use both JPA or MongoRepository if you are using both databases by creating different entities and repositories for each database.
I recommend you to have a look at spring documentation (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/)
#Document is a Spring Data Mongo annotation while #Entity is part of Java Persistence API (JPA).
You can check both documentations:
Spring Data Mongo docs
JPA docs
Where into "Example 10. Repository definitions using domain classes with annotations" there is this piece of code:
interface PersonRepository extends Repository<Person, Long> { … }
#Entity
class Person { … }
interface UserRepository extends Repository<User, Long> { … }
#Document
class User { … }
And documentation says:
PersonRepository references Person, which is annotated with the JPA #Entity annotation, so this repository clearly belongs to Spring Data JPA. UserRepository references User, which is annotated with Spring Data MongoDB’s #Document annotation.
So you can see here the difference.

#Cacheable for default spring data jpa methods without overriding them

Sry, if my question isn't new but i couldn't find answer.I use Spring Data JPA and Spring Cache.
I have folowing repository
#CacheConfig(cacheNames = "Category")
#Cacheable
#Repository
public interface Repository extends CrudRepository<Category, Long> {
Category findByCategory(String Category);
}
And i want to cache default CrudRepository methods, like findAll() and etc.
It's work If i override them like this
#CacheConfig(cacheNames = "Category")
#Cacheable
#Repository
public interface Repository extends CrudRepository<Category, Long> {
Category findByCategory(String Category);
List<Category> findAll();
}
But it's not convenient override them every time for every repository.
Is there a way cache defaults spring jpa methods without override them or no such way?
Yes, we can do it. Basically, Spring uses Hibernate ORM as the implementation of JPA. Hibernate itself supports caching functionality with it and will integrate better than Spring Cache.
To enable L2 cache, add these properties to your project add the following properties.
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
and dependencies hibernate-ehcache
Once this is done, all your default methods of JPA like findOne(), findAll() will be cached.
If you add any custom methods, you can add like below:
#QueryHints({ #QueryHint(name = "org.hibernate.cacheable", value ="true") }) Category findByCategory(String Category);
To test where default methods are cached, you can use the following properties to see if SQL has been executed.
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Writing generic spring jpa repository for all entities

I am working on Spring Boot application.
We have Service Layer,Rest Controller and Dao as repository.
I have 20 to 30 tables in my database and I dont want to create repository for each entity and extends that to CrudRepository.
ex : User is an Entity, to perform persistance operations on User, I have to create UserRepository which extends CrudRepository.
Same with Department, Company etc...
What i want to do is, I will write a BaseRepository which gonna extend CrudRepository, base repository should accept all entities and do persistance operations.
Is there a way to that ??
Don't extend CrudRepository it's functionality is all tied to the generic type, it'd be hacky to extend it for a generic implementation. You probably just want something simple which uses the JPA entity manager directly:
import javax.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
public class GenericRepository {
#Autowired
private EntityManager entityManager;
public <T, ID> T findById(Class<T> type, ID id) {
return entityManager.find(type, id);
}
}

How to stream resultsets in spring Data JPA with custom criteria

I'm working on a POC, using Spring Boot 2.0.5, Spring Data JPA using Hibernate. I'm trying to implement a way to stream the result sets for a custom criteria. I have seen examples like
public interface MyRepository implements JPARepository<Person,Long>{
#Query("select p from person p")
Stream<Person> findAll();
}
However I'm extending SimpleJPARepository and want to get results as a stream using a Criteria something like
Stream<Person> findAll(Criteria criteria);
Since I'm using class that extends SimpleJPARepository, I need to provide my implementation. But are there any methods in SimpleJPARepository or its parent classes, that can provide me default implementation using the criteria I provide. Any reference to such example is much helpful.
Also, in some examples I see that #NoRepositoryBean is used and in some cases #Repository. I'm confused between these two and which one should I use and why?
As per Spring Data JPA specifications Spring Data JPA, this is how you can create Criteria queries.
Step 1: extend your repository interface with the JpaSpecificationExecutor interface, as follows:
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor {
…
}
Step 2: the findAll method returns all entities that match the specification, as shown in the following example:
List<T> findAll(Specification<T> spec);
Step 3: The Specification interface is defined as follows:
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder);
}

Understanding repositories in Spring Data

I want to create a "generic" repository that query data from multiple entities. If I do that:
#Repository
public interface MyRepository {
#Query("select r from Role r")
List<Role> getRoles();
}
I get an error because Spring doesn't find an implementation to inject when a MyRepository instance is required. So far, so good. Now, If I do this:
#Repository
public interface MyRepository extends JpaRepository {
#Query("select r from Role r")
List<Role> getRoles();
}
I get an error because Object is not a JPA managed type (JpaRepository is generic). Ok, again. If I do this:
#Repository
public interface MyRepository extends JpaRepository<User, String> {
#Query("select r from Role r")
List<Role> getRoles();
}
It works. Why? I'm declaring a JpaRepository for entity User, not Role. Why does JpaRepository need a concrete entity even when the queries will be against another one?
Every repository in Spring Data has to extend the Repository interface, that is a generic interface, so you always have to specify the entity you are gonna work with and you can't do anything about it because it is how Spring Data is implemented. You can find more information here about creating repositories:
http://docs.spring.io/spring-data/jpa/docs/1.4.0.M1/reference/html/repositories.html
On the other hand, of course you can specify one entity to the repository and then add methods that return other type of entities because in your interface you can add whatever you want (also notice that Repository interface has no methods). But if you want to use the methods of the parent interface you have to use the entity you specified.
In your example, you could do what #M. Deinum suggested and create a JpaRepository<Role, Long> and use the findAll query, that makes much more sense. Using a JpaRepository<User, String> as you are doing is just a misuse of the framework.

Resources