Wrapping Spring Data JPA with ApsectJ - jpa

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.

Related

spring data repository Implenation

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
}

Mixing Spring Data Envers and QueryDSL

I'm using a global custom repository in my project which extends QueryDslJpaRepository:
public class CustomPagingAndSortingRepositoryImpl<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID>
implements CustomPagingAndSortingRepository<T, ID> {
And the interface:
public interface CustomPagingAndSortingRepository<T, ID extends Serializable>
extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {
And then on my configuration I annotate it with:
#EnableJpaRepositories(repositoryBaseClass = CustomPagingAndSortingRepositoryImpl.class)
All is working fine, but now I was trying to add auditing support to my entities by using spring-data-envers and according to the docs I should use a specific repository factory bean class :
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, repositoryBaseClass = CustomPagingAndSortingRepositoryImpl.class)
Now obviously if I do this things won't work because my repositories will now be created through the EnversRevisionRepositoryFactoryBean class and will no longer be of CustomPagingAndSortingRepositoryImpl type.
How can I support something like this? I'm not seeing how since my custom repository need to extend from QueryDslJpaRepository already.
I think the relevant part for you is this method of EnversRevisionRepositoryFactoryBean:
#Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return EnversRevisionRepositoryImpl.class;
}
Here you really want your CustomPagingAndSortingRepositoryImpl returned. So I would try the following:
extend EnversRevisionRepositoryFactoryBean and overwrite getRepositoryBaseClass to return your CustomPagingAndSortingRepositoryImpl.
Make CustomPagingAndSortingRepositoryImpl extend EnversRevisionRepositoryImpl.

Spring data jpa. Partial custom repository implemantation

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.

Play Framework PathBindable with JPA Transactionnal context incompatible?

I'm trying to implement a simple PathBindable Interface for my customEntity, thing is I'm working with JPA, and when I try to load the bindable url, I get this JPA error :
[info] application - onBadRequest: POST /path/customEntity/2554003 - No EntityManager bound to this thread. Try to annotate your action method with #play.db.jpa.Transactional
Of course the action method bound to this route is annotated with #Transaction.
I've also tried to add the annotation directly on the customEntity.bind method but i got same error.
My Entity code sample :
public class MyEntity implements Serializable, PathBindable<MyEntity> {
#Override #play.db.jpa.Transactional
public MyEntity bind(String key, String value) {
return findById(Long.valueOf(value));
}
#Override
public String unbind(String s) {
return String.valueOf(id);
}
#Override
public String javascriptUnbind() {
return String.valueOf(id);
}
}
My controller sample :
#Transactional
public class MyController extends Controller {
public static Result read(final MyEntity ent) {
return ok(ent.getName());
}
}
My route file sample :
POST /myEntity/:ent controllers.MyController.read(ent : MyEntity)
EDIT :
Like suggested by #Mon Calamari, I've tried with JPA.withTransaction syntax and it works partially, it leads me to another problem with relational data loading, looks like I can't get any relational data from the model afterwards if the model is fetched withing the withTransaction method.
Execution exception[[LazyInitializationException: failed to lazily initialize a collection of role: models.Entity.mappedChilds, could not initialize proxy - no Session]]
Some kind of JPA limitation ? Does it mean JPA and PathBindable's Play feature are incompatible ?
#play.db.jpa.Transactional annotation is for controllers methods only. Try wrapping findById(Long.valueOf(value)) into JPA.withTransaction

How to customize MongoRepository without overriding the annotated #Query methods in interface?

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