I want to use RepositoryItemWriter to write batch entities using the default implementation of SimpleJpaRepository.
#Autowired
private MyCrudRepository crudDao;
RepositoryItemWriter<HrsGiataId> w = new RepositoryItemWriter<>();
w.setRepository(crudDao);
w.setMethodName("deleteInBatch");
public interface MyCrudRepository extends CrudRepository<MyEntity, Long> {}
But the code above will not work as the w.setMethodName requires a method name from the CrudRepository interface, even though the default implementation for the crudrepository is SimpleJpaRepository, which has the deleteInBatch() method.
So, what can I do to make use of the spring crud repository specific jpa method?
I don't believe that the writer requires a method from the CrudRepository. You should be able to specify any method name you want. If not, I'd raise that as a bug in Jira (https://jira.spring.io/browse/BATCH/).
Related
Is there any way to intercept or change the document before MongoRepository.save(e) updates it?
I am trying to push a subproperty inside and array in a document. I have tried to manipulate DBObjects by implementing converter(using custom converter) but the $push operation did not work over there.
I think to make it work I have to implement something like mongoOperation.update(dbObjectMatch,dbObjectUdate).
I found MongoRepositorysave(document) doesn't support partial update, i.e write only the change to an existing document. I want to know the internal code of MongoRepository.save to override the default behavior.
I have implemented MyRepositoryCustom where I can override save by extending the same in MyRepository, which extends MongoRepository<T, ID extends Serializable> and then used mongoTemplate.updateFirst(query,update,Clazz.class) to achieve what I am looking for but I am not satisfied.
You have multiple, slightly different questions:
From your title:
Change/Override Default Behaviour of Mongorepository Save() ( S save(S var1)) Method
You can use custom implementations to override the behavior of existing methods in Spring Data repositories. See the reference documentation how to do that. Your last paragraph suggests you already do that. Unfortunately you don't tell us why you aren't satisfied with this.
Is there any way to intercept/change before Mongorepository Save() ( S save(S var1)) method For document update.
Yes a Spring Data MongoDB repository fires various life cycle events for this purpose. Once again, see the reference documentation for details.
I want to know the Internal code of Mongorepository Save
What you are looking for is SimpleMongoRepository.java which delegates for almost all work to MongoTemplate.java
You're looking for Lifecycle Events.
Overriding repository base methods allows you to interact with the domain object itself but the mapping happens inside of MappingMongoConverter.
Saving an object will fire events such as
BeforeSaveEvent
BeforeConvertEvent
AfterConvertEvent
These events carry a reference to your saved object. AfterConvertEvent additionally exposes the mapped representation (DBObject) of your object which you can change/enhance.
You can listen to these events by configuring a listener bean such as ApplicationListener<AfterConvertEvent>.
You can fragmentally override the MongoRepository by ensuring you match the signature of method in the custom interface.
E.g. If you want to override save() method, create a new interface like
interface CustomizedSave<T> {
<S extends T> S save(S entity);
}
Implement this interface:
#Component
class CustomizedSaveImpl<T> implements CustomizedSave<T> {
//This is optional, I added it to just show you can use it here
MongoTemplate mongoTemplate;
#Autowired
public CustomizedSaveImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public <S extends T> S save(S entity) {
// Your custom implementation
}
}
Extend the interface in the base MongoRepository like
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}
This will ensure only save menthod gets overridden and rest stay as is.
Docs: https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.single-repository-behavior
So I was following the tutorial here: https://spring.io/guides/gs/accessing-data-jpa/
and it works fine, I'm trying to implement it in my application (Because it makes JPA so easy to use), but I'm confused.
Where it has
#Bean
public CommandLineRunner demo(CustomerRepository repository)
and then it acts on the repository
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
How can it act on an interface? CustomerRepository is an interface, and I can't instantiate it
CustomerRepository c = new CustomerRepository()
cant be done, and I don't have any classes that implement the interface. I just wanted to do something like
CustomerRepository c = new CustomerRepository()
c.save(new Customer("whatever", "whatever")
but I can only use it in the CommandLineRunner bean method. Why can I do this with commandlinerunner but cant do it with another class?
I was thinking I could just make a class that extends CustomerRepository, but then I read that the interface actually does all the method implementation itself (JPA does it) so you don't have to worry about it.
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
so if I extended it, wouldn't I have to override the findbylastname() method, meaning JPA wouldn't do it itself?
Thanks for any assistance.
so if I extended it, wouldn't I have to override the findbylastname()
method, meaning JPA wouldn't do it itself?
No, it is not JPA which does the job but Spring which generates by APO some JPA processings.
With Spring Repository, you have multiples ways of doing :
write your own SQL/JPQL query.
use naming convention in the method name to write the query
In both cases, you don't need to implement directly the interface you declare here :
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
Because as you understood, the job is performed for you by Spring.
For example, in the case you quote, you use naming convention in the method name to write the query.
When Spring inspects your interface and sees the method findByLastName(String lastName), it associates the method name to a query which does a find with a match by lastName field. So, it generate a JPQL query like that :
select c from Customer c where c.lastName=%lastName
and it sets the lastName param with the effective parameter used when the method is call.
I extended it, wouldn't I have to override the findbylastname()
method, meaning JPA wouldn't do it itself ?
No, you don't need to implement the methods as spring-data-jpa will take care of it, you can look here on how Spring data repository interfaces are actually implemented by proxy at runtime.
The main point that you need to remember is that spring data has got few rules to derive the sql queries from the method names (like findByLastName(), findByLastnameOrderByFirstnameAsc(), etc..), you can look here to understand how method names work and they are related to field names of your entity bean.
If you wanted to write some complex queries which can't be derived from method names you can use #Query for your methods.
If I made a class public class Cust implements CustomerRepository what
would I do when it asks me I have to implement the
findByLastName(String lastName); method that JPA is supposed to take
care of ?
If you wanted to implement repository to provide your custom behaviour for few of the methods for few of your methods, you can do that (like class Cust implements CustomerRepository), you can refer the section "Using JpaContext in custom implementations", it is well explained in the ref doc.
I'm trying to write a generic SecurePagingAndSorting repository which will check security on CRUD operations to save repeating the same PreAuthorize (with different authorities) throughout all JPA repositories.
Here is a simple example below:
#NoRepositoryBean
public interface SecuredPagingAndSortingRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
#Override
#PreAuthorize("hasPermission(#id, domainType, 'delete'))
void delete(ID id);
}
Now it's the domainType argument that's the problem here, since this is a generic interface, it can't be hard coded. What is the best approach here to get the domain type from repositories that derive from SecurePagingRepository.
The best solution I see is writing your own implementation of the interface PermissionEvaluator and then inject it in the security context replacing the default one.
Should you try this way, extending class AclPermissionEvaluator will save you lots of code already managed by Spring, and ensures back compatibility.
I settled on this solution in the end. PreAuthorize has the facility to use any bean within the Spel expression using the # character.
#Override
#PreAuthorize("hasPermission(#id, #security.getDeletePermission(#id,#this.this)))
void delete(ID id);
}
So when the 'security' bean's getDeletePermission function is called, the #this.this parameter translates to the SimpleJpaRepository in question. This allows us determine the concrete repository in question and returned the desired permission name
I would like to create a Spring Data JPA repository with custom behavior, and implement that custom behavior using Specifications. I have gone through the Spring Data JPA documentation for implementing custom behavior in a single repository to set this up, except there is no example of using a Spring Data Specification from within a custom repository. How would one do this, if even possible?
I do not see a way to inject something into the custom implementation that takes a specification. I thought I would be tricky and inject the CRUD repository portion of the repository into the custom portion, but that results in a circular instantiation dependency.
I am not using QueryDSL. Thanks.
I guess the primary source for inspiration could be how SimpleJpaRepository handles specifications. The key spots to have a look at are:
SimpleJpaRepository.getQuery(…) - it's basically creating a CriteriaQuery and bootstraps a select using a JPA Root. Whether the latter applies to your use case is already up to you. I think the former will apply definitely.
SimpleJpaRepository.applySpecificationToCriteria(…) - it basically uses the artifacts produced in getQuery(…) (i.e. the Root and the CriteriaQuery) and applies the given Specification to exactly these artifacts.
this is not using Specification, so not sure if it's relevant to you, but one way that I was able to inject custom behavior is as follows,
Basic structure: as follows
i. create a generic interface for the set of entity classes which are modeled after a generic parent entity. Note, this is optional. In my case I had a need for this hierarchy, but it's not necessary
public interface GenericRepository<T> {
// add any common methods to your entity hierarchy objects,
// so that you don't have to repeat them in each of the children entities
// since you will be extending from this interface
}
ii. Extend a specific repository from generic (step 1) and JPARepository as
public interface MySpecificEntityRepository extends GenericRepository<MySpecificEntity>, JpaRepository<MySpecificEntity, Long> {
// add all methods based on column names, entity graphs or JPQL that you would like to
// have here in addition to what's offered by JpaRepository
}
iii. Use the above repository in your service implementation class
Now, the Service class may look like this,
public interface GenericService<T extends GenericEntity, ID extends Serializable> {
// add specific methods you want to extend to user
}
The generic implementation class can be as follows,
public abstract class GenericServiceImpl<T extends GenericEntity, J extends JpaRepository<T, Long> & GenericRepository<T>> implements GenericService<T, Long> {
// constructor takes in specific repository
public GenericServiceImpl(J genericRepository) {
// save this to local var
}
// using the above repository, specific methods are programmed
}
specific implementation class can be
public class MySpecificEntityServiceImpl extends GenericServiceImpl<MySpecificEntity, MySpecificEntityRepository> implements MySpecificEntityService {
// the specific repository is autowired
#Autowired
public MySpecificEntityServiceImpl(MySpecificEntityRepository genericRepository) {
super(genericRepository);
this.genericRepository = (MySpecificEntityRepository) genericRepository;
}
}
I am confused. I could not find out, how to define together custom "search" methods with methods that were loaded with help of spring-data-rest.
Could you answer me, does the framework has this possibility "out-of-box"?
And if has, could you tell me, where can i find it?
For a deeper understanding my situation i describe my issue:
class UserService {
public String getListOfWaitingUsers() {
return userRepository.findAll(UserSpecification.isWaiting());
}
}
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
Page<User> findByNameLike(#Param("name") String name, Pageable pageable);
}
I want that it to be like:
/user/search/
findByNameLike
findWaitingUsers
How to implement that my methods of specifications or services (there is not method in repository) will define with path "/resource_name/search/METHOD_NAME" ( methods of repository + ( METHODS SERVICES OR SPECIFICATIONS)
Spring Data REST framework is based on Spring Data Respository, so your service class can be ignored here.
All methods that are not part of CRUD/Paging Repository as exposed as "search" methods provided you annotated all parameters with #Param annotation. So in your case, you need to implement your method following the conventions outline in Spring Data commons docs. So once you have implementation for findByNameLike method, the method would be exposed as ../search/findByNameLike URL. If needed, you could customize the rel and path with #RestResource annotation.
Also note your UserRepository should ideally be working only on User object and hence the methods you defined. In your case, UserRepository is returning Process/ProcessContext objects? Instead it should be like below
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
Page<User> findByNameLike(#Param("name") String name, Pageable pageable);
}