【问题标题】:Using Spring's Cachable annotation on methods that returns Hibernate objects在返回 Hibernate 对象的方法上使用 Spring Cacheable 注解
【发布时间】:2015-08-20 05:57:34
【问题描述】:

我昨天设置了 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_AbstractDependencyInjectionAspect$2$1ea6722c(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$001(SimpleMessageListenerContainer.java:82)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.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.access$700(SimpleMessageListenerContainer.java:82)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1103)
    at java.lang.Thread.run(Thread.java:744)

如果有帮助,这里有一个 Session 类的 sn-p,它是由 Spring Roo 生成的,然后使用 Push-In 重构,我去掉了所有的 AspectJ,所以现在它只是一个 Java 类:

@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 类上进行缓存?

如果不可能,那么在没有方面的情况下使它工作的过程是什么(假设它可以缓存)?

工作解决方案

不得不创建一个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);
    }
}

必须将 Session 类修改为可序列化并返回 Roo 注释。

@Entity
@RooJavaBean
@RooToString
@Configurable
@RooJpaActiveRecord
public class Session implements Serializable {
   ...
}

然后使用我的自动装配会话服务:

@Autowired
SessionService sessionService;

我现在可以使用缓存了:

sessionService.findSessionByUserName(...)

【问题讨论】:

  • 您是否尝试优化检索当前登录用户的方式,我遇到了同样的问题。我是这样做的,以防万一:stackoverflow.com/questions/31851337/…
  • 如果我使用 Spring Security 并且安全检查由 Spring Security 完成,您的解决方案将有效。我正在尝试解决缓存休眠对象的问题,因为我需要缓存其他每分钟被命中 10000 次的对象
  • 哦...好吧... Redis 似乎是一个不错的选择,而不是这个...我是这么认为的..
  • 从长远来看,我会将这些表移动到 Redis DB,从短期来看,我会尝试消除一些瓶颈。由于我已经实现了 Memcached 来缓存某些 API 调用,所以我不妨捎带该基础设施。
  • 检查这个 pastebin,我已经发布了你想要的一切,如果你有疑问,我可以在午休后 1 小时内为你提供帮助。 :pastebin.com/1VCcpi1Q

标签: java spring hibernate caching spring-roo


【解决方案1】:

问题是您正在尝试将缓存添加到静态方法(在 Roo ActiveRecord 模式中完成的方式)。您必须在 bean 方法(非静态)上配置缓存层。

一种方法可以是使用 Roo 生成在实体层和 Web 层之间创建一个服务层(查看Roo documentation)。此实用程序创建调用 Roo 实体方法并修改 Contollers 方法以使用它的 Spring beans。因此,您可以使用 @Cacheable 注释此 Service-Bean 的方法,以便在需要时使用缓存。

祝你好运!

【讨论】:

  • 所以你的意思是我应该将静态调用包装在一个 Bean 中,然后我自动装配然后使用 Autowired Bean 而不是静态方法,然后将 Cachable 注释移动到 Autowired Bean ?
  • Yes... 如果您使用 Roo 命令 service,Roo 会修改控制器的 .aj 文件以使用新的 service,但前提是您将实体留在Roo 方式(取消注释 Roo 注释)
  • 现在只需要进一步修改它,添加 @RooService 注释后,让它工作起来相当简单。发布我的解决方案以防其他人陷入困境。感谢@jmvivo 的精彩回答
猜你喜欢
  • 2013-06-26
  • 2019-01-30
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多