2016-01-10 2 views
0

Когда я интегрирую Spring JPA Data и Spring Cache, возникает странное поведение, которое я не могу объяснить.Странное поведение при интеграции Spring JPA Data и Spring Cache

Я использую Spring Boot для настройки моего демонстрационного проекта. Код ниже.

Мой конфиг боб:

@Configuration 
public class AppConfig { 

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

Моя сущность боб.

@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; 
    } 

} 

My 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 переменные точно указывают на одну и ту же ссылку, даже если это прокси-объект. Зачем вызывать метод той же ссылки, вызывающий 2 результата разности?

Я также замечаю, что аннотация @Query работает хорошо. В ссылках JpaRepository и PersonRepository используется настроенный SQL.

Возможно, некоторые различия между тем, как Spring Cache и Spring JPA Data генерируют прокси-советник. Возможно ли это здесь?

+0

Подпись фактического метода настраивается для определения. В первом это 'PersonRepository.findOne', в другом -' JpaRepository.findOne'. При использовании Spring Data уже много проксирования, и добавление кеширования не станет более ясным. Также вам действительно нужно Spring Caching или вы действительно хотите иметь кеш второго уровня, управляемый провайдером JPA. Кэши служат совершенно другой цели. –

ответ

1

Добавить @EnableCaching в конфигурацию:

@EnableCaching 
@Configuration 
public class AppConfig { 

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

Объявление аннотаций кэша не автоматически запускать их действия сами по себе, вы должны декларативно включить Caching поведение с помощью EnableCaching аннотацию. Одним из преимуществ этого подхода является то, что вы можете отключить его, удалив только одну строку конфигурации, а не все аннотации кода.

+0

Я добавил аннотацию @EnableCahing к моему классу bootstrap и не вставлял этот класс в вопрос. Это не основная причина этого вопроса. – crisli

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) 

Спринг контейнер методы прокси-2 findOne с различными аргументами. Я думаю, что это вызвано общим кодом, поскольку весь общий код будет удален после компиляции.

Когда я использую родительский интерфейс, вызовите метод, он вызывает метод public final java.lang.Object com.sun.proxy.$Proxy66.findOne(java.io.Serializable) . И по этому методу не существует способа добавить примечание @cacheable.

Я не знаю, есть ли способ сосредоточиться. Контейнер Spring генерирует только один метод findOne, когда есть суб-интерфейс, который перезаписывает метод с помощью общего программирования.

Смежные вопросы