Spring Data JPA - dependency injection of concrete entity class - spring

We are migrating from MyBatis over to Spring Data JPA (using hibernate). Originally, the Spring configuration specified a util:map of specific domain level objects as values that were referenced by an enum key. Thus the map was injected in to a service level class. This map was then used to get the domain level object based on a specific enum during the process flow. Now we have refactored the service layer to use Spring Data enabled #Entity domain level objects, when we try and get an instance of the entity using the enum key, it returns a proxy (as you would expect). As such, when we try and make use of the returned instance we get a ClassCastException (Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy43 cannot be cast to com.ourpackage.Event). My question is: How can I inject an #Entity class in to a util:map configuration so further properties can be set?
Here is the configuration of the map of entity objects and enum lookup keys:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<!--*********************************************************************-->
<!-- Event bean definitions -->
<!--*********************************************************************-->
<bean id="workEvent" class="com.ourpackage.Event">
<constructor-arg name="action" type="java.lang.String" value="action"/>
<constructor-arg name="type" type="java.lang.String" value="type"/>
<constructor-arg name="description" type="java.lang.String" value="A description"/>
</bean>
<!--*********************************************************************-->
<!-- Event Maps -->
<!--*********************************************************************-->
<util:map id="workEvents" map-class="java.util.HashMap" key-type="com.anotherpackage.EventType" value-type="com.ourpackage.Event">
<entry>
<key><value type="com.anotherpackage.EventType">WORK_ITEM</value></key>
<ref local="workEvent"/>
</entry>
</util:map>
</beans>
And here is the entity domain class definition:
package com.ourpackage;
import static javax.persistence.GenerationType.IDENTITY;
import static javax.persistence.TemporalType.TIMESTAMP;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.transaction.annotation.Transactional;
import com.basepackage.ServiceEntity;
/**
* The Class Event.
*
*/
#Entity
#Table(name = "events")
#Transactional(readOnly = true)
public class Event extends ServiceEntity {
....
}
Here is the code from the service class making use of this map:
// The following line produces the ClassCastException
workEvent = workEvents.get(EventType.WORK_ITEM);
This can of course be resolved by scrapping the dependency injection configuration and simply constructing the Event class on the fly, however configuration is the preferred approach.
Any help would be greatly appreciated.

The problem is the concrete class is no longer com.ourpackage.Event, because Spring wrapped in it an proxy of type com.sun.proxy.$Proxy43, and it's not possible to cast a proxy to an Event because they are two completely different types.
To solve this, make event implement an interface and cast to that interface instead of casting to a concrete class.
In general it is a generally good practice to avoid casting to concrete types in applications that heavily use proxying/AOP (like Spring/Hibernate applications).

Related

#PostConstruct spring does not get called without bean declaration

