How to extends Spring Data JPA #Query Method Annotation ?
From this article http://www.javacodegeeks.com/2012/08/hibernate-native-sql-features-into-your.html I learn how to extends Spring data JPA Functionality, unfortunately the article tells about ElementType.TYPE annotation, but I need to understand ElementType.METHOD annotation and how this annotated method will by registered and handled by spring data jpa.
11052012 1547 --
I did further research about how to handle method annotation, I Found that I Need to involve AOP to intercept original method invocation with my custom annotation processor.
I Did this
#NoRepositoryBean
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Dynaque {
String value() default "";
}
#NoRepositoryBean
#Aspect
#Component("dynaqueAopListener")
public class DynaqueAopListener {
#Pointcut("#annotation(my.repo.engine.package.Dynaque)")
public void dynaqueMethods() {
}
#Around("dynaqueMethods()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
Method m = ((MethodSignature) pjp.getSignature()).getMethod();
Dynaque d = null;
if (m != null) {
d = m.getAnnotation(Dynaque.class);
}
Object output = pjp.proceed();
//my custom query logic here
return output;
}
}
Then I got next obstacle,
I Found that SD JPA scan all methods of every Repository Interface and do QueryLookup using QueryLookupStrategy to them, I don't know how to exclude methods that using #Dynaque annotation from SD - JPA QueryLookup so that I can implement my own query logic to that methods.
Related
I have a parent transaction at controller layer, but I want to start a new transaction when I call a repository, to achieve this I tried annotating Repository interface as below
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface EventRepo extends JpaRepository<Event, Integer>{ }
However this seems to not start a new transaction upon calls to EventRepo#save. Why?
Here is my service layer.
public interface IApplicationService {
void save(Event event);
}
#Service
public class ApplicationService implements IApplicationService {
#Autowired
private EventRepo eventRepo;
#Override
public void save(Event event) {
eventRepo.save(event);
}
}
It is in turn called from controller layer
#RequestMapping(value="/{indicator}", method=RequestMethod.POST)
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
#ResponseBody
public String processRequest(#PathVariable Integer indicator) {
Event event = new Event("Student1");
service.save(event);
if(indicator != 0) {
throw new RuntimeException();
}
return "Success";
}
However everything works perfectly if I annotate Service interface with #Transactional
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface IApplicationService {
void save(Event event);
}
When I say working what is mean is, if I run the below curl commands I will see 2 rows in h2 db for Event entity
curl -X POST http://localhost:8080/1
curl -X POST http://localhost:8080/0
I understand it is good to control transactions at Service layer then repository or controller layer, constructing situation this way makes it easy to demonstrate the problem.
Spring boot starter version is 2.5.6
below dependencie have versions managed by springboot starter
spring-boot-starter-data-jpa
spring-boot-starter-web
lombok
h2
Here is a thread that suggests it should be ok to annotate Repository layer although discourages it.
#Transactional on a JpaRepository
In this Spring article we can read the following:
Additionally, we can get rid of the #Transactional annotation for the method as the CRUD methods of the Spring Data JPA repository implementation are already annotated with #Transactional.
To me, this means that whatever #Transactional annotation you add to your EventRepo will be overridden by the #Transactional annotation mentioned above in the CRUD methods. Having said that, I really doubt #Transactional annotation has any effect in JpaRepository methods. It would have in your own custom methods, but it seems to me that it has none in the inherited methods.
In order to apply your own transactional settings in EventRepo#save override the save method:
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface EventRepo extends JpaRepository<Event, Integer>{
#Override
Event save(Event event);
}
Explanation
Spring ignores your #Transactional annotation because it cannot find the save method in the EventRepo proxy and applies the default transaction settings from the parent CrudRepository interface.
Further reading: How Does Spring #Transactional Really Work?
The question seems to be duplicate but none of the answers from existing questions worked.
I am creating a custom annotation as below,
#Target({ ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Translate {
}
I have created an Aspect as,
#Aspect
#Configuration
public class TranslateAspect {
#Around("#annotation(translate)")
public Object translate(ProceedingJoinPoint joinPoint, Translate translate) throws Throwable {
Object result = joinPoint.proceed();
System.out.println(result); //Other logic
return result;
}
}
I tried providing the complete class name with the package also. The entity is getting passed to RestController,
#Entity
#Getter
#Setter
public class Pojo {
#Translate
private String label;
}
But the translate method is not getting invoked whenever the new request is served.
Highly appreciate any help around this.
From the reference documentation : 5.2. Spring AOP Capabilities and Goals
Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans). Field interception is not implemented, although support for field interception could be added without breaking the core Spring AOP APIs. If you need to advise field access and update join points, consider a language such as AspectJ.
Spring AOP works with spring container managed beans and advicing method executions. The code shared here annotates a field and not the corresponding setter method. PCD defined is for the execution any Spring container managed bean method annotated with #Translate.
A class annotated with #Entity will not register its instance as a Spring managed bean. Do go through this StackOverflow Q&A
Pojo instance is not a Spring managed bean ( also pointed out by João Dias in his answer ).
Try this:
#Aspect
#Configuration
public class TranslateAspect {
#Pointcut("#annotation(com.full.packagename.to.annotation.Translate)")
public void anyTranslatableMethod() {
}
#Around("anyTranslatableMethod()")
public Object translate(ProceedingJoinPoint joinPoint) throws Throwable {
// ...
}
}
A working example here: https://github.com/asgarov1/springTricks/blob/main/aop/src/main/java/com/asgarov/aop/config/LoggingConfiguration.java
Try the following:
#Aspect
#Configuration
public class TranslateAspect {
#Around("#annotation(Translate)")
public Object translate(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
System.out.println(result); //Other logic
return result;
}
}
Mind the uppercase "T" in #Around("#annotation(Translate)").
UPDATE
Just noticed you are expecting the aspect to be applied to a class annotated with #Entity. These are Entities that are JPA entities but they are not Spring-managed Beans. Spring AOP only handles Spring-managed Beans so this is simply not possible.
I have defined a custom annotation as below.
package com.xyz;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Loggable {
String message() default "Log Message";
}
My aspect class contains the below method:
#Around(value = "#annotation(com.xyz.Loggable)")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// come code here
}
My service interface is as below.
public interface Service {
#Loggable
public void method1();
}
My implementation is as below.
public class ServiceImpl implements Service {
public void method1() {
// some code here
}
}
With this setup, My advice is not getting triggered. (however it gets triggered if i move the #Loggable annotation to method1() in ServiceImpl class).
I would like to keep the annotation defined at interface level instead of method implementations. Is there a way to get this work ?
No, that is not possible (yet?).
Annotations can only be inherited among Classes and even then only if they are themselves annotated with the meta-Annotation #Inherited:
http://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Inherited.html
It is not possible to have annotations on Interfaces be inherited to their implementing classes.
This is also explained in the AspectJ documentation: http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance
#Inherited annotations are not inherited when used to annotate anything other than a type. A type that implements one or more interfaces never inherits any annotations from the interfaces it implements.
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...
I have a project where I have an interface, an Abstract class implementing the same interface and then a set of concrete classes which implement this interface and extend the Abstract Class.
public interface Invoice
{
void process();
}
#component
public abstract class AbstractInvoice(){
#Resource
protected Writer writer;
protected validateInvoice(){
//some implementation
}
}
#Component
public Class TypeAInvoice() extends AbstractInvoice implements Invoice{
#Override
public void process(){
//... some code
writer.write();
}
}
public Interface Writer(){
public void write();
}
#Component
public class CDWriter implements Writer{
#Override
public void write() { /* implementation.....*/}
}
Spring file has a component scan for the package.
<context:annotation-config>
<context:component-scan base-package="com.xyz" />
I am using a factory to get an instance of TypeAInvoice invoice
Now calling invoice.process() gets a NPE when getting to write.write()
I am not sure what am I missing here. I tried to see the component scan and scope and could not find anything conceptually wrong.
I am using a factory to get an instance of TypeAInvoice invoice
Depending on what your Factory does, this may be the problem. If the Factory creates a new TypeAInvoice, Spring wiring doesn't apply. You have to query the Spring context for the Bean. One way (though not very pretty) is to use ContextLoader:
return ContextLoader.getCurrentWebApplicationContext().getBean(TypeAInvoice.class)
I'd say static Factories and Spring don't go to well together. Spring stands for the Inversion of Control pattern, while Factories stand for the Service Locator pattern. I'd suggest that you get rid of your factories and autowire your Spring Beans.
Everything is good, except for the fact you use a factory to get the TypeAInvoice. If you create it like TypeAInvoice typer = new TypeAInvoice() then spring knows nothing of it, the Writer is not autowired, there for you get the NullPointerException. You should get the bean from the spring application context.
In my case, inside a Spring4 Application, i had to use a classic Abstract Factory Pattern(for which i took the idea from - http://java-design-patterns.com/patterns/abstract-factory/) to create instances each and every time there was a operation to be done.So my code was to be designed like:
public abstract class EO {
#Autowired
protected SmsNotificationService smsNotificationService;
#Autowired
protected SendEmailService sendEmailService;
...
protected abstract void executeOperation(GenericMessage gMessage);
}
public final class OperationsExecutor {
public enum OperationsType {
ENROLL, CAMPAIGN
}
private OperationsExecutor() {
}
public static Object delegateOperation(OperationsType type, Object obj)
{
switch(type) {
case ENROLL:
if (obj == null) {
return new EnrollOperation();
}
return EnrollOperation.validateRequestParams(obj);
case CAMPAIGN:
if (obj == null) {
return new CampaignOperation();
}
return CampaignOperation.validateRequestParams(obj);
default:
throw new IllegalArgumentException("OperationsType not supported.");
}
}
}
#Configurable(dependencyCheck = true)
public class CampaignOperation extends EO {
#Override
public void executeOperation(GenericMessage genericMessage) {
LOGGER.info("This is CAMPAIGN Operation: " + genericMessage);
}
}
Initially to inject the dependencies in the abstract class I tried all stereotype annotations like #Component, #Service etc but even though Spring context file had ComponentScanning for the entire package, but somehow while creating instances of Subclasses like CampaignOperation, the Super Abstract class EO was having null for its properties as spring was unable to recognize and inject its dependencies.After much trial and error I used this **#Configurable(dependencyCheck = true)** annotation and finally Spring was able to inject the dependencies and I was able to use the properties in the subclass without cluttering them with too many properties.
<context:annotation-config />
<context:component-scan base-package="com.xyz" />
I also tried these other references to find a solution:
http://www.captaindebug.com/2011/06/implementing-springs-factorybean.html#.WqF5pJPwaAN
http://forum.spring.io/forum/spring-projects/container/46815-problem-with-autowired-in-abstract-class
https://github.com/cavallefano/Abstract-Factory-Pattern-Spring-Annotation
http://www.jcombat.com/spring/factory-implementation-using-servicelocatorfactorybean-in-spring
https://www.madbit.org/blog/programming/1074/1074/#sthash.XEJXdIR5.dpbs
Using abstract factory with Spring framework
Spring and Abstract class - injecting properties in abstract classes
Inject spring dependency in abstract super class
Spring autowire dependency defined in an abstract class
Spring can you autowire inside an abstract class?
Please try using **#Configurable(dependencyCheck = true)** and update this post, I might try helping you if you face any problems.
So precisely my point here is you don't need to get a bean from spring context all the time.