Sitemap
Arif Dwi Prasetya

Arif Dwi Prasetya

Springboot4 — Hazelcast (Cache)

3 min readApr 27, 2026

--

Press enter or click to view image in full size

Hari ke 14 mencoba konsisten nulis, hari ini kita akan menggunakan cache lagi tapi kali ini pakai hazelcast embeded. Tapi ketika aplikasi mati / restart cache akan otomatis hilang.

Press enter or click to view image in full size
https://start.spring.io/

application.yaml

app:
cache:
maps:
nasabah-all:
ttl: 3600
max-idle: 600
max-size: 1000

spring:
application:
name: hazelcache
cache:
type: hazelcast
datasource:
url: jdbc:postgresql://localhost:5432/bank?currentSchema=base_bank
username: postgres
password: postgres

jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql: true
format_sql: true
dialect: org.hibernate.dialect.PostgreSQLDialect
  • ttl : ini adalah waktu expired, jika waktu hapus cache akan dihapus, dalam kasus ini 3600 detik (1 Jam). Jadi diakses tidak diakses cache akan dihapus dalam waktu 1 jam.
  • max-idle : ini waktu idle yang akan menghapus cache ketika tidak diakses dalam waktu ini (max-idle) dalam kasus ini 600 detik (10 menit). Jadi jika dalam 10 menit jika cache tidak diakses, otomatis cache akan dihapus. Kecuali dalam 10 menit diakses lagi, akan mereset lagi waktunya 10 menit dari terakhir kalai diakses. Tetapi tetap maksimal adalah waktu ttl (1 jam). Jadi ketika pertama kali kita akses jam 10.00, expired ttl otomatis jam 11.00. Ketika kita mengakses jam 10.59, cache akan tetap diahpus jam 11.00 karena masuk ttl diatas (1 jam)
  • max-size : berpa banyak jumlah entry maksimal dalam 1 node yang bisa tersimpan. Dalam kasus ini ada 1000, jadi entry ke 1001 akan masuk dan akan menghapus data yg paling lama tidak diakses. Karena nanti kita akan menggunakan strategy LRU (nanti ada di config)

CacheProperties

@Data
@Configuration
@ConfigurationProperties(prefix = "app.cache")
public class CacheProperties {

private Map<String, MapSettings> maps = new HashMap<>();

@Data
public static class MapSettings {
private int ttl = 3600;
private int maxIdle = 600;
private int maxSize = 1000;
}
}

Ambil properties dari .yaml

HazelcastConfig

@Configuration
@RequiredArgsConstructor
public class HazelcastConfig {

private final CacheProperties cacheProperties;

@Bean
public HazelcastInstance hazelcastInstance() {
Config config = new Config().setInstanceName("hazelcache-instance");
cacheProperties.getMaps().forEach((name, settings) ->
config.addMapConfig(buildMapConfig(name, settings))
);
return Hazelcast.newHazelcastInstance(config);
}

@Bean
public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
List<Cache> caches = cacheProperties.getMaps().keySet().stream()
.map(name -> (Cache) new ConcurrentMapCache(name, hazelcastInstance.getMap(name), false))
.toList();
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return cacheManager;
}

private MapConfig buildMapConfig(String name, CacheProperties.MapSettings s) {
return new MapConfig(name)
.setEvictionConfig(new EvictionConfig()
.setEvictionPolicy(EvictionPolicy.LRU)
.setMaxSizePolicy(MaxSizePolicy.PER_NODE)
.setSize(s.getMaxSize()))
.setTimeToLiveSeconds(s.getTtl())
.setMaxIdleSeconds(s.getMaxIdle());
}
}

disini kita pakai Policy.LRU

Get Arif Dwi Prasetya’s stories in your inbox

Join Medium for free to get updates from this writer.

Kita buat mode dan reponya dulu

Nasabah

@Data
@Builder
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "nasabah")
public class Nasabah {
@Id
private String nik;
private String nama;
private Integer tabungan;
}

NasabahDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class NasabahDto implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

private String nik;
private String name;
private Integer tabungan;
}

NasabahRepository

public interface NasabahRepository extends JpaRepository<Nasabah, String> {
}

Lanjut bikin service

NasabahService

public interface NasabahService {
NasabahDto save(NasabahDto dto);

List<NasabahDto> findAll();
}

NasabahServiceImpl

@Service
public class NasabahServiceImpl implements NasabahService {

private final NasabahRepository nasabahRepository;

public NasabahServiceImpl(NasabahRepository nasabahRepository) {
this.nasabahRepository = nasabahRepository;
}

@Override
@CacheEvict(value = "nasabah-all", allEntries = true)
public NasabahDto save(NasabahDto dto) {
return toDto(nasabahRepository.save(toEntity(dto)));
}

@Override
@Cacheable(value = "nasabah-all")
public List<NasabahDto> findAll() {
return toList(nasabahRepository.findAll());
}

NasabahDto toDto(Nasabah nasabah) {
return NasabahDto.builder()
.nik(nasabah.getNik())
.name(nasabah.getNama())
.tabungan(nasabah.getTabungan())
.build();
}

Nasabah toEntity(NasabahDto dto) {
return Nasabah.builder()
.nik(dto.getNik())
.nama(dto.getName())
.tabungan(dto.getTabungan())
.build();
}

List<NasabahDto> toList(List<Nasabah> list) {
return list.stream().map(this::toDto).toList();
}
}

@cacheEvict itu untuk hapus cache (invalidate) untuk menjaga konsistensi data, supaya ketika findAll, data terupdate lagi.

NasabahController

@RestController
@RequestMapping("/nasabah")
public class NasabahController {
private final NasabahService nasabahService;

public NasabahController(NasabahService nasabahService) {
this.nasabahService = nasabahService;
}

@PostMapping("/save")
public ResponseEntity<NasabahDto> save(@RequestBody NasabahDto request) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(nasabahService.save(request));
}

@GetMapping("/findAll")
public ResponseEntity<List<NasabahDto>> findAll() {
return ResponseEntity.ok(nasabahService.findAll());
}
}

Oke langung kita coba di swagger: http://localhost:8080/swagger-ui/index.html

Coba service save, kemudian coba service findAll. Pertama kali findAll akan ada log. Kemudian coba sekali lagi lognya tidak akan muncul lagi, karena sudah memakai cache.

Sekian artikel yg ini pun sederhana basic kan. Semoga bermanfaat

— Arif Dwi Prasetya

--

--