在 returns Hibernate 对象的方法上使用 Spring 的 Cachable 注释
Using Spring's Cachable annotation on methods that returns Hibernate objects
我昨天设置了 Memcached 并最终得到 Spring @Cachable
注释工作...
这段代码缓存完美:
@Component("CacheProcessor")
public class CacheProcessor {
@Cacheable(value = "defaultCache", key="'dateTime-'.concat(#anything)")
public String getDateTime2(String anything) {
Date d = new Date();
String response = Long.toString(d.getTime());
return response;
}
}
我尝试了为每个请求调用的缓存方法:
@org.springframework.cache.annotation.Cacheable(value="defaultCache", key="username")
public static List<Session> findSessionByUserName(String username) {
String hql = "SELECT o FROM Session AS o WHERE o.username=:username";
TypedQuery<Session> query = Session.entityManager().createQuery(hql, Session.class);
query.setParameter("username", username);
return query.getResultList();
}
...但我收到的是堆栈跟踪而不是缓存结果:
java.lang.IllegalStateException: Post-processor tried to replace bean instance of type [com.ahp.core.model.Session] with (proxy) object of type [com.sun.proxy.$Proxy66] - not supported for aspect-configured classes!
at org.springframework.beans.factory.wiring.BeanConfigurerSupport.checkExposedObject(BeanConfigurerSupport.java:168)
at org.springframework.beans.factory.wiring.BeanConfigurerSupport.configureBean(BeanConfigurerSupport.java:140)
at org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect.configureBean(AnnotationBeanConfigurerAspect.aj:60)
at org.springframework.beans.factory.aspectj.AbstractDependencyInjectionAspect.ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspectea6722c(AbstractDependencyInjectionAspect.aj:91)
at com.ahp.core.model.Session.<init>(Session.java:20)
at com.ahp.core.model.Session.entityManager_aroundBody0(Session.java:57)
at com.ahp.core.model.Session.entityManager(Session.java:1)
at com.ahp.core.processor.AccountProcessor.validateSession(AccountProcessor.java:545)
at com.ahp.core.processor.WarehouseProcessor.consume(WarehouseProcessor.java:93)
at com.ahp.core.processor.WarehouseProcessor.consume(WarehouseProcessor.java:1)
at com.ahp.messaging.processor.AbstractRPCConsumer.onMessage(AbstractRPCConsumer.java:32)
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:228)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:756)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:679)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access[=14=]1(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:167)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1241)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:660)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1005)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:989)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access0(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1103)
at java.lang.Thread.run(Thread.java:744)
如果有帮助,这里是 Session class 的一个片段,它是由 Spring Roo 生成的,然后使用 Push-In 重构,我摆脱了所有 AspectJ,所以现在它是只有一个 Java class:
@Entity
@Configurable
//@RooJavaBean
//@RooToString
//@RooJpaActiveRecord
public class Session {
@PersistenceContext
transient EntityManager entityManager;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
@Version
@Column(name = "version")
private Integer version;
@ManyToOne
private Account account;
@ManyToOne
private Company company;
@Column(name = "qusername")
private String username;
....
如果我删除 Configurable
注释,则不会注入实体管理器并抛出 IllegalStateException
。
public static final EntityManager entityManager() {
EntityManager em = new Session().entityManager;
if (em == null)
throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
return em;
}
如何让缓存在 aspect-configured
classes 上工作?
如果这不可能,让它在没有方面的情况下工作的过程是什么(假设这会使它可缓存)?
工作解决方案
必须创建 SessionService 接口:
@RooService(domainTypes = {Session.class })
public interface SessionService {
public List<Session> findSessionByUserName(String username);
}
SessionServiceImpl:
@Component
public class SessionServiceImpl implements SessionService {
@Override
@Cacheable(value="defaultCache", key="#username")
public List<Session> findSessionByUserName(String username) {
return Session.findSessionByUserName(username);
}
}
必须将会话 class 修改为可序列化并返回 Roo 注释。
@Entity
@RooJavaBean
@RooToString
@Configurable
@RooJpaActiveRecord
public class Session implements Serializable {
...
}
然后使用我自动装配的会话服务:
@Autowired
SessionService sessionService;
我现在可以使用缓存了:
sessionService.findSessionByUserName(...)
问题是您正在尝试将缓存添加到静态方法(在 Roo ActiveRecord 模式中完成的方式)。您必须在 bean 方法(非静态)上配置缓存层。
一种方法是使用 Roo 生成在实体层和 Web 层之间创建一个服务层(查看 Roo documentation)。此实用程序创建 Spring 个调用 Roo 实体方法并修改 Contollers 方法以使用它的 bean。所以你可以用@Cacheable
注解这个Service-Bean的方法,在你需要的地方使用缓存。
祝你好运!
我昨天设置了 Memcached 并最终得到 Spring @Cachable
注释工作...
这段代码缓存完美:
@Component("CacheProcessor")
public class CacheProcessor {
@Cacheable(value = "defaultCache", key="'dateTime-'.concat(#anything)")
public String getDateTime2(String anything) {
Date d = new Date();
String response = Long.toString(d.getTime());
return response;
}
}
我尝试了为每个请求调用的缓存方法:
@org.springframework.cache.annotation.Cacheable(value="defaultCache", key="username")
public static List<Session> findSessionByUserName(String username) {
String hql = "SELECT o FROM Session AS o WHERE o.username=:username";
TypedQuery<Session> query = Session.entityManager().createQuery(hql, Session.class);
query.setParameter("username", username);
return query.getResultList();
}
...但我收到的是堆栈跟踪而不是缓存结果:
java.lang.IllegalStateException: Post-processor tried to replace bean instance of type [com.ahp.core.model.Session] with (proxy) object of type [com.sun.proxy.$Proxy66] - not supported for aspect-configured classes!
at org.springframework.beans.factory.wiring.BeanConfigurerSupport.checkExposedObject(BeanConfigurerSupport.java:168)
at org.springframework.beans.factory.wiring.BeanConfigurerSupport.configureBean(BeanConfigurerSupport.java:140)
at org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect.configureBean(AnnotationBeanConfigurerAspect.aj:60)
at org.springframework.beans.factory.aspectj.AbstractDependencyInjectionAspect.ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspectea6722c(AbstractDependencyInjectionAspect.aj:91)
at com.ahp.core.model.Session.<init>(Session.java:20)
at com.ahp.core.model.Session.entityManager_aroundBody0(Session.java:57)
at com.ahp.core.model.Session.entityManager(Session.java:1)
at com.ahp.core.processor.AccountProcessor.validateSession(AccountProcessor.java:545)
at com.ahp.core.processor.WarehouseProcessor.consume(WarehouseProcessor.java:93)
at com.ahp.core.processor.WarehouseProcessor.consume(WarehouseProcessor.java:1)
at com.ahp.messaging.processor.AbstractRPCConsumer.onMessage(AbstractRPCConsumer.java:32)
at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:228)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:756)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:679)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access[=14=]1(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:167)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1241)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:660)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1005)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:989)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access0(SimpleMessageListenerContainer.java:82)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1103)
at java.lang.Thread.run(Thread.java:744)
如果有帮助,这里是 Session class 的一个片段,它是由 Spring Roo 生成的,然后使用 Push-In 重构,我摆脱了所有 AspectJ,所以现在它是只有一个 Java class:
@Entity
@Configurable
//@RooJavaBean
//@RooToString
//@RooJpaActiveRecord
public class Session {
@PersistenceContext
transient EntityManager entityManager;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
@Version
@Column(name = "version")
private Integer version;
@ManyToOne
private Account account;
@ManyToOne
private Company company;
@Column(name = "qusername")
private String username;
....
如果我删除 Configurable
注释,则不会注入实体管理器并抛出 IllegalStateException
。
public static final EntityManager entityManager() {
EntityManager em = new Session().entityManager;
if (em == null)
throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
return em;
}
如何让缓存在 aspect-configured
classes 上工作?
如果这不可能,让它在没有方面的情况下工作的过程是什么(假设这会使它可缓存)?
工作解决方案
必须创建 SessionService 接口:
@RooService(domainTypes = {Session.class })
public interface SessionService {
public List<Session> findSessionByUserName(String username);
}
SessionServiceImpl:
@Component
public class SessionServiceImpl implements SessionService {
@Override
@Cacheable(value="defaultCache", key="#username")
public List<Session> findSessionByUserName(String username) {
return Session.findSessionByUserName(username);
}
}
必须将会话 class 修改为可序列化并返回 Roo 注释。
@Entity
@RooJavaBean
@RooToString
@Configurable
@RooJpaActiveRecord
public class Session implements Serializable {
...
}
然后使用我自动装配的会话服务:
@Autowired
SessionService sessionService;
我现在可以使用缓存了:
sessionService.findSessionByUserName(...)
问题是您正在尝试将缓存添加到静态方法(在 Roo ActiveRecord 模式中完成的方式)。您必须在 bean 方法(非静态)上配置缓存层。
一种方法是使用 Roo 生成在实体层和 Web 层之间创建一个服务层(查看 Roo documentation)。此实用程序创建 Spring 个调用 Roo 实体方法并修改 Contollers 方法以使用它的 bean。所以你可以用@Cacheable
注解这个Service-Bean的方法,在你需要的地方使用缓存。
祝你好运!