集成Spring JPA Data和Spring Cache时的奇怪行为

时间:2016-01-10 14:24:11

标签: spring spring-data-jpa spring-data spring-cache

当我整合Spring JPA Data和Spring Cache时,有一种我无法解释的奇怪行为。

我正在使用Spring Boot来设置我的演示项目。代码如下。

我的配置bean:

@Configuration
public class AppConfig {

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("Person");
    }
}

我的实体bean。

@Entity
public class Person implements Serializable {

    private static final long serialVersionUID = 2263555241909370718L;

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

我的JPA界面。我从JpaRepository覆盖了一些方法并添加了@cachable注释。

public interface PersonRepository extends JpaRepository<Person, Long> {

    @Override
    @CacheEvict(value = "Person", allEntries = true)
    public void delete(Long id);

    @Cacheable("Person")
    public Person findByName(String name);

    @Override
    @Query("select p from Person p where p.id = :id + 1L")
    @Cacheable("Person")
    public Person findOne(@Param("id") Long id);
}

我的单元测试课

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringDataDemoApplication.class)
public class SpringDataDemoApplicationTests {

    @Autowired
    private PersonRepository personRepository;

    @Autowired
    private CacheManager cacheManager;

    @Before
    public void setup() {
        Person p1 = new Person();
        p1.setName("Chris");
        personRepository.save(p1);
    }

    @Test
    public void test2() {

        JpaRepository<Person, Long> jpaRepository = personRepository;

        Person p = personRepository.findOne(0L);
        Assert.assertNotNull(p);

        p = personRepository.findOne(0L);
        Assert.assertNotNull(p);

        System.err.println("---------------------------------------");

        p = jpaRepository.findOne(0L);
        Assert.assertNotNull(p);

        p = jpaRepository.findOne(0L);
        Assert.assertNotNull(p);
    }

}

输出很奇怪。

Hibernate: insert into person (id, name) values (default, ?)
Hibernate: select person0_.id as id1_0_, person0_.name as name2_0_ from person person0_ where person0_.id=?+1
---------------------------------------
Hibernate: select person0_.id as id1_0_, person0_.name as name2_0_ from person person0_ where person0_.id=?+1
Hibernate: select person0_.id as id1_0_, person0_.name as name2_0_ from person person0_ where person0_.id=?+1

它应该只为我的期望打印出一个sql语句。 jpaRepository.findOne(0L)没有使用缓存对象。

在将PersonRepository接口分配给其父接口JpaRepository之后,缓存注释无法正常工作。

这两个变量正好指向相同的引用,即使它是一个代理对象。为什么要调用相同的引用方法导致2个不同的结果?

我还注意到@Query注释运行良好。 JpaRepository和PersonRepository引用都使用自定义SQL。

我想Spring Cache和Spring JPA Data如何生成代理顾问之间可能存在一些差异。这可能是一个错误吗?

2 个答案:

答案 0 :(得分:1)

@EnableCaching添加到您的配置中:

@EnableCaching
@Configuration
public class AppConfig {

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("Person");
    }
}

声明缓存注释本身不会自动触发其操作,您应该使用Caching注释以声明方式启用EnableCaching行为。这种方法的一个优点是你可以通过只删除一个配置行而不是代码中的所有注释来禁用它。

答案 1 :(得分:0)

我想我找到了这种情况发生的原因。我添加了一些aop代码来跟踪存储库中调用的方法。

@Aspect
@Configuration
public class AppConfig {

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager();
    }

    @AfterReturning("execution(* org..*Repository.*(..))")
    public void logServiceAccess(JoinPoint joinPoint) {

        Arrays.asList(joinPoint.getTarget().getClass().getMethods()) //
                .stream() //
                .filter(m -> m.getName().startsWith("findOne")) //
                .forEach(m -> System.err.println(m));

        System.err.println("Completed: " + joinPoint);
    }
}

输出

public final java.lang.Object com.sun.proxy.$Proxy66.findOne(java.io.Serializable)
public final org.chris.demo.domain.Person com.sun.proxy.$Proxy66.findOne(java.lang.Long)

Spring容器代理2使用不同参数的findOne方法。我认为由通用代码引起的所有通用代码将在编译后被删除。

当我使用父接口调用方法时,它调用public final java.lang.Object com.sun.proxy.$Proxy66.findOne(java.io.Serializable) 方法。在该方法上,没有办法添加@cacheable注释。

我不知道是否有办法集中当一个子接口通过泛型编程覆盖该方法时,Spring容器只生成一个findOne方法。