Skip to content

Commit 45fd8c4

Browse files
committed
feat: Implement Redis cache
fix: Refactor classes to use .env
1 parent 4db5526 commit 45fd8c4

File tree

11 files changed

+92
-30
lines changed

11 files changed

+92
-30
lines changed

Dockerfile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
FROM maven:3.8.5-openjdk-17 AS build
2+
WORKDIR /
3+
COPY /src /src
4+
COPY pom.xml /
5+
RUN mvn -f /pom.xml clean package
6+
17
FROM openjdk:17-jdk-slim
2-
COPY target/*.jar application.jar
8+
WORKDIR /
9+
COPY /src /src
10+
COPY --from=build /target/*.jar application.jar
11+
EXPOSE 8080
312
ENTRYPOINT ["java", "-jar", "application.jar"]

docker-compose.yaml

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,40 @@
11
version: '3'
22

3+
volumes:
4+
cache:
5+
driver: local
6+
db-data:
7+
driver: local
8+
39
services:
410
app:
511
build:
612
context: .
713
container_name: tasklist
814
depends_on:
915
- db
10-
environment:
11-
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/tasklist?currentSchema=tasklist
12-
- SPRING_DATASOURCE_USERNAME=postgres
13-
- SPRING_DATASOURCE_PASSWORD=z7J.6q
16+
env_file:
17+
- .env
1418
ports:
1519
- '8080:8080'
1620

1721
db:
1822
image: postgres:15.1-alpine
1923
container_name: postgres
2024
environment:
21-
- POSTGRES_USER=postgres
22-
- POSTGRES_PASSWORD=z7J.6q
23-
- POSTGRES_DB=tasklist
25+
- POSTGRES_USER=${POSTGRES_USERNAME}
26+
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
27+
- POSTGRES_DB=${POSTGRES_DATABASE}
28+
volumes:
29+
- ./src/main/resources/db-init:/docker-entrypoint-initdb.d
30+
- db-data:/var/lib/postgresql/data
31+
32+
redis:
33+
image: redis:7.2-rc-alpine
34+
restart: always
35+
container_name: redis
36+
ports:
37+
- '6379:6379'
38+
command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
2439
volumes:
25-
- ./src/main/resources/db-init:/docker-entrypoint-initdb.d
40+
- cache:/data

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
<artifactId>spring-boot-starter-web</artifactId>
4747
</dependency>
4848

49+
<dependency>
50+
<groupId>org.springframework.boot</groupId>
51+
<artifactId>spring-boot-starter-data-redis</artifactId>
52+
</dependency>
53+
4954
<dependency>
5055
<groupId>org.springframework.boot</groupId>
5156
<artifactId>spring-boot-starter-security</artifactId>

src/main/java/com/example/tasklist/TasklistApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.cache.annotation.EnableCaching;
56
import org.springframework.transaction.annotation.EnableTransactionManagement;
67

78
@SpringBootApplication
89
@EnableTransactionManagement
10+
@EnableCaching
911
public class TasklistApplication {
1012

1113
public static void main(String[] args) {

src/main/java/com/example/tasklist/domain/task/Task.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
import lombok.Data;
44

5+
import java.io.Serializable;
56
import java.time.LocalDateTime;
67

78
@Data
8-
public class Task {
9+
public class Task implements Serializable {
910

1011
private Long id;
1112
private String title;

src/main/java/com/example/tasklist/domain/user/User.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import com.example.tasklist.domain.task.Task;
44
import lombok.Data;
55

6+
import java.io.Serializable;
67
import java.util.List;
78
import java.util.Set;
89

910
@Data
10-
public class User {
11+
public class User implements Serializable {
1112

1213
private Long id;
1314
private String name;

src/main/java/com/example/tasklist/service/impl/TaskServiceImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import com.example.tasklist.repository.TaskRepository;
77
import com.example.tasklist.service.TaskService;
88
import lombok.RequiredArgsConstructor;
9+
import org.springframework.cache.annotation.CacheEvict;
10+
import org.springframework.cache.annotation.CachePut;
11+
import org.springframework.cache.annotation.Cacheable;
912
import org.springframework.stereotype.Service;
1013
import org.springframework.transaction.annotation.Transactional;
1114

@@ -19,6 +22,7 @@ public class TaskServiceImpl implements TaskService {
1922

2023
@Override
2124
@Transactional(readOnly = true)
25+
@Cacheable(value = "TaskService::getById", key = "#id")
2226
public Task getById(Long id) {
2327
return taskRepository.findById(id)
2428
.orElseThrow(() -> new ResourceNotFoundException("Task not found."));
@@ -32,6 +36,7 @@ public List<Task> getAllByUserId(Long id) {
3236

3337
@Override
3438
@Transactional
39+
@CachePut(value = "TaskService::getById", key = "#task.id")
3540
public Task update(Task task) {
3641
if (task.getStatus() == null) {
3742
task.setStatus(Status.TODO);
@@ -42,6 +47,7 @@ public Task update(Task task) {
4247

4348
@Override
4449
@Transactional
50+
@Cacheable(value = "TaskService::getById", key = "#task.id")
4551
public Task create(Task task, Long userId) {
4652
task.setStatus(Status.TODO);
4753
taskRepository.create(task);
@@ -51,6 +57,7 @@ public Task create(Task task, Long userId) {
5157

5258
@Override
5359
@Transactional
60+
@CacheEvict(value = "TaskService::getById", key = "#id")
5461
public void delete(Long id) {
5562
taskRepository.delete(id);
5663
}

src/main/java/com/example/tasklist/service/impl/UserServiceImpl.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import com.example.tasklist.repository.UserRepository;
77
import com.example.tasklist.service.UserService;
88
import lombok.RequiredArgsConstructor;
9+
import org.springframework.cache.annotation.CacheEvict;
10+
import org.springframework.cache.annotation.CachePut;
11+
import org.springframework.cache.annotation.Cacheable;
12+
import org.springframework.cache.annotation.Caching;
913
import org.springframework.security.crypto.password.PasswordEncoder;
1014
import org.springframework.stereotype.Service;
1115
import org.springframework.transaction.annotation.Transactional;
@@ -21,20 +25,26 @@ public class UserServiceImpl implements UserService {
2125

2226
@Override
2327
@Transactional(readOnly = true)
28+
@Cacheable(value = "UserService::getById", key = "#id")
2429
public User getById(Long id) {
2530
return userRepository.findById(id)
2631
.orElseThrow(() -> new ResourceNotFoundException("User not found."));
2732
}
2833

2934
@Override
3035
@Transactional(readOnly = true)
36+
@Cacheable(value = "UserService::getByUsername", key = "#username")
3137
public User getByUsername(String username) {
3238
return userRepository.findByUsername(username)
3339
.orElseThrow(() -> new ResourceNotFoundException("User not found."));
3440
}
3541

3642
@Override
3743
@Transactional
44+
@Caching(put = {
45+
@CachePut(value = "UserService::getById", key = "#user.id"),
46+
@CachePut(value = "UserService::getByUsername", key = "#user.username")
47+
})
3848
public User update(User user) {
3949
user.setPassword(passwordEncoder.encode(user.getPassword()));
4050
userRepository.update(user);
@@ -43,6 +53,10 @@ public User update(User user) {
4353

4454
@Override
4555
@Transactional
56+
@Caching(cacheable = {
57+
@Cacheable(value = "UserService::getById", key = "#user.id"),
58+
@Cacheable(value = "UserService::getByUsername", key = "#user.username")
59+
})
4660
public User create(User user) {
4761
if (userRepository.findByUsername(user.getUsername()).isPresent()) {
4862
throw new IllegalStateException("User already exists.");
@@ -60,12 +74,14 @@ public User create(User user) {
6074

6175
@Override
6276
@Transactional(readOnly = true)
77+
@Cacheable(value = "UserService::isTaskOwner", key = "#userId + '.' + #taskId")
6378
public boolean isTaskOwner(Long userId, Long taskId) {
6479
return userRepository.isTaskOwner(userId, taskId);
6580
}
6681

6782
@Override
6883
@Transactional
84+
@CacheEvict(value = "UserService::getById", key = "#id")
6985
public void delete(Long id) {
7086
userRepository.delete(id);
7187
}

src/main/java/com/example/tasklist/web/controller/ControllerAdvice.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public ExceptionBody handleAuthentication(AuthenticationException e) {
7474
@ExceptionHandler(Exception.class)
7575
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
7676
public ExceptionBody handleException(Exception e) {
77+
e.printStackTrace();
7778
return new ExceptionBody("Internal error.");
7879
}
7980

src/main/java/com/example/tasklist/web/security/JwtTokenProvider.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.springframework.stereotype.Service;
2020

2121
import java.security.Key;
22+
import java.time.Instant;
23+
import java.time.temporal.ChronoUnit;
2224
import java.util.Date;
2325
import java.util.List;
2426
import java.util.Set;
@@ -43,12 +45,11 @@ public String createAccessToken(Long userId, String username, Set<Role> roles) {
4345
Claims claims = Jwts.claims().setSubject(username);
4446
claims.put("id", userId);
4547
claims.put("roles", resolveRoles(roles));
46-
Date now = new Date();
47-
Date validity = new Date(now.getTime() + jwtProperties.getAccess());
48+
Instant validity = Instant.now()
49+
.plus(jwtProperties.getAccess(), ChronoUnit.HOURS);
4850
return Jwts.builder()
4951
.setClaims(claims)
50-
.setIssuedAt(now)
51-
.setExpiration(validity)
52+
.setExpiration(Date.from(validity))
5253
.signWith(key)
5354
.compact();
5455
}
@@ -62,12 +63,11 @@ private List<String> resolveRoles(Set<Role> roles) {
6263
public String createRefreshToken(Long userId, String username) {
6364
Claims claims = Jwts.claims().setSubject(username);
6465
claims.put("id", userId);
65-
Date now = new Date();
66-
Date validity = new Date(now.getTime() + jwtProperties.getRefresh());
66+
Instant validity = Instant.now()
67+
.plus(jwtProperties.getRefresh(), ChronoUnit.DAYS);
6768
return Jwts.builder()
6869
.setClaims(claims)
69-
.setIssuedAt(now)
70-
.setExpiration(validity)
70+
.setExpiration(Date.from(validity))
7171
.signWith(key)
7272
.compact();
7373
}

src/main/resources/application.yaml

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
spring:
2+
config:
3+
import: optional:file:.env[.properties]
4+
cache:
5+
type: redis
6+
cache-names: redis-cache
7+
data:
8+
redis:
9+
host: ${REDIS_HOST}
10+
port: 6379
11+
password: ${REDIS_PASSWORD}
212
datasource:
3-
url: jdbc:postgresql://localhost:5432/tasklist?currentSchema=tasklist
4-
username: postgres
5-
password: z7J.6q
13+
url: jdbc:postgresql://${HOST}:5432/${POSTGRES_DATABASE}?currentSchema=${POSTGRES_SCHEMA}
14+
username: ${POSTGRES_USERNAME}
15+
password: ${POSTGRES_PASSWORD}
616
driver-class-name: org.postgresql.Driver
7-
sql:
8-
init:
9-
mode: never
10-
continue-on-error: false
11-
# data-locations: classpath:demo_data.sql
1217
liquibase:
1318
change-log: classpath:liquibase/db.changelog.yaml
1419
enabled: true
@@ -17,8 +22,8 @@ mybatis:
1722
mapper-locations: classpath:mybatis/mapper/*.xml
1823
security:
1924
jwt:
20-
secret: aGZiYmtiYWllYmNpZWFpZWJsZWNldWNlY2xhZWNhaWJlbGNhZWN3Q0VCV0VXSUM=
21-
access: 3600000
22-
refresh: 2592000000
25+
secret: ${JWT_SECRET}
26+
access: 1
27+
refresh: 30
2328
springdoc:
2429
override-with-generic-response: false

0 commit comments

Comments
 (0)