linkedin insight
Omax Tech

Loading...

Redis in a Spring Boot School Management System: A Journey

Redis in a Spring Boot School Management System: A Journey

Software Development
Sep 7, 2024
5-6 min

Share blog

Introduction

I was recently assigned to a school management project for a client in Riyadh, Saudi Arabia, by my company. The project was built using Spring Boot for the backend and Angular for the frontend. As a member of the backend team, I was excited to dive into this project and work on building a robust system that could handle the complex requirements of managing a school’s data and operations.

The project journey was engaging, filled with challenges and learning opportunities. However, one thing quickly became clear: the amount of data we were pulling from the database on almost every page was substantial. Information like existing grade years, courses, and academic years was needed everywhere—from the dashboard to student profiles to report cards. Each time a user navigated to a different section of the app, we were hitting the database repeatedly for the same data. It wasn’t long before I realized that this approach was not only inefficient but also putting unnecessary strain on our database.

This is where Redis came into the picture.

Redis: Why It Was the Right Choice

Redis is an in-memory data structure store that’s often used as a cache. It’s incredibly fast because it stores data in memory, meaning retrieval times are minimal compared to querying a database. For our school management system, caching this commonly accessed data made perfect sense. Instead of making recurring queries to the database, we could fetch the necessary information from Redis, speeding up our application and reducing the load on our database.

Implementing Redis in Spring Boot:

Creating the Redis Service Interface

To keep things clean and reusable, I started by creating a Redis service interface that would handle basic operations like saving, retrieving, and deleting data in Redis.

javascript
1public interface RedisService<T extends BaseDto> {
2 String generateKey(T entity);
3 void save(T t, Long expiry);
4 Object get(String key);
5 void delete(String key);
6}

Creating the Redis Service Interface

Next, I implemented this interface in a RedisServiceImpl class. This is where the actual interaction with Redis happens. By extending this class, other services in the application can easily cache and retrieve data without worrying about the underlying Redis operations.

javascript
1@Service
2public class RedisServiceImpl<T extends BaseDto> implements RedisService<T> {
3
4 protected final RedisTemplate<String, Object> redisTemplate;
5 protected final ObjectMapper objectMapper;
6 public RedisServiceImpl(RedisTemplate<String, Object> redisTemplate,
7 ObjectMapper objectMapper) {
8 this.redisTemplate = redisTemplate;
9 this.objectMapper = objectMapper;
10 }
11
12 @Override
13 public void save(T entity, Long expiry) {
14 String key = generateKey(entity);
15 redisTemplate.opsForValue().set(key, entity);
16 if (expiry != null) {
17 redisTemplate.expire(key, expiry, TimeUnit.SECONDS);
18 }
19 }
20
21 @Override
22 public Object get(String key) {
23 return redisTemplate.opsForValue().get(key);
24 }
25
26 @Override
27 public void delete(String key) {
28 redisTemplate.delete(key);
29 }
30}

Specialized Service for Grade Years and Courses

One of the key areas where Redis caching was particularly beneficial was in managing grade years and their associated courses. For this, I extended the RedisServiceImpl class in a new service, YearsWithCoursesService. This service not only managed the caching of grade years and courses but also provided methods to retrieve and sort them.

javascript
1@Service
2public class YearsWithCoursesService extends RedisServiceImpl<YearDto> {
3 public YearsWithCoursesService(RedisTemplate<String, Object> redisTemplate,
4 ObjectMapper objectMapper) {
5 super(redisTemplate, objectMapper);
6 }
7
8 @Override
9 public String generateKey(YearDto yearDto) {
10 return "year:" + yearDto.getId();
11 }
12
13 public YearDto getYear(Long yearId) {
14 String key = "year:" + yearId;
15 Object value = get(key);
16 if (value == null) {
17 return null;
18 }
19 return objectMapper.convertValue(value, YearDto.class);
20 }
21
22 public List<YearDto> getYears() {
23 List<YearDto> yearDtos = new ArrayList<>();
24 Set<String> keys = redisTemplate.keys("year*");
25
26 if (keys != null) {
27 for (String key : keys) {
28 Object value = redisTemplate.opsForValue().get(key);
29 if (value != null) {
30 YearDto yearDto = objectMapper.convertValue(value, YearDto.class);
31 yearDtos.add(yearDto);
32 }
33 }
34 }
35
36 return yearDtos.stream().sorted(Comparator.comparing(YearDto::getId)).toList();
37 }
38}

