Sending daily summary emails helps people stay on top of what’s happened without checking in all day. As updates, changes, and entries build up, it makes more sense to group everything into one message. Spring Boot lets you set this up by running scheduled jobs that pull in recent data, format it with a template, and send it out automatically.
Collecting and Organizing User Activity
Before anything can be sent in an email, you need to have something worth sending. That starts with tracking what users are doing throughout the day. You want a system that quietly logs events without slowing anything down, stores them in a way that’s easy to query later, and gives you just enough detail to make the email feel specific to each person. The best time to capture this data is as the activity happens, not at the end when it’s time to send something.
When the data is in place, you’ll need a way to group it by user, sort it by time, and filter it so you don’t accidentally repeat things or leave them out.
Storing Daily User Events
The most direct way to record activity is with a database table that tracks user actions. This lets you log what happened, who did it, and when. A simple JPA entity like this gets the job done:
The action
field can be plain text or a short label pulled from a known list of activity types. Either one works, as long as it gives enough context later when someone reads it in a sentence or bullet point. Keep the timestamps precise and in UTC so they sort properly and line up with the email scheduling later.
Note: Make sure you’re using jakarta.persistence.*
instead of the old javax.persistence.*
imports. Spring Boot 3 and later use the Jakarta namespace for JPA.
This method can be called any time something worth reporting happens. That could be a user creating something, editing a record, completing a task, or interacting with another part of the system.
If you’re logging multiple events quickly, batching saves time. You can use saveAll
to store them in one round trip.
This helps with performance and also makes it easier to group related actions together, so you’re not scattering partial data across the timeline.
Preparing The Summary Content
After events have been collected, they need to be pulled together in a way that fits the daily format. It helps to grab everything from a fixed time range and then group it by user so each person only gets their own activity.
You can start by writing a method that selects all the day’s events, either with a native SQL query or JPQL. This version grabs everything between two time markers:
It gives you one long list with entries for everyone, so the next step is to split them up. Grouping by user ID helps prepare separate summaries:
This gives you a map where each user ID points to their list of actions. If you need to filter out inactive users, you can drop any groups with empty lists or skip users who haven’t triggered any trackable events in the last day.
The messages inside those activity records might be short and raw, like “CREATED_TASK”
or “LOGIN_SUCCESS”
. If you want to write friendly summaries, you can translate those into plain sentences before they get passed to the template. A map or helper method works well for that:
Loop through the grouped activities and map them to readable lines before handing them off for email formatting.
Choosing The Right Time Range
Daily summaries only make sense if you’re looking at the right slice of time. If you go too wide, you’ll include old events. Too narrow, and something might get missed. Most setups either pull from midnight to the current time or from the end of the previous batch to now.
Here’s how to get midnight of the current day:
This works well if the summary runs in the late evening or early morning. It lines up with how people think about their activity for a day.
If you want more control, you can track the last sent time per user and go from that timestamp instead. That’s especially useful if the job fails partway through or if users have different time zones.
You could use a table like this to store the last successful summary per user:
Then before you send the next one, fetch that record and use it to calculate the window:
After sending the summary, update the record:
That gives you a reliable loop that knows what’s been covered and what hasn’t. It also leaves room for sending missed summaries later without overlap.
Sending Emails on a Schedule with Templates
After the data’s grouped and ready, the next step is getting it out the door. This runs on its own after setup and doesn’t wait on someone to trigger it. Spring Boot handles the timing, and the email content gets filled in from the daily activity that was already collected.
Before you rely on this job to deliver summaries, it helps to check that your mail settings work and that messages are making it through. That avoids surprises later when the scheduled task runs by itself.
Setting Up The Scheduled Task
A scheduled task runs in the background on a fixed schedule. Spring Boot makes it easy to set up with an annotation. To get started, you just mark your main class like this:
Now that scheduling is active, you can add a method that runs on a regular timer. If you want the job to go early in the morning, setting it for five minutes after midnight gives the system a moment to settle before sending anything.
The cron expression controls the timing. This one fires daily at 12:05 a.m. You can also lock it to a specific timezone so it runs when you expect:
It’s helpful to track how long the job takes, especially if you’re sending a lot of emails. Add some basic timing to your method to see how it performs.
This creates a full loop where data gets collected earlier in the day, and then this task grabs it, prepares the content, and sends it without anyone needing to touch it.
Rendering Templated Email Content
A plain text email can work, but it’s easier to read when the format is structured and clean. That’s where templates come in. They let you design a layout that holds your content, with placeholders that get replaced with real data when the message is built. Thymeleaf is a good fit here, since it’s already supported in Spring Boot and doesn’t need much configuration.
Place your email template in resources/templates
. Here's a basic layout:
This pulls in a list of events and builds a short bulleted summary. You pass that list from your Java code when it’s time to send the message. The template stays the same no matter what gets inserted.
To fill in the data, create a method that prepares the context and processes the template:
If you want to personalize the message further, you can pass in other values like the user’s name or the date. Just add more variables to the context and update the HTML to match.
Then in your template, reference those values:
That lets you keep the message readable while still showing real-time data, all wrapped in a layout that doesn’t need to change unless you want to redesign it later.
Sending The Email
With the content ready, it’s time to send the message. Spring Boot uses JavaMailSender
for email. You point it to your provider’s SMTP server and pass in the details it needs.
Put these into your application.properties
file:
Then you’re ready to write the mail-sending logic. Inject the sender via the constructor and use a helper class to build the message with HTML content.
If a message fails, it won’t bring down the entire batch. You can handle retries later or skip over that one and move on. Either way, your scheduler keeps running.
To bring it together, here’s a method that pulls everything for one user:
This loops through the prepared list of users and sends one email per person, based on the events pulled from the last daily cycle. You get something that runs in the background, formats itself based on real user activity, and shows up in inboxes like clockwork.
Conclusion
Everything runs on a simple loop. Activity gets logged as it happens, grouped later by user, and shaped into a message through a template. From there, the scheduler takes over and delivers the email without anyone stepping in. It all comes together through timing, structure, and a few parts working quietly in the background.
