├── .circleci
└── config.yml
├── pom.xml
├── readme.md
├── renovate.json
└── src
├── main
├── java
│ └── pl
│ │ └── piomin
│ │ └── services
│ │ └── redis
│ │ ├── SampleSpringRedisApplication.java
│ │ ├── model
│ │ ├── Account.java
│ │ ├── Customer.java
│ │ └── Transaction.java
│ │ ├── repository
│ │ ├── CustomerRepository.java
│ │ └── TransactionRepository.java
│ │ └── web
│ │ ├── CustomerController.java
│ │ └── TransactionController.java
└── resources
│ └── application.yml
└── test
└── java
└── pl
└── piomin
└── services
└── redis
├── CustomerIntegrationTests.java
├── RedisContainerDevMode.java
├── RedisCustomerRepositoryTest.java
├── RedisTransactionRepositoryTest.java
└── SampleSpringRedisApplicationTest.java
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: '2.1'
2 |
3 | jobs:
4 | analyze:
5 | docker:
6 | - image: 'cimg/openjdk:21.0.6'
7 | steps:
8 | - checkout
9 | - run:
10 | name: Analyze on SonarCloud
11 | command: mvn verify sonar:sonar -DskipTests
12 | test:
13 | executor: machine_executor_amd64
14 | steps:
15 | - checkout
16 | - run:
17 | name: Install OpenJDK 21
18 | command: |
19 | java -version
20 | sudo apt-get update && sudo apt-get install openjdk-21-jdk
21 | sudo update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java
22 | sudo update-alternatives --set javac /usr/lib/jvm/java-21-openjdk-amd64/bin/javac
23 | java -version
24 | export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
25 | - run:
26 | name: Maven Tests
27 | command: mvn test
28 |
29 | orbs:
30 | maven: circleci/maven@2.0.0
31 |
32 | executors:
33 | machine_executor_amd64:
34 | machine:
35 | image: ubuntu-2204:2023.10.1
36 | environment:
37 | architecture: "amd64"
38 | platform: "linux/amd64"
39 |
40 | workflows:
41 | maven_test:
42 | jobs:
43 | - test
44 | - analyze:
45 | context: SonarCloud
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.5.0
11 |
12 |
13 | pl.piomin.services
14 | sample-spring-redis
15 | 1.1-SNAPSHOT
16 |
17 |
18 | 21
19 | piomin_sample-spring-redis
20 | piomin
21 | https://sonarcloud.io
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-data-redis
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-web
32 |
33 |
34 | org.projectlombok
35 | lombok
36 | 1.18.38
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-test
41 | test
42 |
43 |
44 | org.testcontainers
45 | testcontainers
46 | 1.21.1
47 | test
48 |
49 |
50 | org.testcontainers
51 | junit-jupiter
52 | 1.21.1
53 | test
54 |
55 |
56 | org.springframework.boot
57 | spring-boot-testcontainers
58 | test
59 |
60 |
61 |
62 |
63 |
64 |
65 | org.springframework.boot
66 | spring-boot-maven-plugin
67 |
68 |
69 | org.jacoco
70 | jacoco-maven-plugin
71 | 0.8.13
72 |
73 |
74 |
75 | prepare-agent
76 |
77 |
78 |
79 | report
80 | test
81 |
82 | report
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## Introduction to Spring Data Redis [](https://twitter.com/piotr_minkowski)
2 |
3 | [](https://circleci.com/gh/piomin/sample-spring-redis)
4 |
5 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-redis)
6 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-redis)
7 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-redis)
8 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-redis)
9 |
10 | Detailed description can be found here: [Introduction to Spring Data Redis](https://piotrminkowski.com/2019/03/05/introduction-to-spring-data-redis/)
11 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base",":dependencyDashboard"
5 | ],
6 | "packageRules": [
7 | {
8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
9 | "automerge": true
10 | }
11 | ],
12 | "prCreation": "not-pending"
13 | }
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/SampleSpringRedisApplication.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
6 |
7 | @SpringBootApplication
8 | @EnableRedisRepositories
9 | public class SampleSpringRedisApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(SampleSpringRedisApplication.class, args);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/model/Account.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import org.springframework.data.redis.core.index.Indexed;
8 |
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | @Getter
12 | @Setter
13 | public class Account {
14 | @Indexed private Long id;
15 | private String number;
16 | private int balance;
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/model/Customer.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import org.springframework.data.annotation.Id;
8 | import org.springframework.data.redis.core.RedisHash;
9 | import org.springframework.data.redis.core.index.Indexed;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | @RedisHash("customer")
15 | @AllArgsConstructor
16 | @NoArgsConstructor
17 | @Getter
18 | @Setter
19 | public class Customer {
20 |
21 | @Id private Long id;
22 | @Indexed private String externalId;
23 | private String name;
24 | private List accounts = new ArrayList<>();
25 |
26 | public Customer(Long id, String externalId, String name) {
27 | this.id = id;
28 | this.externalId = externalId;
29 | this.name = name;
30 | }
31 |
32 | public void addAccount(Account account) {
33 | this.accounts.add(account);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/model/Transaction.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import org.springframework.data.annotation.Id;
8 | import org.springframework.data.redis.core.RedisHash;
9 | import org.springframework.data.redis.core.index.Indexed;
10 |
11 | import java.util.Date;
12 |
13 | @RedisHash("transaction")
14 | @AllArgsConstructor
15 | @NoArgsConstructor
16 | @Getter
17 | @Setter
18 | public class Transaction {
19 | @Id
20 | private Long id;
21 | private int amount;
22 | private Date date;
23 | @Indexed
24 | private Long fromAccountId;
25 | @Indexed
26 | private Long toAccountId;
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/repository/CustomerRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis.repository;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 | import pl.piomin.services.redis.model.Customer;
5 |
6 | import java.util.List;
7 |
8 | public interface CustomerRepository extends CrudRepository {
9 |
10 | Customer findByExternalId(String externalId);
11 | List findByAccountsId(Long id);
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/repository/TransactionRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis.repository;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 | import pl.piomin.services.redis.model.Transaction;
5 |
6 | import java.util.List;
7 |
8 | public interface TransactionRepository extends CrudRepository {
9 |
10 | List findByFromAccountId(Long fromAccountId);
11 | List findByToAccountId(Long toAccountId);
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/web/CustomerController.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis.web;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.web.bind.annotation.*;
5 | import pl.piomin.services.redis.model.Customer;
6 | import pl.piomin.services.redis.model.Transaction;
7 | import pl.piomin.services.redis.repository.CustomerRepository;
8 |
9 | import java.util.Optional;
10 |
11 | @RestController
12 | @RequestMapping("/customers")
13 | public class CustomerController {
14 |
15 | @Autowired
16 | CustomerRepository repository;
17 |
18 | @PostMapping
19 | public Customer add(@RequestBody Customer customer) {
20 | return repository.save(customer);
21 | }
22 |
23 | @GetMapping("/{id}")
24 | public Customer findById(@PathVariable("id") Long id) {
25 | Optional optCustomer = repository.findById(id);
26 | return optCustomer.orElse(null);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/services/redis/web/TransactionController.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis.web;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.web.bind.annotation.*;
5 | import pl.piomin.services.redis.model.Transaction;
6 | import pl.piomin.services.redis.repository.TransactionRepository;
7 |
8 | import java.util.List;
9 | import java.util.Optional;
10 |
11 | @RestController
12 | @RequestMapping("/transactions")
13 | public class TransactionController {
14 |
15 | @Autowired
16 | TransactionRepository repository;
17 |
18 | @PostMapping
19 | public Transaction add(@RequestBody Transaction transaction) {
20 | return repository.save(transaction);
21 | }
22 |
23 | @GetMapping("/{id}")
24 | public Transaction findById(Long id) {
25 | Optional optTransaction = repository.findById(id);
26 | return optTransaction.orElse(null);
27 | }
28 |
29 | @GetMapping("/from/{accountId}")
30 | public List findByFromAccountId(Long accountId) {
31 | return repository.findByFromAccountId(accountId);
32 | }
33 |
34 | @GetMapping("/to/{accountId}")
35 | public List findByToAccountId(Long accountId) {
36 | return repository.findByToAccountId(accountId);
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: sample-spring-redis
4 | data:
5 | redis:
6 | host: localhost
7 | server:
8 | port: 8090
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/services/redis/CustomerIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.boot.test.web.client.TestRestTemplate;
7 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
8 | import org.testcontainers.containers.GenericContainer;
9 | import org.testcontainers.junit.jupiter.Container;
10 | import org.testcontainers.junit.jupiter.Testcontainers;
11 | import pl.piomin.services.redis.model.Account;
12 | import pl.piomin.services.redis.model.Customer;
13 |
14 | import static org.junit.jupiter.api.Assertions.*;
15 |
16 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
17 | @Testcontainers
18 | public class CustomerIntegrationTests {
19 |
20 | @Autowired
21 | TestRestTemplate template;
22 |
23 | @Container
24 | @ServiceConnection
25 | static final GenericContainer redis = new GenericContainer("redis:latest")
26 | .withExposedPorts(6379);
27 |
28 | @Test
29 | void testAddAndFind() {
30 | assertTrue(redis.isRunning());
31 | Customer customer = new Customer(1L, "123456", "John Smith");
32 | customer.addAccount(new Account(1L, "1234567890", 2000));
33 | customer.addAccount(new Account(2L, "1234567891", 4000));
34 | customer = template.postForObject("/customers", customer, Customer.class);
35 | assertNotNull(customer);
36 | customer = template.getForObject("/customers/{id}", Customer.class, 1L);
37 | assertNotNull(customer);
38 | assertEquals("123456", customer.getExternalId());
39 | assertEquals(2, customer.getAccounts().size());
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/services/redis/RedisContainerDevMode.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis;
2 |
3 | import org.springframework.boot.test.context.TestConfiguration;
4 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
5 | import org.springframework.context.annotation.Bean;
6 | import org.testcontainers.containers.GenericContainer;
7 | import org.testcontainers.utility.DockerImageName;
8 |
9 | @TestConfiguration
10 | public class RedisContainerDevMode {
11 |
12 | @Bean
13 | @ServiceConnection("redis")
14 | GenericContainer redisContainer() {
15 | return new GenericContainer(DockerImageName.parse("redis").withTag("7.0"))
16 | .withExposedPorts(6379);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/services/redis/RedisCustomerRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis;
2 |
3 | import org.junit.Ignore;
4 | import org.junit.jupiter.api.MethodOrderer;
5 | import org.junit.jupiter.api.Order;
6 | import org.junit.jupiter.api.Test;
7 | import org.junit.jupiter.api.TestMethodOrder;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
11 | import org.testcontainers.containers.GenericContainer;
12 | import org.testcontainers.junit.jupiter.Container;
13 | import org.testcontainers.junit.jupiter.Testcontainers;
14 | import pl.piomin.services.redis.model.Account;
15 | import pl.piomin.services.redis.model.Customer;
16 | import pl.piomin.services.redis.repository.CustomerRepository;
17 |
18 | import java.util.List;
19 |
20 | import static org.junit.jupiter.api.Assertions.assertEquals;
21 | import static org.junit.jupiter.api.Assertions.assertNotNull;
22 |
23 | @DataRedisTest
24 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
25 | @Testcontainers
26 | public class RedisCustomerRepositoryTest {
27 |
28 | @Container
29 | @ServiceConnection
30 | static final GenericContainer redis = new GenericContainer("redis:latest")
31 | .withExposedPorts(6379);
32 |
33 | @Autowired
34 | CustomerRepository repository;
35 |
36 | @Test
37 | @Order(1)
38 | void shouldAdd() {
39 | Customer customer = new Customer(1L, "80010121098", "John Smith");
40 | customer.addAccount(new Account(1L, "1234567890", 2000));
41 | customer.addAccount(new Account(2L, "1234567891", 4000));
42 | customer.addAccount(new Account(3L, "1234567892", 6000));
43 | customer = repository.save(customer);
44 | assertNotNull(customer);
45 | }
46 |
47 | // @Test
48 | // @Order(2)
49 | void shouldFindByAccounts() {
50 | List customers = repository.findByAccountsId(3L);
51 | assertEquals(1, customers.size());
52 | Customer customer = customers.get(0);
53 | assertNotNull(customer);
54 | assertEquals(1, customer.getId().longValue());
55 | }
56 |
57 | @Test
58 | @Order(3)
59 | void shouldFindByExternal() {
60 | Customer customer = repository.findByExternalId("80010121098");
61 | assertNotNull(customer);
62 | assertEquals(1, customer.getId().longValue());
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/services/redis/RedisTransactionRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis;
2 |
3 | import org.junit.jupiter.api.MethodOrderer;
4 | import org.junit.jupiter.api.Order;
5 | import org.junit.jupiter.api.Test;
6 | import org.junit.jupiter.api.TestMethodOrder;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
9 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
10 | import org.testcontainers.containers.GenericContainer;
11 | import org.testcontainers.junit.jupiter.Container;
12 | import org.testcontainers.junit.jupiter.Testcontainers;
13 | import pl.piomin.services.redis.model.Transaction;
14 | import pl.piomin.services.redis.repository.TransactionRepository;
15 |
16 | import java.util.Date;
17 | import java.util.List;
18 |
19 | import static org.junit.jupiter.api.Assertions.assertNotNull;
20 | import static org.junit.jupiter.api.Assertions.assertTrue;
21 |
22 | @DataRedisTest
23 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
24 | @Testcontainers
25 | public class RedisTransactionRepositoryTest {
26 |
27 | @Container
28 | @ServiceConnection
29 | static final GenericContainer redis = new GenericContainer("redis:latest")
30 | .withExposedPorts(6379);
31 |
32 | @Autowired
33 | TransactionRepository repository;
34 |
35 | @Test
36 | @Order(1)
37 | void shouldAdd() {
38 | Transaction transaction = new Transaction(1L, 1000, new Date(), 20L, 40L);
39 | transaction = repository.save(transaction);
40 | assertNotNull(transaction);
41 | }
42 |
43 | @Test
44 | @Order(2)
45 | public void shouldFindByFromAccountId() {
46 | List transactions = repository.findByFromAccountId(20L);
47 | assertTrue(transactions.size() == 1);
48 | }
49 |
50 | @Test
51 | @Order(3)
52 | public void shouldFindByToAccountId() {
53 | List transactions = repository.findByToAccountId(40L);
54 | assertTrue(transactions.size() == 1);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/services/redis/SampleSpringRedisApplicationTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.services.redis;
2 |
3 | import org.springframework.boot.SpringApplication;
4 |
5 | public class SampleSpringRedisApplicationTest {
6 |
7 | public static void main(String[] args) {
8 | SpringApplication.from(SampleSpringRedisApplication::main)
9 | .with(RedisContainerDevMode.class)
10 | .run(args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------