Why post construct does not get called without putting bean in applicationContext.xml
Here is my class which contains #PostConstruct annotation.
package org.stalwartz.config;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Below is my applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.directwebremoting.org/schema/spring-dwr
http://www.directwebremoting.org/schema/spring-dwr/spring-dwr-3.0.xsd">
<dwr:annotation-config />
<dwr:annotation-scan base-package="org.stalwartz" scanDataTransferObject="true" scanRemoteProxy="true" />
<dwr:url-mapping />
<!-- <bean id="proeprtyLoader" class="org.stalwartz.config.PropertyLoader"></bean> -->
<dwr:controller id="dwrController" debug="false">
<dwr:config-param name="activeReverseAjaxEnabled" value="true" />
</dwr:controller>
<context:annotation-config>
<context:component-scan base-package="org.stalwartz" annotation-config="true"></context:component-scan>
</context:annotation-config>
<mvc:annotation-driven />
...
...
...
</beans>
Looks simple, but it does not work without uncommenting bean declaration.
In Spring environment initialization callback method (the one annotated by #PostConstruct) make sense only on spring-managed-beans. To make instance(s) of your PropertyLoader class managed, you must do one of the following:
Explicitly register your class in context configuration (as you did)
<bean id="proeprtyLoader" class="org.stalwartz.config.PropertyLoader"></bean>
Let component scanning do the work (as you nearly did), but classes must be annotated by one of #Component, #Repository, #Service, #Controller.
Note from Spring documentation: The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. There is usually no need to include the <context:annotation-config> element when using <context:component-scan>.
Because putting bean in applicationContext.xml you are adding bean to Spring container, which has interceptor for this annotation. When Spring inject beans it checks #PostConstruct annotation, between others.
When you call simple new PropertyLoader() JVM will not search for the #PostConstruct annotation.
From doc of #PostConstruct annotation:
The PostConstruct annotation is used on a method that needs to be executed
after dependency injection is done to perform any initialization. This
method MUST be invoked before the class is put into service. This
annotation MUST be supported on all classes that support dependency
injection. The method annotated with PostConstruct MUST be invoked even
if the class does not request any resources to be injected.
Singleton is a scope annotation. It can be used to declare 'singletone' scope for a particular bean, but not instantiate it. See this article.
If you want to instantiate your class as singleton you can try Spring Service annotation.
#Service
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Also, you can replace annotation-config tag with component-scan. Here is a good article about differences of annotation-config and component-scan tags.
you are using #Singleton from javax.inject package which is not picked up as bean by spring container. Change it to :
package org.stalwartz.config;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
and the spring will auto detect PropertyLoader and will include it in Spring container as bean via the #Component annotation and this bean will be with singleton scope
by default a bean is singleton scoped in Spring, and #PostConstruct is usually used for service beans and service beans must scoped prototype and here because you need multiple objects for that particular class, Spring will provide you singleton instance.
also by doing this spring will attempt multiple times to find this service bean and finally throws below exception:
java.lang.NoClassDefFoundError: Could not initialize class org.springframework.beans.factory.BeanCreationException
so try like this in annotation way:
package org.stalwartz.config;
import javax.annotation.PostConstruct;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope("prototype") //you have to make it prototype explicitly
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Now every thing is good, and work fine for you.
add this dependency to pom.xml
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>

Can I use Hibernate Criteria without mapping with Hibernate?

I am using JPA annotations to map entities in my model. However, I found Hibernate Criteria is easy to use and contains less codes to write, so is there some way to use Criteria without mapping with hibernate xml ways? I tried this in my DAO implementation class:
private SessionFactory sFactory; // of type org.hibernate.SessionFactory
....
Session session = sFactory.getCurrentSession();
Criteria criteria = session.createCriteria(BTerminal.class);
But, without hibernate.cfg.xml it's giving nullpointerexception. Of course because it is not injected. But to fill this cfg.xml I have to add mapping xml files, which is not the way I like. So, can I use JPA mapping while using Hibernate Criteria?
I am not using Spring. Still doubt which is easier: write 10+ mapping xmls with all atributes, or to learn more about Spring DaoSupport, or any other ways.
Thanks in advance.
Yes, it will work. You can have JPA annotated entities, while you use Hibernate Criteria to query your entities, instead of JPA Criteria.
I have actually have tested it.
My entity class looks like this:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class TestEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Version
private Long version;
...
}
Then, I have Hibernate config file: hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/test</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="hbm2ddl.auto">create</property>
<property name="show_sql">true</property>
<mapping class="com.test.model.TestEntity" />
</session-factory>
</hibernate-configuration>
Notice, that I still have to list down the entity classes, but I'm not using Hibernate mapping files (hbm.xml). I don't think that Hibernate has support for auto-detection of entity classes, like JPA does (so you still have to list them down even if they are annotated).
Then I have this code as a test, persist entity then retrieve using Hibernate Criteria:
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
TestEntity testEntity = new TestEntity();
testEntity.setName("test");
session.save(testEntity);
List<TestEntity> tests = (List<TestEntity>) session.createCriteria(TestEntity.class).list();
for (TestEntity test : tests) {
System.out.println(test.getName());
}
session.getTransaction().commit();
I have the ff. output in my console:
Hibernate: insert into TestEntity (name, version) values (?, ?)
Hibernate: select this_.id as id1_0_0_, this_.name as name2_0_0_, this_.version as version3_0_0_ from TestEntity this_
test

#Autowired - No qualifying bean of type found for dependency at least 1 bean

