I am trying to enable basic caching with Spring Data JPA. But I cannot understand why the DAO methods are still querying the database instead of using the cache.
Given the following Spring Boot 1.5.1 application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class Server{
public static void main(String[] args) {
SpringApplication.run(Server.class, args);
}
}
Controller
#Controller
public class PasswordsController {
#Autowired
private PasswordService service;
#SuppressWarnings("unchecked")
#RequestMapping("/passwords.htm")
public void passwords(Map model,
HttpServletRequest request) {
model.put("passwords", service.getPasswords(request));
}
...
Service
#Service
#Transactional
public class PasswordService extends BaseService {
#Autowired
private PasswordJpaDao passwordDao;
public Collection<Password> getPasswords(HttpServletRequest request) {
Collection<Password> passwords = passwordDao.getPasswords(params);
return passwords;
}
...
Interface
#Transactional
public interface PasswordJpaDaoCustom {
public Collection<Password> getPasswords(PasswordSearchParameters params);
}
and implementation
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import com.crm.entity.Password;
import com.crm.search.PasswordSearchParameters;
#Transactional
#Repository
public class PasswordJpaDaoImpl implements PasswordJpaDaoCustom {
#PersistenceContext
private EntityManager em;
#Override
#Cacheable("passwords")
public Collection<Password> getPasswords(PasswordSearchParameters params) {
System.err.println("got here");
return em.createQuery(hql, Password.class);
}
...
Maven Dependencies
<!-- Spring Boot start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Spring Boot end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
I understand that Spring Boot will implicitly use ConcurrentHashMap for caching without any specific configuration necessary?
But the getPasswords() dao method is always called instead of using the cache. Why is this?
Yes, spring boot by default uses ConcurrentHashMap for caching and the issue with your code is that you did not set any key for your passwords cache, so it is calling the database every time for fetching the data.
So you need to the key (any unique identifier) using the params object variables as shown below:
#Cacheable(value="passwords", key="#params.id")//any unique identifier
public Collection<Password> getPasswords(PasswordSearchParameters params) {
System.err.println("got here");
return em.createQuery(hql, Password.class);
}
Related
I am trying to create a spring boot library with Custom annotation and Spring AOP. When I used this library with new spring boot application. Then Its not working. Even I am not getting any error.
Library Sample -
Custom Annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface HttpLogger {
}
Spring AOP class
#Aspect
class LoggingAspect {
#Around("#annotation(com.demo.commonlogging.aspect.HttpLogger)")
public Object inControllers(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
return loggingAdvice(proceedingJoinPoint); // Method for implementation
}
}
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
Using mvn clean install for creating library
Now new library is imported in springboot application.
And new Custom annotation is used in controllers
Controllers
#RestController
#RequestMapping(value = "/rest/test")
public class RestApiTestControllers {
#GetMapping
#HttpLogger
public String get(){
return "Hello !";
}
}
Please help here.
Seems like you are missing #Component from LoggingAspect also make call to proceed proceedingJoinPoint.proceed(); and return it's value.
So your code should look like:
#Aspect
#Component
class LoggingAspect {
#Around("#annotation(com.demo.commonlogging.aspect.HttpLogger)")
public Object inControllers(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Before call");
Object returned = proceedingJoinPoint.proceed();
System.out.println("After call");
return returned;
}
}
Hope this helps!
I was following this JavaBrains tutorials of Spring Boot.
My project structure is as follows:
CourseApiApp.java:
#SpringBootApplication
#ComponentScan(basePackages = {
"com.bloodynacho.rishab.topics"
})
#EntityScan("com.bloodynacho.rishab.topics")
public class CourseApiApp {
public static void main(String[] args) {
SpringApplication.run(CourseApiApp.class, args);
}
}
TopicController.java:
#RestController
public class TopicController {
#Autowired
private TopicService topicService;
#RequestMapping(
value = "/topics"
)
public List<Topic> getAllTopcs() {
return topicService.getAllTopics();
}
}
TopicService.java:
#Service
public class TopicService {
#Autowired
private TopicRepository topicRepository;
public List<Topic> getAllTopics() {
List<Topic> topics = new ArrayList<>();
this.topicRepository
.findAll()
.forEach(topics::add);
return topics;
}
}
Topic.java:
#Entity
public class Topic {
#Id
private String id;
private String name;
private String description;
}
TopicRepository.java:
#Repository
public interface TopicRepository extends CrudRepository<Topic, String>{
}
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
I was using the lombok #Getter, #Getter and #AllArgsConstructor in Topic.java but I removed it after reading one of the answers here.
I read this1, this2, this3
Still, I get
***************************
APPLICATION FAILED TO START
***************************
Description:
Field topicRepository in com.bloodynacho.rishab.topics.TopicService required a bean of type 'com.bloodynacho.rishab.topics.TopicRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.bloodynacho.rishab.topics.TopicRepository' in your configuration.
Process finished with exit code 1
EDIT: I read this explaining how even without actually implementing the interface the #Autowired works. I understand the solution, but I don't understand how to solve my issue. Clearly, there is some problem with the way Spring Data is set up and configured (as mentioned in the answer)
Because if your other packages hierarchies are below your main application with the #SpringBootApplication annotation, you’re covered by implicit components scan.
Therefore, one simple solution can be done by following 2 steps:
Rename the package of main class to be com.bloodynacho.rishab.
(That is what I suggest that the complete package name of main app. is supposed to be root of other packages.)
Remove #ComponentScan and #EntityScan annotation.
(Although #ComponentScan is different from #EntityScan, it can be also removed in my experience.)
Since I'm using Thymeleaf only for email template processing (not for view creation), I have the following exclusion in my application class (i.e. a class extending SpringBootServletInitializer):
#SpringBootApplication(exclude=org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration.class)
Absent Spring Boot's autoconfiguration, I need to configure the Java8TimeDialect class manually, so I've added the following bean to my ThymeleafConfig class:
#Bean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
}
Nonetheless, I'm still getting the following exception:
Attempted to call method
format(java.time.LocalDateTime,java.lang.String) on null context
object
Clearly the temporals object isn't being added to the context. How can I resolve this?
I've included Thymeleaf in my POM file as follows:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
Add pom.xml
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
then:
package com.users.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
import nz.net.ultraq.thymeleaf.LayoutDialect;
#Configuration
public class Thymeleaf {
//TemplateEngine templateEngine = new TemplateEngine();
#Bean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
}
#Bean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
Finnally:
The time is: <strong th:text="${#temporals.format(now, 'dd/MMM/yyyy HH:mm')}">31/12/2015 15:00</strong>
I have some issue in Logging all the methods when a service calls. Code is like this:
package com.myproject.controller;
#RestController(/person)
public class Controller{
public Person getpersonInfo(){
......
getValidPerson();
}
}
public Person getValidPerson() {
isPersonValid(Person person);
....
}
Person class Methods :
package com.myproject.dao;
public class Dao{
public boolean isPersonValid(){
//Checks for the person is Valid
}
}
Aspect Class :
package com.myproject;
#Component
#Aspect
public class Logging{
#Before("execution(* com.myproject..*.*(..)))")
public void beforeServiceCall(Jointpoint jp) {
//Some Logging function
}
}
Main class like this
package com.myproject;
#SpringBootApplication
#EnableAutoConfiguration
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableAspectJAutoProxy()
public class Main implements LoadTimeWeavingConfigurer{
public static void main(String[] args){
......
}
}
Pom file :
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-instrument -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.1</version>
</dependency>
When I call the service http://localhost:8080/person - GET
getpersonInfo() is only Logged in this case, I have also tried
LTW , but do not solve
I need to log all internal methods to the service like is getValidPerson(),isPersonValid() mentioning all arguments to those invoked method.
I am trying to call "ServiceB" from "ServiceA", both of the services are resource server, I am trying to make this inter service call through "Feign Client and OAuth2 toke" which is working fine with the below bean implmentation in Configuration class:
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication()
.getDetails();
requestTemplate.header("Authorization",
"bearer " + details.getTokenValue());
}
};
}
When I am trying to use Feign client with fallback i.e. Hystrix without OAuth token(i.e. when none of services are Resource Server) that is also working fine.
But while trying to use three of these (i.e. Feignclient, Hystrix and OAuth2)all together, it is not working. Every time it is going to fallback method though all the services are up and running.
Below are my code:
App.java
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import feign.RequestInterceptor;
import feign.RequestTemplate;
#SpringBootApplication
#RestController
#EnableFeignClients
#EnableEurekaClient
#EnableCircuitBreaker
public class App
{
/*#Autowired
#Qualifier("abc")
private GitHubClient gitHub;*/
#Autowired
private CallService callService;
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
#RequestMapping(value="/feign",method=RequestMethod.POST,consumes="application/json")
public String contributors1(#RequestBody JSONObject payLoad) {
String callservice2 = callService.callservice(payLoad);
return callservice2;
}
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)
SecurityContextHolder.getContext().getAuthentication()
.getDetails();
requestTemplate.header("Authorization",
"bearer " + details.getTokenValue());
}
};
}
}
Callervice.java
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
#Service
public class CallService {
#Autowired
#Qualifier("abc")
private GitHubClient gitHub;
public String callservice(JSONObject payLoad){
String forObject =gitHub.contributors(payLoad);
return forObject;
}
}
HystrixWrappedClient.java
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
#Component("abc")
public class HystrixWrappedClient implements GitHubClient{
#Autowired
#Qualifier("gitHubClient")
private GitHubClient gitHub;
#Override
#HystrixCommand(fallbackMethod="failure")
public String contributors(JSONObject payLoad) {
return gitHub.contributors(payLoad);
}
public String failure(JSONObject payLoad){
System.out.println(payLoad);
return "Failure";
}
}
GitHubClient.java
import org.json.simple.JSONObject;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#FeignClient("MockRestService")
interface GitHubClient {
#RequestMapping(method = RequestMethod.POST, value = "/test",consumes="application/json")
String contributors(#RequestBody JSONObject payLoad);
}
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
<!-- For swagger documenation -->
<dependency>
<groupId>com.mangofactory</groupId>
<artifactId>swagger-springmvc</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<!-- NEW -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>1.0.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Please suggest. Whenever trying to use Feignclient, OAuth2 and Hystrix all together it always going to Fallback method.
Did you shared Spring Security context with Hystrix in some way?
Since Spring Cloud Netflix 1.2.0 you can enable sharing of security context with Hystrix using i.e config param
hystrix.shareSecurityContext: true
u can look into this http://kodgemisi.com/2017/02/use-securitycontext-feign-requestinterceptor/
Hystrix execute in another/different thread, transfer your security context by HystrixRequestVariableDefault