Overcoming Roadblocks: Challenges Faced and How I Tackled Them

Running Redis on a Windows Machine

One of the unexpected challenges I faced during the project was that Redis is primarily designed to run on Unix-based systems like Ubuntu. However, I was developing on a Windows machine, which meant I couldn’t just install Redis directly as I would on Linux.

Solution: Using Windows Subsystem for Linux (WSL)

To get Redis up and running on my Windows machine, I decided to use the Windows Subsystem for Linux (WSL). WSL allows you to run a Linux distribution on Windows, which gave me the flexibility to install and manage Redis just as if I were on a Linux machine. If you would like to know more about how to set up Redis on Windows using WSL check out my blog [reference to blog] !

This setup allowed me to develop and test the Redis integration on my Windows machine without any issues. The use of WSL bridged the gap between the Unix-based nature of Redis and the Windows environment I was working in.

Handling Data Inconsistency

One of the challenges I anticipated was ensuring that the cached data in Redis remained consistent with the database. Since Redis doesn’t automatically sync with the database, I needed a strategy to handle updates, particularly when a user added or modified grade years or courses.

To solve this, I employed a cache invalidation strategy. Whenever there was a change in the database, I forced the system to refresh the Redis cache by setting a forceDB flag. Here’s how I implemented this in a YearService class:

javascript
1@Service
2public class YearService {
3 private final YearsWithCoursesService yearsWithCoursesService;
4
5 @Transactional(readOnly = true)
6 public List<YearDto> findYearsWithCourses(Long schoolId, boolean forceDB) {
7
8 List<YearDto> yearDtos = new ArrayList<>();
9
10 if (!forceDB) {
11 yearDtos = yearsWithCoursesService.getYears();
12 }
13
14 if (yearDtos.isEmpty()) {
15 List<Year> years = yearRepository.findAll();
16 List<Course> courses = courseRepository.findByYearIdIn(
17 years.stream().map(Year::getId).toList());
18
19 Map<Long, List<CourseDto>> coursesByYear = courses.stream()
20 .collect(Collectors.groupingBy(
21 course -> course.getYear().getId(),
22 Collectors.mapping(
23 itm -> createCourse(itm, schoolId),
24 Collectors.toList()
25 )
26 ));
27
28 yearDtos = yearMapper.yearsToYearDtos(years);
29 List<YearDto> existingYearDtos = yearsWithCoursesService.getYears();
30 for (YearDto dto : existingYearDtos) {
31 String key = yearsWithCoursesService.generateKey(dto);
32 yearsWithCoursesService.delete(key);
33 }
34 for (YearDto yearDto : yearDtos) {
35 yearDto.setCourses(coursesByYear.get(yearDto.getId()));
36 if (yearDto.getCourses() == null) {
37 yearDto.setCourses(new ArrayList<>());
38 }
39 yearDto.setStudentsCount(yearDto.getCourses().stream()
40 .map(CourseDto::getStudentsCount).reduce(0, Integer::sum));
41 yearsWithCoursesService.save(yearDto, null);
42 }
43 } else {
44 yearDtos = yearsWithCoursesService.getYears();
45
46 }
47
48 return yearDtos;
49 }
50}

In this setup, if the forceDB flag is set to true, the service fetches fresh data from the database, deletes the outdated cache entries, and saves the updated information in Redis. This way, the application always serves the most accurate and up-to-date data to the users.

Final Thoughts