Currently I'm facing an issue in Autowire configuration between controller and the service layer.
I'm unable to trace my mistakes.
Simple Log Info
SEVERE: Exception while loading the app
SEVERE: Undeployment failed for context /OTT
SEVERE: Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: org.apache.catalina.LifecycleException: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.ott.service.EmployeeService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
Below I have also given the Controller and Service Layer code and also the dispatcher-servlet.xml
Controller
package com.ott.controller;
import com.ott.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
* #author SPAR
*/
#Controller
public class AdminController {
private EmployeeService employeeService;
#RequestMapping("/employee")
public String employee(){
this.employeeService.fetchAll();
return "employee";
}
#Autowired(required = true)
#Qualifier(value="employeeService")
public void setEmployeeService(EmployeeService empService) {
this.employeeService = empService;
}
}
Service Interface
package com.ott.service;
import com.ott.hibernate.Employee;
import java.util.List;
/**
*
* #author SPAR
*/
public interface EmployeeService {
List<Employee> fetchAll();
}
Service Interface Impl
package com.ott.service;
import com.ott.dao.EmployeeDAO;
import com.ott.hibernate.Employee;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
*
* #author SPAR
*/
#Service
public class EmployeeServiceImpl implements EmployeeService{
private EmployeeDAO employeeDAO;
#Override
#Transactional(readOnly = true)
public List<Employee> fetchAll() {
List<Employee> employees = employeeDAO.fetchAll();
for (Employee employee : employees) {
System.out.println("Name : "+employee.getFirst_Name() +" "+ employee.getLast_Name());
System.out.println("Email Id : "+employee.getEmail_Id());
}
return employees;
}
#Autowired(required = true)
#Qualifier(value="employeeDAO")
public void setEmployeeDAO(EmployeeDAO empDAO) {
this.employeeDAO = empDAO;
}
}
Dispatcher-servlet.xml
<?xml version='1.0' encoding='UTF-8' ?>
<!-- was: <?xml version="1.0" encoding="UTF-8"?> -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.ott.controller"/>
<context:component-scan base-package="com.ott.hibernate"/>
<context:component-scan base-package="com.ott.service"/>
<context:component-scan base-package="com.ott.dao"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<mvc:resources mapping="/resources/**" location="/resources/" />
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles-def/general-layout.xml</value>
</list>
</property>
</bean>
<bean id="viewResolverTiles" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
</bean>
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
Guys I found the issue
I just tried by adding the qualifier name in employee service finally it solved my issue.
#Service("employeeService")
public class EmployeeServiceImpl implements EmployeeService{
}
You don't have to necessarily provide name and Qualifier. If you set a name, that's the name with which the bean is registered in the context. If you don't provide a name for your service it will be registered as uncapitalized non-qualified class name based on BeanNameGenerator. So in your case the Implementation will be registered as employeeServiceImpl. So if you try to autowire with that name, it should resolve directly.
private EmployeeService employeeServiceImpl;
#RequestMapping("/employee")
public String employee() {
this.employeeService.fetchAll();
return "employee";
}
#Autowired(required = true)
public void setEmployeeService(EmployeeService employeeServiceImpl) {
this.employeeServiceImpl = employeeServiceImpl;
}
#Qualifier is used in case if there are more than one bean exists of same type and you want to autowire different implementation beans for various purposes.
I believe for #Service you have to add qualifier name like below :
#Service("employeeService") should solve your issue
or after #Service you should add #Qualifier annontion like below :
#Service
#Qualifier("employeeService")
In your controller class, just add #ComponentScan("package") annotation. In my case the package name is com.shoppingcart.So i wrote the code as #ComponentScan("com.shoppingcart") and it worked for me.
You forgot #Service annotation in your service class.
#Service: It tells that particular class is a Service to the client. Service class contains mainly business Logic. If you have more Service classes in a package than provide #Qualifier otherwise it should not require #Qualifier.
Case 1:
#Service("employeeService")
public class EmployeeServiceImpl implements EmployeeService{
}
Case2:
#Service
public class EmployeeServiceImpl implements EmployeeService{
}
both cases are working...
If you only have one bean of type EmployeeService, and the interface EmployeeService does not have other implementations, you can simply put "#Service" before the EmployeeServiceImpl and "#Autowire" before the setter method.
Otherwise, you should name the special bean like #Service("myspecial") and put "#autowire #Qualifier("myspecial") before the setter method.
Just add below annotation with qualifier name of service in service Implementation class:
#Service("employeeService")
#Transactional
public class EmployeeServiceImpl implements EmployeeService{
}
Missing the 'implements' keyword in the impl classes might also be the issue

