使用Hazelcast作为Spring数据存储库的开源案例

20-09-17 banq

在我们的付款系统中,使用了非常简单的缓存方式。我们有本地的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缓存

  • maven依赖

<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();
}

 

              

猜你喜欢