在我们的付款系统中,使用了非常简单的缓存方式。我们有本地的EhCache,它工作得很好,是在JDBC层提供的。这种设计的缺点是:
- 这是本地缓存。没有数据更改传播到其他节点。
- 不涉及JPA。
解决上述问题的好方法是将Hazelcast包装到Spring Data API中, 称为spring-data-hazelcast
在该存储库中,我将向您展示如何使用H2数据库支持的Hazelcast Spring Data API 构建完整的只读缓存。
首先,一定要看一下spring-data-jpa-hazelcast-migration 如果您是Spring Data JPA新手,那么本指南将非常有帮助。然后确保您熟悉Hazelcast MapLoader。
使用HazelcastRead-through缓存<properties> <java.version>1.8</java.version> <spring-data-hazelcast-version>2.2.5</spring-data-hazelcast-version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>com.hazelcast</groupId> <artifactId>spring-data-hazelcast</artifactId> <version>${spring-data-hazelcast-version}</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
|
spring-data-hazelcast插件已经打包好了hazelcast,因此无需担心版本问题。大赞!缓存实体是存储在H2数据库,并通过MapLoader提供给hazelcast的,该实体是此read-through体系结构的引擎:
@KeySpace("persons") @Entity @Table(name = "persons") public class Person implements Serializable {
@Id @javax.persistence.Id private Long personId; private String name; private String surname; private String role; private Long teamId;
@Column(name = "PERSON_ID") public Long getPersonId() { return personId; }
public void setPersonId(Long personId) { this.personId = personId; }
@Column(name = "NAME") public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Column(name = "SURNAME") public String getSurname() { return surname; }
public void setSurname(String surname) { this.surname = surname; }
@Column(name = "ROLE") public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
@Column(name = "TEAM_ID") public Long getTeamId() { return teamId; }
public void setTeamId(Long teamId) { this.teamId = teamId; }
|
此处的KeySpace注释表示,hazelcast 将使用名为“persons”的IMap将数据保存在内存中。
MapLoader实现将数据库和Hazelcast粘合在一起:
@Component public class HazelcastMapStore implements ApplicationContextAware, MapLoader<Long, Person> {
private static PersonsJPARepository personsJPARepository;
@Override public Person load(Long personId) { System.out.println("Loading by key: "+personId); return personsJPARepository.findById(personId).get(); }
@Override public Map<Long, Person> loadAll(Collection<Long> collection) { System.out.println("Loading collections of IDS: "); Map<Long, Person> result = new HashMap<>(); for (Long key : collection) { Person productMap = this.load(key); if (productMap != null) { result.put(key, productMap); } } return result; }
@Override public Iterable<Long> loadAllKeys() { System.out.println("Getting all the keys!"); return personsJPARepository.findAllId(); }
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { personsJPARepository = applicationContext.getBean(PersonsJPARepository.class); }
|
MapLoader已准备就绪,现在每次当请求的实体不在内存中时,Hazelcast都会命中MapLoader.load(personId)其他两种方法只是预热缓存方法,用于在第一次缓存命中之前加载缓存的数据。创建"persons" IMap,并连接到已经创建的HazelcastMapStore。
@Configuration public class HazelcastConfiguration {
@Bean public Config hazelcastConfig(@Lazy HazelcastMapStore mapStore) { return new Config().setInstanceName("hazelcast-instance").addMapConfig( new MapConfig().setName("persons") .setMapStoreConfig( new MapStoreConfig().setEnabled(true).setInitialLoadMode(MapStoreConfig.InitialLoadMode.EAGER) .setImplementation(mapStore) )); }
@Bean public HazelcastInstance hazelcastInstance(Config config) { return Hazelcast.newHazelcastInstance(config); }
|
如果在将您的存储库连接到MapLoader时遇到问题,请查看此 Stackoverflow讨论
配置用于Hazelcast(缓存)和JPA(访问H2 DB)的Spring数据存储库
@SpringBootApplication(exclude = { HazelcastAutoConfiguration.class }) @EnableHazelcastRepositories(basePackages={"com.example.hazelcast.demo.repositories.hz"}) @EnableJpaRepositories(basePackages={"com.example.hazelcast.demo.repositories.jpa"}) public class DemoApplication {
public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
|
EnableHazelcastRepositories,EnableJpaRepositories只是说明Spring数据存储库的位置。
这是完整的代码:
package com.example.hazelcast.demo.repositories.hz;
import com.example.hazelcast.demo.model.Person; import org.springframework.data.hazelcast.repository.HazelcastRepository;
public interface PersonsHazelcastRepository extends HazelcastRepository<Person, Long> { Person findPersonByPersonId(Long personId); }
package com.example.hazelcast.demo.repositories.jpa;
import com.example.hazelcast.demo.model.Person; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository;
public interface PersonsJPARepository extends CrudRepository<Person, Long> { @Query("SELECT n.id FROM Person n") Iterable<Long> findAllId(); }
|