We can write custom implementation of repository:
interface UserRepositoryCustom {
public void someCustomMethod(User user);
}
class UserRepositoryImpl implements UserRepositoryCustom {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
But what if I want customize only some methods? For example:
interface UserRepositoryCustom {
public User findByFirstName(String firstName);
#Query("select u.firstName from User u where u.age > 18")
public Set<String> findAllAdultUsers();
public void someCustomMethod(User user);
}
class UserRepositoryImpl implements UserRepositoryCustom {
//I want implement only this method
public void someCustomMethod(User user) {
// Your custom implementation
}
}
If I declare a class, which implements an interface, I have to implement all methods, but I want to write custom logic for only one method.
Is it possible to do this? Maybe I can make this class abstract? Will spring data resolve this?
I think only solution is to split methods in 2 interfaces: first - for spring query method, and second - for custom implementation, as shows in doc: http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.single-repository-behaviour.
But I think solution with abstract class would be more natural and logic: you provide only needed method implementations and spring data do the rest for you.
Let's say you have a repository with a few methods whose implementations are generated by Spring:
interface UserRepository extends CrudRepository<User,String> {
List<User> findUserByLastname(String lastName);
}
In order to add a method with a custom implementation, you need to create another interface that only contains the methods you want to customize, and make your repository extend the custom one:
interface CustomUserRepository {
User someCustomMethod();
}
interface UserRepository extends CrudRepository<User,String>, CustomUserRepository {
List<User> findUserByLastname(String lastName);
}
You can then implement the extra methods by creating an implementation class for the new interface:
class CustomUserRepositoryImpl implements CustomUserRespository {
#Override
User someCustomMethod() {
// implementation goes here.
}
}
The class name is important here: in order for Spring to find it, it should be the name of the interface that is being extended with Impl on the end.
The implementation repository is a normal Spring bean, so you can autowire a constructor to inject various dependencies.
There is a much more detailed tutorial here: https://www.baeldung.com/spring-data-composable-repositories.
Related
First of all, this is not a duplicate of Spring Data: Override save method. I want to override the save method, and I know where to find the documentation, but my question is how to call the original implementation in my custom code.
To override save() method in spring-data-*, you do something like below:
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> {
}
interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
My question is how to call the "super" implementation of save()? In spring-data-elasticsearch, the default save() implementation is not so simple to set up (basically I need to copy AbstractElasticsearchRepository source code), so I would rather not do this.
#Autowired
private EntityManager em;
#Override
public User save(User entity) {
JpaRepositoryFactory jrf = new JpaRepositoryFactory(em);
UserRepositories repoWithoutCustom = jrf.getRepository(UserRepositories.class);
do somth....
}
Where repoWithoutCustom what you need, your UserRepository without any customized methods. Just use required RepositoryFactory, in your case Elastic as i understood
I am using Spring DATA JPA and selected #Query annotation for creating queries (instead of using NamedQueries and Queries created from MethodName)
I have a data repository as below
public interface EventRepository extends CrudRepository<Event, Long> {
#Query("select e from Event e where e.name = :eventName)
public List<Event>findEventByName(String eventName );
}
Interface looks good and its enough as per Spring reference doc.
But I need a impl class because I need many other methods in addition to above.
I am facing 2 issues when I create EventRepositoryImpl java implementing EventRepository
Its asking to implement all the methods in EventRepository, findEventByName method is self contained in interface and why I need implement it again in Impl class?
Its asking to implement all the methods in CrudRepository, I know its per OOPS design, But there many methods
So, for these issues can I define my EventRepositoryImpl as abstract,
this seems to be working fine.
But do I need to worry about anything else, when Spring uses a abstract class as a bean.
or is there an elegant way to solve this issue.
Appreciate your help.
You do not have to implement all of these methods neither create an abstract class. Take a look into official documentation.
interface UserRepositoryCustom {
public void someCustomMethod(User user);
}
class UserRepositoryImpl implements UserRepositoryCustom {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom {
// Declare query methods here
}
Am successfully injecting jpa repositories using CDI. I wanted to add custom behaviour(soft deletes) to all repositories. When using spring I can enable customer behaviour by specifying the repository base class
#EnableJpaRepositories(repositoryBaseClass = StagedRepositoryImpl.class)
How do I specify the same in CDI? Thanks in advance.
To add custom behaviour to Jpa Repositories(in your case for delete),
1. Create a base repository like below:
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
#Override
default void delete(T entity){
// your implementation
}
}
2. Now inherit Jpa Repositories from custom repository(i.e BaseRepository) like below:
public interface EmployeeRepository extends BaseRepository<Employee, Long> {
}
3. Inject your repository into Service class and call the delete method.
#Service
class EmployeeService {
#Inject
private EmployeeRepository employeeRepository;
public void delete(Long id) {
employeeRepository.delete(id);
}
}
Now whenever you call delete on repositories which are child of BaseRepository, your custom implementation for delete will be invoked.
Here is the way to add custom logic to your repositories:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
Basically you create a custom repository named {YourRepositoryName}Custom
interface UserRepositoryCustom {
public void someCustomMethod(User user);
}
And implement it:
class UserRepositoryImpl implements UserRepositoryCustom {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
Your main repository should extend the custom one.
Hope this helps!
Is it possible?
Currently I am using some aspects for my MVC controllers, what works really fine. I'm wrapping their responses and I have desired effect.
I also want to do this with Spring Data JPA repositories. But since they're generated based on the interface e.g:
public interface SomeRepository<T extends Some, ID extends Serializable> extends
BaseRepository<T, ID>, JpaSpecificationExecutor<T> {
public List<T> findById(Long id)
}
It generates me controller which is ready to use:
http://localhost:8080/findById?id=1234
I also want to wrap this controller. Is it possible?
This should work:
#Component
#Aspect
public class MyAdvice {
#Before("execution(* com.company.jpa.SomeRepository+.findById(..))")
public void intercept() { ... }
}
Basically, we are telling the framework to intercept the call to the findById method on any sub-class of SomeRepository.
Here is a sample application demonstrating this in action.
I want to customize MongoRepository by adding one method, and still using the implemented methods provided by MongoRepository. Below is the code:
public interface TopoRepositoryInterface extends MongoRepository<Topo, String>
{
#Query("{'name':?0}")
public Topo findByName(String name);
public long getPublishedTopoCount();
}
the implementation declaration is:
public class TopoRepositoryImpl extends SimpleMongoRepository<Topo, String> implements TopoRepositoryInterface
If without the customization, method findByName declared in TopoRepositoryInterface can be automatically implemented by adding #Query("{'name':?0}") annotation. But now, since there is interface inheritage, I must add code
#Override
public Topo findByName(String name)
{
Topo topo = getMongoOperations().findOne(Query.query(Criteria.where("name").is(name)), Topo.class);
return topo;
}
Is there any way to write my own code for getPublishedTopoCount() only, and leave findByName() be implemented by #Query annotation? Thank you very much.
You have to split your repository interface into two.
First one - "Custom" containing methods you implement manually would be:
public interface TopRepositoryCustom {
long getPublishedTopoCount();
}
Second one for generated methods:
public interface TopRepository extends MongoRepository<Topo, String>, TopRepositoryCustom {
#Query("{'name':?0}")
Topo findByName(String name);
}
Then you just need to implement first repository and remember to follow proper naming convention. See more at: spring-data mongodb custom implementation PropertyReferenceException and Spring Data MongoDB Custom implementations reference