Using Redis in our Spring Boot project significantly improved the performance of our school management system. By caching frequently accessed data like grade years, courses, and academic years we reduced the load on the database and sped up response times for end users. Implementing the Redis service as a reusable component allowed us to easily manage caching throughout the application.

While there were challenges, like ensuring data consistency and handling serialization, the overall experience was positive. Redis proved to be a powerful tool that, when used correctly, can greatly enhance the performance of any data-intensive application. If you’re dealing with similar challenges, I highly recommend considering Redis as a solution. It’s fast, flexible, and relatively easy to integrate into a Spring Boot project.

And that’s it! I hope you enjoyed joining me on this journey through Redis and Spring Boot and found some useful insights and maybe even a bit of inspiration for your own projects. Happy coding!

Blogs

Discover the latest insights and trends in technology with the Omax Tech Blog.

View All Blogs
Responsive web development illustration showing cross-device software design on laptop, tablet, and mobile screens.
6-8 min
April 20, 2026

Our Proven Web Development Process That Delivers Real Results

In software development, success does not come from coding alone. Real results come from understanding business needs, planning the right workflow, building user-friendly designs...

Read More
Secure AWS Systems Manager connectivity illustration showing private cloud access to servers and databases without SSH exposure.
6-8 min
April 20, 2026

Secure AWS Connectivity Using AWS Systems Manager (SSM)

In traditional cloud architectures, secure access to private resources such as databases and internal servers often relies on...

Read More
Cloud upload architecture illustration showing secure multi-account AWS infrastructure for enterprise environments.
6-10 min
April 19, 2026

Building a Secure Multi-Account AWS Architecture for Enterprise Environments (Dev, STG, UAT, Prod)

In today’s cloud-first world, scalability and speed are no longer enough security, governance, and cost control are equally critical...

Read More
Friendly AI assistant robot beside a smartphone, representing adaptive AI agents for modern workflows.
6-8 min
April 15, 2026

Why You Should Use AI Agents Over Single Prompts: Unlocking the Power of Adaptive AI for Complex Workflows

In the world of artificial intelligence (AI), one of the biggest advancements has been the rise of AI agents that adapt dynamically to real-time data and complex workflows...

Read More
Data operations dashboard showing production quality checks, performance trends, and incident alerts across stores.
8-10 min
April 09, 2026

Production Ready ( Quality, performance, and the lessons learned shipping to 150 stores )

We chose dbt over custom scripts, built observability, optimized performance, and shipped to production...

Read More
Scalable data pipeline diagram highlighting dbt macros, reusable models, and multi-store analytics flow.
8-10 min
April 08, 2026

Scaling from 15 to 150 Stores ( When copy-paste becomes technical debt, macros become salvation )

We built a pipeline with observability, incremental models for performance, and snapshots for history. Our 15-store deployment ran smoothly...

Read More
Observability dashboard tracking source freshness, pipeline status, and real-time data quality alerts.
8-10 min
April 07, 2026

Keeping Your Data Fresh: ( The wake-up call at 3am that taught us about observability )

That morning taught us a crucial lesson: a successful dbt run doesn't mean your data is fresh, accurate, or complete. You need observability.

Read More
Retail data architecture visual showing fragmented store databases consolidated into a unified analytics pipeline.
8-10 min
April 06, 2026

Retail Data Chaos: How We Found Our Way Out ( When spreadsheets fail and databases multiply, where do you turn? )

Picture this: You're managing data for a growing retail chain. Store after store opens New York, San Francisco, Los Angeles—each with its own MySQL database...

Read More
Secure AI access workflow showing authentication, authorization, and protected enterprise operations.
8-10 min
April 07, 2026

Securing Your AI-Powered Future (How Authorization Ensures Safe and Appropriate Access)

Discover how authorization in MCP ensures secure, role-based access for AI-powered business workflows...

Read More

Get In Touch

Build Your Next Big Idea with Us

From MVPs to full-scale applications, we help you bring your vision to life on time and within budget. Our expert team delivers scalable, high-quality software tailored to your business goals.