Spring Inject PersistenceContext

Trying to implement a DDD architecture with aspect oriented tests that access a database and check if user exists, using AspectJ LTW...
Currently I am faces with two issues, I don't know if this class is being injected in a Spring context. I have tried to add the
//#RunWith(SpringJUnit4ClassRunner.class)
//#ContextConfiguration(locations = {"classpath*:EntityTest-context.xml"})
With no sucess. Here is the test that I am trying to run. If you notice I am creating the EntityManager on the #Before I don't know if this is a proper usage, because when I try to find the object that is created I get returned null.
package ienterprise.common.aspects;
import ienterprise.common.model.CompanyPosition;
import ienterprise.common.model.InternalUser;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;
import static org.junit.Assert.assertEquals;
import org.springframework.test.context.ContextConfiguration;
//#RunWith(SpringJUnit4ClassRunner.class)
//#ContextConfiguration(locations = {"classpath*:EntityTest-context.xml"})
public class EntityTest {
private static final Logger logger = LoggerFactory.getLogger(EntityTest.class);
// #PersistenceContext(unitName="mysql") // FIXME inject this in unit tests
private static EntityManager manager;
#Before
public void setUp(){
EntityManagerFactory mngFactory = Persistence.createEntityManagerFactory("mysql");
manager = mngFactory.createEntityManager();
}
#Test
public void createUser(){
InternalUser someGuy = new InternalUser();
someGuy.setName("Adam");
someGuy.setUser("Engineer");
someGuy.create();
logger.debug("created user: {}", someGuy);
//FIXME: Can't find the user in the database.
InternalUser foundUser = manager.find(InternalUser.class, 1L);
logger.debug("fetched user: {}",foundUser);
assertEquals( someGuy, foundUser);
}
}
Our context xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:load-time-weaver/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml"></property>
</bean>
</beans>
How can I remove the #Before and inject a PersistenceContext?
How can I make sure that my class has a Spring Context?
This is all new stuff for me, and I would appreciate some links to github repositories if there are any with this kind of Spring+Hibernate+AspectJ+JUnit setup.
Let me know if something is not clear or additional detail is necessary.

#Cacheable "annotation type not applicable to this kind of declaration"

I'm learning Spring and Data JPA. I have a problem with Ehcache. I want to cache the return value of one of my methods that returns some records from database. This is an exercise with Ehcache instance pre-configured (I assume). The problem is that I cannot use the annotation #Cacheable to mark my method as the method that its return value should be cached. I get an incompatible type compile error (required: boolean, found: String). Here is one of the classes in my service layer that I think I should put #Cacheable here (am I right?):
package wad.datatables.service;
import javax.persistence.Cacheable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import wad.datatables.domain.Book;
import wad.datatables.repository.BookRepository;
import wad.datatables.view.DataTablesResponse;
#Service
public class JpaDataTablesBookService implements DataTablesBookService {
#Autowired
private BookRepository bookRepository;
#Override
#Transactional(readOnly = true)
#Cacheable("books")
public DataTablesResponse getBooks(String queryString) {
Pageable pageable = new PageRequest(0, 10, Sort.Direction.ASC, "title");
Page<Book> page = bookRepository.findByTitleContaining(queryString, pageable);
DataTablesResponse response = new DataTablesResponse();
response.setTotalRecords(page.getTotalElements());
response.setTotalDisplayRecords(page.getNumberOfElements());
response.setData(page.getContent());
return response;
}
}
And my repository layer (only one class):
package wad.datatables.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import wad.datatables.domain.Book;
public interface BookRepository extends JpaRepository<Book, Long> {
Page<Book> findByTitleContaining(String title, Pageable pageable);
}
And here are my config files:
cache.xml (located in WEB-INF/spring/):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven cache-manager="cacheManager" />
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache"/>
</bean>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
</bean>
</beans>
And ehcache.xml (located in src/main/resources):
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true"
monitoring="autodetect"
dynamicConfig="true">
<cache name="books" maxEntriesLocalHeap="1000" eternal="true" memoryStoreEvictionPolicy="LRU"/>
</ehcache>
The error is because you are using wrong Cacheable annotation. Instead of javax.persistence.Cacheable use org.springframework.cache.annotation.Cacheable.

Resources