I am new to caching world and I am trying to implement cache. I want to cache the output of a method which is calling a web service for some data in order to avoid calling the web service again and again for same data. But my cache is not working and my method is calling the web service again and again for the same data.
spring-server.xml
<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"/>
<property named = "shared" value="true"/>
</bean>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true"
monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir"/>
<cache name="loadstatus"
maxEntriesLocalHeap = "10000"
maxEntriesLocalDisk= "100000"
eternal = "false"
diskSpoolBufferSizeMB = "20"
timeToIdleSeconds="28800" timeToLiveSeconds="28800"
memoryStoreEvictionpolicy ="LFU"`enter code here`
transactionalMode ="off">
<persistance strategy = "localTempSwap"/>
</cache>
</ehcache>
service class
#Service
public class ServiceImplementation {
#Autowired
ClientImplementation webServiceClient;
#Cacheable(value = "loadstatus")
public List<GetLoadStatusResonseElement> getLoadStatus() throws Exception{
List<GetLoadStatusResonseElement> list = null;
list = webServiceClient.getLoadStatus();
return list;
}
}
PS: Data coming from web service into my method loadstatus need to be refreshed every 24 hr. But for the same day data will remain same, So for a particular day I want to cache the output of the web service.
Just specifying #Cacheable is not enough. You also need to tell Spring that is should look for those annotations.
Adding <cache:annotation-driven/> should fix it (and of course, you need to add the correspondig xsd)
Related
Note: See my own answer to this question for an example of how I solved this issue.
I am getting the following exception in my Spring MVC 4 + Hibernate 4 project:
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: com.mysite.Company.acknowledgements, could not
initialize proxy - no Session
After reading a lot of other questions about this issue, I understand why this exception occurs, but I am not sure how to fix it in a good way. I am doing the following:
My Spring MVC controller calls a method in a service
The service method invokes a method in a DAO class
The method in the DAO class fetches an entity object through Hibernate and returns it to the calling service
The service returns the fetched object to the controller
The controller passes the object to the view (JSP)
The view tries to iterate on a many-to-many association that is lazily loaded (and is thus a proxy object)
The exception is thrown because the session is closed at this point, and the proxy cannot load the associated data
I have previously worked with PHP and doctrine2, and this way of doing things caused no problems. I am trying to figure out the best way to solve this, because the solutions that I found so far don't seem so great:
Load the association eagerly, potentially loading lots of unnecessary data
Call Hibernate.initialize(myObject.getAssociation()); - this means that I have to loop through associations to initialize them (I guess), and just the fact that I have to do this, kind of makes the lazy loading less neat
Using a Spring filter to leave the session open in the views, but I doubt that this is a good thing to do?
I tried to use #Transactional in my service, but without luck. This makes sense because I am trying to access data that has not yet been loaded after my service method returns. Ideally I would like to be able to access any association from within my view. I guess the drawback of initializing the associations within my service is that I have to explicitly define which data I need - but this depends on the context (controller) in which the service is used. I am not sure if I can do this within my controller instead without losing the abstraction that the DBAL layer provides. I hope that makes sense. Either way, it would be great if I didn't have to always explicitly define which data I want to be available to my view, but just let the view do its thing. If that is not possible, then I am just looking for the most elegant solution to the problem.
Below is my code.
View
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1><c:out value="${company.name}" /> (ID: <c:out value="${company.id}" />)</h1>
<c:forEach var="acknowledgement" items="${company.acknowledgements}">
<p><c:out value="${acknowledgement.name}" /></p>
</c:forEach>
Controller
#Controller
public class ProfileController {
#Autowired
private CompanyService companyService;
#RequestMapping("/profile/view/{id}")
public String view(Model model, #PathVariable int id) {
Company company = this.companyService.get(id);
model.addAttribute("company", company);
return "viewCompanyProfile";
}
}
Service
#Service
public class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyDao companyDao;
#Override
public Company get(int id) {
return this.companyDao.get(id);
}
}
DAO
#Repository
#Transactional
public class CompanyDaoImpl implements CompanyDao {
#Autowired
private SessionFactory sessionFactory;
#Override
public Company get(int id) {
return (Company) this.sessionFactory.getCurrentSession().get(Company.class, id);
}
}
Company entity
#Entity
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
// Other fields here
#ManyToMany
#JoinTable(name = "company_acknowledgement", joinColumns = #JoinColumn(name = "company_id"), inverseJoinColumns = #JoinColumn(name = "acknowledgement_id"))
private Set<Acknowledgement> acknowledgements;
public Set<Acknowledgement> getAcknowledgements() {
return acknowledgements;
}
public void setAcknowledgements(Set<Acknowledgement> acknowledgements) {
this.acknowledgements = acknowledgements;
}
// Other getters and setters here
}
Acknowledgement entity
#Entity
public class Acknowledgement {
#Id
private int id;
// Other fields + getters and setters here
}
mvc-dispatcher-servlet.xml (a part of it)
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.mysite.company.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven />
Thanks in advance!
As #Predrag Maric said writing service methods with different initialization for entity assotiations might be an option.
OpenSessionInView is a quit discussable pattern, which can be a solution in your case, though.
Another option is to set
<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
property. It is designed to solve org.hibernate.LazyInitializationException problem and is available since hibernate 4.1.6.
So what does this property do? It simply signals Hibernate that it
should open a new session if session which is set inside current
un-initialised proxy is closed. You should be aware that if you have
any other open session which is also used to manage current
transaction, this newly opened session will be different and it may
not participate into the current transaction unless it is JTA. Because
of this TX side effect you should be careful against possible side
effects in your system.
Find more information here: http://blog.harezmi.com.tr/hibernates-new-feature-for-overcoming-frustrating-lazyinitializationexceptions and
Solve Hibernate Lazy-Init issue with hibernate.enable_lazy_load_no_trans
The simplest and most transparent solution is the OSIV pattern. As I'm sure you aware, there is plenty of discussion about this (anti)pattern and the alternatives both on this site and elsewhere so no need to go over that again. For example:
Why is Hibernate Open Session in View considered a bad practice?
However, in my opinion, not all criticisms of OSIV are entirely accurate (e.g. the transaction will not commit till your view is rendered? Really? If you are using the Spring implementation then https://stackoverflow.com/a/10664815/1356423)
Also, be aware that JPA 2.1 introduced the concept of Fetch Graphs which give you more control over what is loaded. I haven't tried it yet but maybe this if finally a solution for this long standing problem!
http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html
For others who are looking for a solution, here is how I solved the problem.
As Alan Hay pointed out, JPA 2.1+ supports entity graphs, which ended up solving my problem. To make use of it, I change my project to use the javax.persistence.EntityManager class instead of the SessionFactory, which would then hand me the current session. Here is how I configured it in my dispatcher servlet configuration (some things excluded):
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
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/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="java:/comp/env/jdbc/postgres" expected-type="javax.sql.DataSource"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="dk.better.company.entity, dk.better.user.entity" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
</beans>
Below is an example of a DAO class.
#Transactional
public class CompanyDaoImpl implements CompanyDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public Company get(int id) {
EntityGraph<Company> entityGraph = this.entityManager.createEntityGraph(Company.class);
entityGraph.addAttributeNodes("acknowledgements");
Map<String, Object> hints = new HashMap<String, Object>();
hints.put("javax.persistence.loadgraph", entityGraph);
return this.entityManager.find(Company.class, id, hints);
}
}
I found that if I did not use #PersistenceContext but Autowired instead, then the database session was not closed when rendering the view, and data updates were not reflected in subsequent queries.
For more information on entity graphs, I encourage you to read the following articles:
http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html
http://www.thoughts-on-java.org/2014/04/jpa-21-entity-graph-part-2-define.html
My vote goes to Hibernate.initialize(myObject.getAssociation()) in service layer (which also means #Transactional should be moved from DAO to service methods)
IMHO, service methods should return all data that is required by the caller. If the caller wants to display some association on the view, it is service's responsibility to supply that data. This means you could have several methods that apparently do the same thing (return Company instance), but depending on the caller, different associations could be fetched.
On one project we had kind of configuration class, which contained information on what associations should be fetched, and we had a single service method which also accepted that class as a parameter. This approach meant we have only one method which is flexible enough to support all callers.
#Override
public Company get(int id, FetchConfig fc) {
Company result = this.companyDao.get(id);
if (fc.isFetchAssociation1()) {
Hibernate.initialize(result.getAssociation1());
}
...
return result;
}
Trigger in the service layer requires lazy loading of the Set's size method.
Tools:
public class HibernateUtil {
/**
* Lazy = true when the trigger size method is equal to lazy = false (load all attached)
*/
public static void triggerSize(Collection collection) {
if (collection != null) {
collection.size();
}
}
}
in your service method:
Apple apple = appleDao.findById('xxx');
HibernateUtil.triggerSize(apple.getColorSet());
return apple;
then use apple in controller, everything is ok!
I was working with a web application using the Spring framework version 3.0.2 along with Hibernate (NetBeans 6.9.1). Later I came to know that there was one of bugs that was causing problems in uploading multiple files as mentioned in my one of previous questions.
I have finished struggling to acquire the solution but couldn't succeed. Therefore, I upgraded the Spring version to 3.2.0.
With the earlier version (3.0.2), AJAX was working fine with Jackson 1.9.8 (its download page) but with the later version (3.2.0), everything works fine but AJAX calls alert an error everywhere in the JavaScript code.
There is a scenario at one place when one of the countries is selected in the country select box, the corresponding state list is retrieved from the Spring controller along with DAO. The method which is mapped with a URL in the Spring controller is as follows,
#RequestMapping(value="ajax/GetStateList", method=RequestMethod.GET)
public #ResponseBody List<Object[]> getStateSelectBox(HttpServletRequest request)
{
return cityService.getStateSelectBox(request.getParameter("countryId"));
}
This method is invoked when a country is selected in the country select box. The getStateSelectBox() method is defined in one of DAO classes as follows,
#Service
#Transactional(readOnly = true, propagation=Propagation.REQUIRES_NEW)
public final class CityDAO implements CityService
{
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
#SuppressWarnings("unchecked")
public List<Object[]> getStateSelectBox(String id)
{
List<Object[]> list = sessionFactory.getCurrentSession()
.createQuery("select s.stateId, s.stateName from StateTable s where countryId.countryId=:id order by s.stateId")
.setParameter("id", Long.parseLong(id)).list();
for(Object[]o:list)
{
System.out.println(o[0]+" : "+o[1]);
}
return list;
}
}
The foreach loop is just for the sake of demonstration, it displays all the states along with their id that correspond to the countryId supplied by the AJAX request but the List is not returned to JSP.
The JavaScript code used to send this AJAX request alerts an error. It appears that there are some problems with JSON mapping. The same thing was working with the earlier version of the Spring framework (3.0.2). I'm not sure why does this cause problems with the higher version of Spring which is 3.2.0. Is there anything with the Spring version 3.2.0 which I might be missing?
If you needed to see the JavaScript code, the full JavaScript code to achieve this would be as follows.
function getStates(countryId)
{
if(countryId==""||countryId==null||countryId==undefined||isNaN(countryId))
{
var str="<select id='cmbStates' name='cmbStates' onchange='errorMessage(this.value);' class='validate[required] text-input'><option value=''>Select</option></select>";
$('#stateList').html(str);
alert("Please select an appropriate option.");
return;
}
var div=document.createElement("div");
div.id="temp";
document.body.appendChild(div);
$.ajax({
datatype:"json",
type: "GET",
contentType: "application/json",
url: "/wagafashion/ajax/GetStateList.htm",
data: "countryId=" + countryId+"&t="+new Date().getTime(),
success: function(response)
{
if(typeof response==='object'&&response instanceof Array)
{
var str="<select id='cmbState' name='cmbState' onchange='errorMessage(this.value);' class='validate[required] text-input'><option value=''>Select</option>";
var l=response.length;
for(var i=0;i<l;i++)
{
str+="<option value='"+response[i][0]+"'>"+$('#temp').text(response[i][1]).html()+"</option>";
}
str+="</select>";
$('#stateList').html(str); // select box is written to #stateList div
$('#temp').remove();
}
},
error: function(e)
{
alert('Error: ' + e);
}
});
}
To be sure, the Jackson library is on the classpath and I'm not getting any error or exception on the server side. The AJAX request succeeds and it goes to the DAO via Spring and the list of type List<Object[]> is retrieved from the database but it is not a response of JSON to JSP (which could/should be mapped to a JavaScript array). presumably, it appears that there is something missing with JSON mapping which was however not the case with the earlier version of Spring.
EDIT:
I have tried to parse List<Object[]> in both of the frameworks, 3.0.2 and 3.2.0 such as
List<Object[]> list = cityService.getStateSelectBox(request.getParameter("countryId"));
ObjectMapper objectMapper=new ObjectMapper();
try
{
objectMapper.writeValue(new File("E:/Project/SpringHibernet/wagafashionLatest/temp.json"), list);
}
catch (IOException ex){}
The file temp.json contains the following string.
[[21,"Gujarat"],[22,"Maharashtra"],[23,"Kerala"],[24,"New Delhi"]]
In both the cases (with both frameworks). So, it appears that the JSON response should be same in both the cases.
The temp.json file can also be deserialized as follows.
try
{
ObjectMapper mapper=new ObjectMapper();
List<Object[]> list = mapper.readValue(new File("E:/Project/SpringHibernet/wagafashionLatest/temp.json"), new TypeReference<List<Object[]>>() {});
for(Object[]o:list)
{
System.out.println(o[0]+" : "+o[1]);
}
}
catch (IOException ex)
{
}
It works fine and the foreach loop iterates over the List of type List<Object[]>. So, the problem might be caused by the Spring framework itself. What else is required, I'm not sure. Why is it not mapped by Jackson?
I have the same post on the Spring forum. The solution from the replies to the question which ultimately worked for me was required to configure the dispatcher-servlet.xml file as follows.
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="favorParameter" value="false" />
<property name="ignoreAcceptHeader" value="false" />
<property name="mediaTypes" >
<value>
atom=application/atom+xml
html=text/html
json=application/json
*=*/*
</value>
</property>
</bean>
This led JSON to work with Jackson 1.9.8 and Spring 3.2.0. This is credited to all the replies to that question.
My entire dispatcher-servlet.xml file now looks like the following.
<?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-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="controller" />
<context:component-scan base-package="validatorbeans" />
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="favorParameter" value="false" />
<property name="ignoreAcceptHeader" value="false" />
<property name="mediaTypes" >
<value>
atom=application/atom+xml
html=text/html
json=application/json
*=*/*
</value>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="index.htm">indexController</prop>
</props>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
<bean name="indexController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController"
p:viewName="index" />
</beans>
I'm using Spring 3.1.3 and I've found that the Jackson mapping attempts to create a root object in the response. This is with the Jackson2 mapper. I haven't tried with the older Jackson mapper. If you also upgraded Jackson you may be seeing the same issue.
In the past, an object array would be mapped something like
[{name:'name1',id:4},{name:'name2',id:6}]
Now I find that they are providing an auto-generated object name for the object, so it is returned something like
{ objectArray: [{name:'name1',id:4},{name:'name2',id:6}]}
So you need to reference response.objectArray[0] instead of being able to directly reference response[0]
In any case, it's probable that the JSON response has changed format somewhat. You should look at the new response and see what changes need to occur in the javascript to make it map the new structure.
I need to get a specific EhCache instance by name and I'd prefer to autowire if possible. Given the following automatically configured controller, how can I autowire in the cache instance I'm looking for?
#Controller
public class MyUniqueService {
...
}
<beans ...>
<ctx:component-scan base-package="my.controllers"/>
<mvc:annotation-driven />
</beans>
How do I configure EhCache in my application context? I don't see any log messages from EhCache about it loading the ehcache.xml file in my /WEB-INF/ directory. How do I make it load it?
How can I integrate EhCache with my Spring application to have it load the ehcache.xml file from my /WEB-INF/ directory and autowire a cache by a given name into my MyUniqueService controller?
First you need to create a Ehcache CacheManager singleton in you app context like this:
<bean id="myEhCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:my-ehcache.xml"/>
</bean>
Here configLocation is set to load from classpath or use value="/WEB-INF/my-ehcache.xml".
In your controller simply inject the CacheManager instance:
#Controller
public class MyUniqueService {
#Resource(name="myEhCacheManager")
private CacheManager cacheManager;
...
}
Alternatively, if you'd like to go the "entirely autowired" route, do:
<bean class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="/WEB-INF/ehcache.xml"/>
</bean>
</property>
</bean>
Setup your class like so:
#Controller
public class MyUniqueService {
#Autowired
private org.springframework.cache.CacheManager cacheManager;
public org.springframework.cache.Cache getUniqueObjectCache() {
return cacheManager.getCache("uniqueObjectCache");
}
}
uniqueObjectCache corresponds to this cache instance in your ehcache.xml cache definition:
<cache name="uniqueObjectCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"
transactionalMode="off"/>
There isn't a way to inject an actual cache instance, but as shown above, you can inject a cache manager and use it to get the cache you're interested in.
Assuming you have cacheManager defined:
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:/ehcache.xml"/>
</bean>
You can get/inject specific cache like this:
#Value("#{cacheManager.getCache('myCacheName')}")
private Cache myCache;
See also examples how to use Spring EL inside the #Value() http://www.mkyong.com/spring3/spring-el-method-invocation-example/ if you are interested.
You can also use autowire if the context can find a bean with the correct class. Here is how I configured my xml
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>WEB-INF/ehcache.xml</value>
</property>
</bean>
<bean id="cache" class="net.sf.ehcache.Cache" factory-bean="cacheManager" factory-method="getCache">
<constructor-arg value="CacheNameHere" />
</bean>
And my java class
#Autowired
private net.sf.ehcache.Cache cache;
This setup works for me.
Indeed! Or if you want to use a java config class:
#Inject
private ResourceLoader resourceLoader;
#Bean
public CacheManager cacheManager() {
EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
try {
ehCacheCacheManager.setCacheManager(ehcacheCacheManager().getObject());
} catch (Exception e) {
throw new IllegalStateException("Failed to create an EhCacheManagerFactoryBean", e);
}
return ehCacheCacheManager;
}
#Bean
public FactoryBean<net.sf.ehcache.CacheManager> ehcacheCacheManager() {
EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
bean.setConfigLocation(resourceLoader.getResource("classpath:ehcache.xml"));
return bean;
}
I testing my DAO, but it didn't work. The following error occurs:
Tests in error:
testAccountOperations(com.tsekhan.rssreader.dao.HibernateControllerTest): Error creating bean with name 'com.tsekhan.rssreader.dao.HibernateControllerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.tsekhan.rssreader.dao.HibernateController com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController; nested exception is java.lang.IllegalArgumentException: Can not set com.tsekhan.rssreader.dao.HibernateController field com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController to $Proxy25
My DAO:
#Service
#Scope("singleton")
public class HibernateController extends HibernateDaoSupport {
#Autowired
public SessionFactory sessionFactory;
#Transactional
public void addAcount(Account account) {
sessionFactory.getCurrentSession().saveOrUpdate(account);
}
}
My test for this DAO:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:/applicationContext.xml")
public class HibernateControllerTest {
#Autowired
HibernateController hibernateController;
private Set<Channel> getTestChannelList(String channelLink) {
Channel testChannel = new Channel();
testChannel.setSourceLink(channelLink);
Set<Channel> testChannelList = new HashSet<Channel>();
testChannelList.add(testChannel);
return testChannelList;
}
private Account getTestAccount(String accountLogin, String channelLink) {
Account testAccount = new Account();
testAccount.setAccountLogin(accountLogin);
testAccount.setChannelList(getTestChannelList(channelLink));
return testAccount;
}
#Test
public void testAccountOperations() {
hibernateController
.addAcount(getTestAccount("test_login", "test_link"));
}
}
My applicationContext.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd"
default-autowire="byName">
<!-- Enabling spring-transaction annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Enabling annotation-driven configurating -->
<context:annotation-config />
<!-- Creation of transaction manager -->
<bean id="transactionManager" scope="singleton"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" scope="singleton"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:/hibernate.cfg.xml"/>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
</bean>
<!--
A Spring interceptor that takes care of Hibernate session lifecycle.
-->
<bean id="hibernateInterceptor"
class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean name="employeeDAO" scope="prototype"
class="com.tsekhan.rssreader.dao.HibernateController" />
<!-- Searching for hibernate POJO files in package com.tsekhan.rssreader.web -->
<context:component-scan base-package="com.tsekhan.rssreader.web" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
</beans>
I note, that if you comment #Transactional in DAO, bean is created correctly. What happens?
First of all its realy bad to give name ending in Controller to a DAO its very confusing, Controller and DAO have all together different purpose.
When you add #Transactional to a service or dao class, for spring to make it work in a transaction needs to create a proxy of that class, its a kind of wrapper where in before the execution of proxied class(class in consideration which is proxied) method spring starts the transaction and after the execution in case no exceptions completes the transaction, this can be done in spring via AOP and Annotations. To describe in code.
public class OriginalDaoImpl implements OriginalDao extends DaoSupport {
public void save(Object o){
manager.save(o);
}
}
public class ProxyDaoImpl implements OriginalDao {
private OriginalDao originalDaoImpl; //instance of OriginalDaoImpl
public void save(Object o){
try{
transaction.start();
originalDaoImpl.save(o);
transaction.commit();
}catch(Exception e){
transaction.rollback();
}finally{
//clean up code
}
}
}
As you see this is not an exact implementation but a foundation code, how transaction magically works for you. The key point is there interface OriginalDao which makes this injection easy as OriginalDaoImpl and ProxyDaoImpl both implement same interface. Hence they can be swapped i.e. proxy taking place of original. This dynamic proxy can be created in java by Java dynamic proxy. Now, the question what if your class is not implementing an interface, it gets harder for the replacement to happen.
One of the libraries CGLIB as far as I know, helps in such a scenario, whereby it generates a dynamic subclass for the class in consideration and in overriden method performs the magic as described above, by calling super.save(o) to delegate to original code.
Now to the problem of injection.
Create interface and make your dao implement that and spring will default to JDK proxy as it is behaving now.
Add proxy-target-class="true" attribute to <tx:annotation-driven transaction-manager="transactionManager"/>
As far as exception is concerned it is throwing as it is expecting injected bean to be of type 'HibernateController' but its not.
For you reference you can refer links below.
10.5.6 Using #Transactional
Spring AOP Doc
Hope this helps !!!!!.
If your are using Spring MVC make sure to scan specific controller classes alone in servlet context file. Otherwise it will scan 2 times and transaction is not available on the application context.
The Google Code site of Morphia says it "works great with Guice, Spring, and other DI frameworks."
I'm learning Spring at the moment, so i'm just experimenting with connecting these two tools.
I've created a User POJO to store user objects with Morphia in MongoDB. I've also created a UserDAO class extending BasicDAO from Morphia to access objects.
My Spring application context configuration XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
xmlns:aop="http://www.springframework.org/schema/aop">
<bean class="java.lang.String" id="mongoDb">
<constructor-arg value="test"/>
</bean>
<bean class="com.google.code.morphia.Morphia" id="morphia" />
<bean class="com.mongodb.Mongo" id="mongo"/>
<bean class="hu.inagy.testspring.daos.UserDAO" id="userDao">
<constructor-arg ref="morphia" index="0" />
<constructor-arg ref="mongo" index="1" />
<constructor-arg ref="mongoDb" index="2" />
</bean>
</beans>
I have a simple main class to test functionality:
public class App
{
public static void main( String[] args )
{
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/app-context.xml");
UserDAO userDao = (UserDAO) applicationContext.getBean("userDao");
userDao.deleteByQuery(userDao.createQuery());
User user = new User();
user.setName("Test");
userDao.save(user);
User ret = userDao.find().get();
System.out.println("Saved user is: "+ret);
}
}
This works fine, however i don't know if i did everything as it should be. For example i haven't called ensureIndexes() and ensureCaps() on the datastore. My code also doesn't have an explicit mapping call for the POJOs on the Morphia object.
Are these done for me automatically or should i do other things to use Morphia correctly with Spring?
I don't use spring but this articles seems to talk about exactly what you need, a hook to do things when you app starts: http://leshazlewood.com/2007/07/30/spring-application-bootstrap-data/
You can do the Datastore.ensureIndexes/Caps() there.
You can also read this thread about using #Autowire and annotations instead of the xml, if you like that stuff.
http://groups.google.com/group/morphia/browse_thread/thread/1013b17963f29468