Is it possible to use named parameters for a #Query method in a mongodb repository, just like we can do with a jpa repository (http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html section 2.3.5)?
As an example, I would like to use the following code:
#Query("{'store' : :store, 'app' : :app }")
List<T> findByStoreAndApp(#Param("store") String store, #Param("app") String app);
instead of:
#Query("{'store' : ?0, 'app' : ?1 }")
List<T> findByStoreAndApp(String store, String app);
It is possible, try:
#Query("{'store' : :#{#store}, 'app' : :#{#app} }")
List<T> findByStoreAndApp(#Param("store") String store, #Param("app") String app);
The support of SpEL expressions in #Query was introduced in Spring Data MongoDB 1.8.
https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.aggregation.projection.expressions
I'm afraid that the spring boot does provide this function in its source code.
You can create an interface and extends MongoRepository
It provides some simple method, and you don't need the implement it.
Just like use the JpaRepository.
#NoRepositoryBean
public interface MongoRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
<S extends T> List<S> save(Iterable<S> var1);
List<T> findAll();
List<T> findAll(Sort var1);
<S extends T> S insert(S var1);
<S extends T> List<S> insert(Iterable<S> var1);
}
However, if you need to do some special query. You could just #Autowired the MongoTemplate and use its own method. And for redis, springboot even doesn't provide the Repository like MongoRepository. You could just only use the template like StringRedisTemplate for query operation.
May be latter, spring boot would add the same function like JpaRepository for NoSql. But now, it doesn't provide this function.
Related
I am looking to use Spring JPA Dynamic Projections to limit the number of fields returned from queries. The table I'm using is wide, but the projection still contains around 10 fields. Therefore I am trying to use Dynamic Projections. The problem seems to be with trying to specify the methods in the repository interface since I first have an interface that extends the JpaRepository using an abstract class and then I have an interface the extends that one using the actual class.
I have tried various approaches to limiting the number of fields and this one seems to be the closest to what I want to use.
Here is my repository on the abstract class User:
#NoRepositoryBean
public interface UserRepository <T extends User> extends JpaRepository<T,
Long>{
<S extends T> S findByLoginName(String loginName);
}
Here is my actual repository on the actual class ConcreteUser:
#Repository
public interface ConcreteUserRepository extends UserRepository<ConcreteUser> {
}
In my service class implementation I have a method call that looks like this:
ConcreteUser user = this.userRepository.findByLoginName(loginName);
This of course returns a large number of fields, so I created an interface that contains the subset of fields that I want called UserProfile. The field names are exactly the same as those in the ConcreteUser. I then added 'implements UserProfile' to the User class. I don't know if that is necessary, but I'm trying to get the generics working so that I can do something like this:
#NoRepositoryBean
public interface UserRepository <T extends User> extends JpaRepository<T,
Long>{
<S extends T> S findByLoginName(String loginName, Class<S> clazz);
}
and then call it like this:
ConcreteUser user = this.userRepository.findByLoginName(loginName, ConcreteUser.class);
UserProfile profile = this.userRepository.findByLoginName(loginName, UserProfile.class;
I've tried various approaches with the generics. I've also tried using my DTO class UserProfileDTO instead of the UserProfile interface.
I am having problems with getting the Generics correct because of the extra level of abstraction.
I'd like to know if there's a way of overriding the delete method for some of my JpaRepository's without having to override the rest of the methods.
Currently I have something like
public interface UserRepo extends JpaRepository<User, Long>
{
findUserById(long id);
findUserByEmail(String email);
// etc...
}
And I'd like to override the delete(T Entity) method in CrudRepository. To do so I've tried implementing the UserRepo but then I have to implement all the findByX and haven't really find anything around on how to properly do that.
Is there any annotation to add to a function in the Entity class so it runs when you call UserRepo.delete(myUser)?
Thanks in advance!
Not sure I understand you clear enough, but lets try:
... I have to implement all the findByX ...
You don't, spring will generate JPQL snippet if you name methods in your interface with suitable convection please take a look at this and this articles
... Is there any annotation to add to a function in the Entity class
so it runs when you call UserRepo.delete(myUser)? ...
You can use #PreRemove / #PostRemove annotation on method in your entity class:
#PreRemove / #PostRemove
public void someMethod() { ... }
In addition to Raheela Aslam post:
Spring-data documentation has an example of how you can override standard repository methods, for example:
interface CustomizedSave<T> {
<S extends T> S save(S entity);
}
class CustomizedSaveImpl<T> implements CustomizedSave<T> {
public <S extends T> S save(S entity) {
// Your custom implementation
}
}
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}
You can read about it there:
https://docs.spring.io/spring-data/jpa/docs/2.1.2.RELEASE/reference/html/#repositories.custom-implementations
UPD:
Read it carefully, because there are some important things, e.g
The most important part of the class name that corresponds to the fragment interface is the Impl postfix.
Also the documentation says:
Custom implementations have a higher priority than the base implementation and repository aspects.
If you want to keep Spring's behavior for deletion, but want to have some logic to be executed either before or after, you may utilize java8's interface default methods, and try the following :
public interface UserRepo extends JpaRepository<User, Long> {
default void customDelete(User user) {
// before logic
// ..
delete(user); // actual call to deletion
// after logic
// ..
}
}
There are several ways to do this depending on what you're trying to do:
Use method naming, and let Spring derive the JPQL query from the naming (https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-creating-database-queries-from-method-names/)
Use the Query annotation and add the desired JPQL query in the annotation (https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-creating-database-queries-with-the-query-annotation/)
Use a named query (https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-creating-database-queries-with-named-queries/)
I prefer to use method naming if possible, the method name gets long, but you know exactly what it does by looking at it.
In your case code will be like as below:
public interface UserRepo extends JpaRepository<User, Long>
{
findUserById(long id);
findUserByEmail(String email);
// etc...
}
public interface UserRepositoryCustom {
void deleteByEmail(String email);
}
public interface UserRepositoryImpl implements UserRepositoryCustom {
public void deleteByEmail(String email) {
//provide your custom implimentation
}
}
Hi you can write your own Interface write implementation with EntityManager and
extend in you interface here is the sample :
https://dzone.com/articles/accessing-the-entitymanager-from-spring-data-jpa
Actually one more way is writing by soemthing like :
User findByUsername(String username) // it will find the user by specific username
spring data will create you an implementation of this method
The same way you can create your own delete method
Here is useful links:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
In this link you can go to part 2.3 QueryMethods:
https://docs.spring.io/spring-data/jpa/docs/1.6.0.RELEASE/reference/html/jpa.repositories.html
You can also define #NameQuery in your entity class:
#Entity
#Table(name = "employee", schema="spring_data_jpa_example")
#NamedQuery(name = "Employee.yourMethodQueryName",
query = "yourQuery"
)
public class Employee {}
#Repository
public interface EmployeeRepository extends JpaRepository<Employee,Long> {
List<Employee> yourMethodQueryName(Your list of params);
}
Here is link with sample:
https://www.logicbig.com/tutorials/spring-framework/spring-data/jpa-named-queries.html
I think this is helpful for you
public interface UserRepo extends JpaRepository<User, Long> {
#Modifying
#Query("delete from User u where u.email = ?1")
void deleteByEmail(String email);
}
#RepositoryRestResource
public interface StudentRepository extends CrudRepository<Student, Long>
{
public List<Student> findById(long id);
//#Query("select s from Student s where s.age <= ?")
public List<Student> findByAgeLessThanEqual (long age);
}
what does <Student, long> mean in the CrudRepository<Student, long> and what are the various parameters that can be passed to cruderepository.
The main idea of CrudRepository is to give you opportunity to use main operations with data without creating your own implementation. You just create needed methods and for most simple cases Spring will create implementation for you (if you use right naming convention for your methods).
First parameter (Student in your case) is a type of entity with which current Repository is working, second parameter (Long in your case) is a type of Id in this entity.
CrudRepository provides methods for the CRUD operations. This interface extends the Repository interface. If you are extending the CrudRepository, there is no need for implementing your own methods.
If do not want data from custom parameter, you have to write custom query for it.
you can use only that parameters which is their in your CrudRepository.
I've tried many different ways to pass an array of JSON to a Spring Data Rest Repository, not sure how to do it. I have a custom respository interface that is extending Repository:
#NoRepositoryBean
interface BaseRepository<T, ID extends Serializable> extends Repository<T, Long> {
T save(T entity)
List<T> save(Iterable<T> entities)
}
I can save a single entity, but when I try to pass an array of JSON objects I get an error cannot deserialize instance...
Not sure how to pass the object so that I can do a batch insert.
Unfortunatly you don't post the code that uses your interface, bug if you are actually passing an array as you describe in the question, you are not calling List<T> save(Iterable<T> entities) but T save(T entity). Arrays are not Iterables so the compiler will interpret your array as T and since an array is not an entity you get the error.
Convert the array to an Iterable to fix this. Arrays.asList(someArray) does the trick.
I seemed to work around it by overriding the save method, I'm sure there is a better way, I am open to suggestions.
BaseRepository
#NoRepositoryBean
interface BaseRepository<T, ID extends Serializable> extends Repository<T, Long> {
#RestResource(path="byIdIn",rel="byIdIn")
#Query("select r from #{#entityName} r where r.id in :q")
Page<T> findByIdIn(#Param("q") List<Long> q, Pageable pageable)
Page<T> findAll(Pageable pageable)
T findOne(ID id)
T save(T entity)
T delete(ID id)
}
ContactRepository
#RepositoryRestResource(collectionResourceRel="contacts", path="contacts")
interface ContactRepository extends BaseRepository<Contact, Long>, ContactRepositoryCustom {
}
ContactRepositoryCustom
interface ContactRepositoryCustom {
public <S extends Contact> S save(S entity)
}
ContactRepositoryImpl
#NoRepositoryBean
class ContactRepositoryImpl implements ContactRepositoryCustom {
#PersistenceContext
private EntityManager em
#Transactional
#Override
public <S extends Contact> S save(S entity) {
Contact contact = entity as Contact
try {
em.persist(contact)
contact.getComment().each {
Comment comment = new Comment(contact, it)
em.persist(comment)
}
} catch (Exception e) {
println e
}
return contact
}
}
This is just a sample, it needs some cleaning up, but I have the save() method working as expected. I just don't want to over do it if there is a baked in way in Spring Data / Spring Data Rest to do this kind of thing with annotations without having to roll a solution like this. I searched through the docs and online, but did not find a solution. I may have overlooked something, not sure.
How can I get access to the Entity Manager in the repository when using Spring Boot and Spring Data?
Otherwise, I will need to put my big query in an annotation. I would prefer to have something more clear than a long text.
You would define a CustomRepository to handle such scenarios. Consider you have CustomerRepository which extends the default spring data JPA interface JPARepository<Customer,Long>
Create a new interface CustomCustomerRepository with a custom method signature.
public interface CustomCustomerRepository {
public void customMethod();
}
Extend CustomerRepository interface using CustomCustomerRepository
public interface CustomerRepository extends JpaRepository<Customer, Long>, CustomCustomerRepository{
}
Create an implementation class named CustomerRepositoryImpl which implements CustomerRepository. Here you can inject the EntityManager using the #PersistentContext. Naming conventions matter here.
public class CustomCustomerRepositoryImpl implements CustomCustomerRepository {
#PersistenceContext
private EntityManager em;
#Override
public void customMethod() {
}
}
In case you have many repositories to deal with, and your need in EntityManager is not specific for any particular repository, it is possible to implement various EntityManager functionality in a single helper class, maybe something like that:
#Service
public class RepositoryHelper {
#PersistenceContext
private EntityManager em;
#Transactional
public <E, R> R refreshAndUse(
E entity,
Function<E, R> usageFunction) {
em.refresh(entity);
return usageFunction.apply(entity);
}
}
The refreshAndUse method here is a sample method to consume a detached entity instance, perform a refresh for it and return a result of a custom function to be applied to the refreshed entity in a declarative transaction context. And you can add other methods too, including query ones...