Skip to content

Commit 0efa241

Browse files
committed
use Spring JDBC for warn, help and message cache repositories
1 parent 3b70723 commit 0efa241

40 files changed

+565
-494
lines changed

build.gradle.kts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.*
44
plugins {
55
java
66
id("com.github.johnrengelman.shadow") version "7.1.2"
7+
id("org.springframework.boot") version "2.7.3"
78
id("io.spring.dependency-management") version "1.0.11.RELEASE"
89
checkstyle
910
}
@@ -59,8 +60,9 @@ dependencies {
5960
// Sentry
6061
implementation("io.sentry:sentry:6.3.0")
6162

62-
// Spring Boot
63-
implementation("org.springframework.boot:spring-boot-starter-web:2.7.0")
63+
// Spring
64+
implementation("org.springframework.boot:spring-boot-starter-web")
65+
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
6466
}
6567

6668
tasks.withType<Jar> {

src/main/java/net/javadiscord/javabot/api/routes/leaderboard/help_experience/HelpExperienceLeaderboardController.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import net.javadiscord.javabot.api.exception.InvalidEntityIdException;
77
import net.javadiscord.javabot.api.routes.CaffeineCache;
88
import net.javadiscord.javabot.api.routes.leaderboard.help_experience.model.ExperienceUserData;
9-
import net.javadiscord.javabot.data.config.BotConfig;
109
import net.javadiscord.javabot.systems.help.HelpExperienceService;
1110
import net.javadiscord.javabot.util.Pair;
1211
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,34 +19,29 @@
2019
import java.util.List;
2120
import java.util.concurrent.TimeUnit;
2221

23-
import javax.sql.DataSource;
24-
2522
/**
2623
* Handles all GET-Requests on the guilds/{guild_id}/leaderboard/experience/ route.
2724
*/
2825
@RestController
2926
public class HelpExperienceLeaderboardController extends CaffeineCache<Pair<Long, Integer>, List<ExperienceUserData>> {
3027
private static final int PAGE_AMOUNT = 8;
3128
private final JDA jda;
32-
private final DataSource dataSource;
33-
private final BotConfig botConfig;
29+
private final HelpExperienceService helpExperienceService;
3430

3531
/**
3632
* The constructor of this class which initializes the {@link Caffeine} cache.
3733
*
3834
* @param jda The {@link JDA} instance to use.
39-
* @param botConfig The main configuration of the bot
40-
* @param dataSource A factory for connections to the main database
35+
* @param helpExperienceService Service object that handles Help Experience Transactions.
4136
*/
4237
@Autowired
43-
public HelpExperienceLeaderboardController(final JDA jda, DataSource dataSource, BotConfig botConfig) {
38+
public HelpExperienceLeaderboardController(final JDA jda, HelpExperienceService helpExperienceService) {
4439
super(Caffeine.newBuilder()
4540
.expireAfterWrite(10, TimeUnit.MINUTES)
4641
.build()
4742
);
4843
this.jda = jda;
49-
this.dataSource = dataSource;
50-
this.botConfig = botConfig;
44+
this.helpExperienceService = helpExperienceService;
5145
}
5246

5347
/**
@@ -67,10 +61,9 @@ public ResponseEntity<List<ExperienceUserData>> getHelpExperienceLeaderboard(
6761
if (guild == null) {
6862
throw new InvalidEntityIdException(Guild.class, "You've provided an invalid guild id!");
6963
}
70-
HelpExperienceService service = new HelpExperienceService(dataSource, botConfig);
7164
List<ExperienceUserData> members = getCache().getIfPresent(new Pair<>(guild.getIdLong(), page));
7265
if (members == null || members.isEmpty()) {
73-
members = service.getTopAccounts(PAGE_AMOUNT, page).stream()
66+
members = helpExperienceService.getTopAccounts(PAGE_AMOUNT, page).stream()
7467
.map(p -> ExperienceUserData.of(p, jda.retrieveUserById(p.getUserId()).complete()))
7568
.toList();
7669
getCache().put(new Pair<>(guild.getIdLong(), page), members);

src/main/java/net/javadiscord/javabot/api/routes/user_profile/UserProfileController.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import net.javadiscord.javabot.systems.user_preferences.model.UserPreference;
2121
import net.javadiscord.javabot.util.Pair;
2222
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.dao.DataAccessException;
2324
import org.springframework.http.HttpStatus;
2425
import org.springframework.http.ResponseEntity;
2526
import org.springframework.web.bind.annotation.GetMapping;
@@ -45,6 +46,8 @@ public class UserProfileController extends CaffeineCache<Pair<Long, Long>, UserP
4546
private final UserPreferenceService preferenceService;
4647
private final DataSource dataSource;
4748
private final BotConfig botConfig;
49+
private final HelpExperienceService helpExperienceService;
50+
private final WarnRepository warnRepository;
4851

4952
/**
5053
* The constructor of this class which initializes the {@link Caffeine} cache.
@@ -54,9 +57,11 @@ public class UserProfileController extends CaffeineCache<Pair<Long, Long>, UserP
5457
* @param preferenceService The {@link UserPreferenceService}
5558
* @param botConfig The main configuration of the bot
5659
* @param dataSource A factory for connections to the main database
60+
* @param helpExperienceService Service object that handles Help Experience Transactions.
61+
* @param warnRepository DAO for interacting with the set of {@link Warn} objects.
5762
*/
5863
@Autowired
59-
public UserProfileController(final JDA jda, QOTWPointsService qotwPointsService, UserPreferenceService preferenceService, BotConfig botConfig, DataSource dataSource) {
64+
public UserProfileController(final JDA jda, QOTWPointsService qotwPointsService, UserPreferenceService preferenceService, BotConfig botConfig, DataSource dataSource, HelpExperienceService helpExperienceService, WarnRepository warnRepository) {
6065
super(Caffeine.newBuilder()
6166
.expireAfterWrite(10, TimeUnit.MINUTES)
6267
.build()
@@ -66,6 +71,8 @@ public UserProfileController(final JDA jda, QOTWPointsService qotwPointsService,
6671
this.preferenceService = preferenceService;
6772
this.dataSource = dataSource;
6873
this.botConfig = botConfig;
74+
this.helpExperienceService = helpExperienceService;
75+
this.warnRepository = warnRepository;
6976
}
7077

7178
/**
@@ -101,21 +108,19 @@ public ResponseEntity<UserProfileData> getUserProfile(
101108
QOTWAccount qotwAccount = qotwPointsService.getOrCreateAccount(user.getIdLong());
102109
data.setQotwAccount(qotwAccount);
103110
// Help Account
104-
HelpExperienceService helpService = new HelpExperienceService(dataSource, botConfig);
105-
HelpAccount helpAccount = helpService.getOrCreateAccount(user.getIdLong());
111+
HelpAccount helpAccount = helpExperienceService.getOrCreateAccount(user.getIdLong());
106112
data.setHelpAccount(HelpAccountData.of(helpAccount, guild));
107113
// User Preferences
108114
List<UserPreference> preferences = Arrays.stream(Preference.values()).map(p -> preferenceService.getOrCreate(user.getIdLong(), p)).toList();
109115
data.setPreferences(preferences);
110116
// User Warns
111-
WarnRepository warnRepository = new WarnRepository(con);
112117
LocalDateTime cutoff = LocalDateTime.now().minusDays(botConfig.get(guild).getModerationConfig().getWarnTimeoutDays());
113118
data.setWarns(warnRepository.getWarnsByUserId(user.getIdLong(), cutoff));
114119
// Insert into cache
115120
getCache().put(new Pair<>(guild.getIdLong(), user.getIdLong()), data);
116121
}
117122
return new ResponseEntity<>(data, HttpStatus.OK);
118-
} catch (SQLException e) {
123+
} catch (DataAccessException|SQLException e) {
119124
throw new InternalServerException("An internal server error occurred.", e);
120125
}
121126
}

src/main/java/net/javadiscord/javabot/data/h2db/message_cache/MessageCache.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import net.dv8tion.jda.api.requests.restaction.MessageAction;
88
import net.javadiscord.javabot.data.config.BotConfig;
99
import net.javadiscord.javabot.data.config.guild.MessageCacheConfig;
10-
import net.javadiscord.javabot.data.h2db.DbHelper;
1110
import net.javadiscord.javabot.data.h2db.message_cache.dao.MessageCacheRepository;
1211
import net.javadiscord.javabot.data.h2db.message_cache.model.CachedMessage;
1312
import net.javadiscord.javabot.systems.user_commands.IdCalculatorCommand;
@@ -18,16 +17,14 @@
1817
import java.io.ByteArrayInputStream;
1918
import java.io.InputStream;
2019
import java.nio.charset.StandardCharsets;
21-
import java.sql.Connection;
22-
import java.sql.SQLException;
2320
import java.time.Instant;
2421
import java.time.ZoneOffset;
2522
import java.time.format.DateTimeFormatter;
2623
import java.util.ArrayList;
2724
import java.util.List;
25+
import java.util.concurrent.ExecutorService;
2826

29-
import javax.sql.DataSource;
30-
27+
import org.springframework.dao.DataAccessException;
3128
import org.springframework.stereotype.Service;
3229

3330
/**
@@ -48,33 +45,36 @@ public class MessageCache {
4845
*/
4946
public int messageCount = 0;
5047

48+
private final ExecutorService asyncPool;
5149
private final BotConfig botConfig;
52-
private final DbHelper dbHelper;
50+
private final MessageCacheRepository cacheRepository;
5351

5452
/**
5553
* Creates a new messages & loads messages from the DB into a List.
5654
* @param botConfig The main configuration of the bot
57-
* @param dataSource A factory for connections to the main database
58-
* @param dbHelper An object managing databse operations
55+
* @param cacheRepository Dao class that represents the QOTW_POINTS SQL Table.
56+
* @param asyncPool The main thread pool for asynchronous operations
5957
*/
60-
public MessageCache(BotConfig botConfig, DataSource dataSource, DbHelper dbHelper) {
58+
public MessageCache(BotConfig botConfig, MessageCacheRepository cacheRepository, ExecutorService asyncPool) {
59+
this.asyncPool = asyncPool;
6160
this.botConfig = botConfig;
62-
this.dbHelper = dbHelper;
63-
try (Connection con = dataSource.getConnection()) {
64-
cache = new MessageCacheRepository(con).getAll();
65-
} catch (SQLException e) {
61+
this.cacheRepository = cacheRepository;
62+
try {
63+
cache = cacheRepository.getAll();
64+
} catch (DataAccessException e) {
6665
ExceptionLogger.capture(e, getClass().getSimpleName());
6766
log.error("Something went wrong during retrieval of stored messages.");
6867
}
68+
6969
}
7070

7171
/**
7272
* Synchronizes Messages saved in the Database with what is currently stored in memory.
7373
*/
7474
public void synchronize() {
75-
dbHelper.doDaoAction(MessageCacheRepository::new, dao -> {
76-
dao.delete(cache.size());
77-
dao.insertList(cache);
75+
asyncPool.execute(()->{
76+
cacheRepository.delete(cache.size());
77+
cacheRepository.insertList(cache);
7878
messageCount = 0;
7979
log.info("Synchronized Database with local Cache.");
8080
});

src/main/java/net/javadiscord/javabot/data/h2db/message_cache/dao/MessageCacheRepository.java

Lines changed: 41 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,22 @@
33
import lombok.RequiredArgsConstructor;
44
import net.javadiscord.javabot.data.h2db.message_cache.model.CachedMessage;
55
import org.jetbrains.annotations.NotNull;
6+
import org.springframework.dao.DataAccessException;
7+
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
8+
import org.springframework.jdbc.core.JdbcTemplate;
9+
import org.springframework.jdbc.core.RowMapper;
10+
import org.springframework.stereotype.Repository;
611

712
import java.sql.*;
8-
import java.util.ArrayList;
913
import java.util.List;
1014

1115
/**
1216
* Dao class that represents the QOTW_POINTS SQL Table.
1317
*/
1418
@RequiredArgsConstructor
19+
@Repository
1520
public class MessageCacheRepository {
16-
private final Connection con;
21+
private final JdbcTemplate jdbcTemplate;
1722

1823
/**
1924
* Inserts a new {@link CachedMessage} object.
@@ -22,16 +27,11 @@ public class MessageCacheRepository {
2227
* @return Whether there were rows affected by this process.
2328
* @throws SQLException If an error occurs.
2429
*/
25-
public boolean insert(CachedMessage message) throws SQLException {
26-
try (PreparedStatement stmt = con.prepareStatement("INSERT INTO message_cache (message_id, author_id, message_content) VALUES (?, ?, ?)",
27-
Statement.RETURN_GENERATED_KEYS
28-
)) {
29-
stmt.setLong(1, message.getMessageId());
30-
stmt.setLong(2, message.getAuthorId());
31-
stmt.setString(3, message.getMessageContent());
32-
int rows = stmt.executeUpdate();
33-
return rows > 0;
34-
}
30+
public boolean insert(CachedMessage message) throws DataAccessException {
31+
int rows = jdbcTemplate.update(
32+
"INSERT INTO message_cache (message_id, author_id, message_content) VALUES (?, ?, ?)",
33+
message.getMessageId(), message.getAuthorId(), message.getMessageContent());
34+
return rows > 0;
3535
}
3636

3737
/**
@@ -40,19 +40,23 @@ public boolean insert(CachedMessage message) throws SQLException {
4040
* @param messages The List to insert.
4141
* @throws SQLException If an error occurs.
4242
*/
43-
public void insertList(@NotNull List<CachedMessage> messages) throws SQLException {
44-
try (PreparedStatement stmt = con.prepareStatement("MERGE INTO message_cache (message_id, author_id, message_content) VALUES (?, ?, ?)",
45-
Statement.RETURN_GENERATED_KEYS
46-
)) {
47-
con.setAutoCommit(false);
48-
for (CachedMessage msg : messages) {
49-
stmt.setLong(1, msg.getMessageId());
50-
stmt.setLong(2, msg.getAuthorId());
51-
stmt.setString(3, msg.getMessageContent());
52-
stmt.executeUpdate();
53-
}
54-
con.commit();
55-
}
43+
public void insertList(@NotNull List<CachedMessage> messages) throws DataAccessException {
44+
jdbcTemplate.batchUpdate("MERGE INTO message_cache (message_id, author_id, message_content) VALUES (?, ?, ?)",
45+
new BatchPreparedStatementSetter() {
46+
@Override
47+
public void setValues(PreparedStatement stmt, int i) throws SQLException {
48+
CachedMessage msg = messages.get(i);
49+
stmt.setLong(1, msg.getMessageId());
50+
stmt.setLong(2, msg.getAuthorId());
51+
stmt.setString(3, msg.getMessageContent());
52+
stmt.executeUpdate();
53+
}
54+
55+
@Override
56+
public int getBatchSize() {
57+
return messages.size();
58+
}
59+
});
5660
}
5761

5862
/**
@@ -62,15 +66,10 @@ public void insertList(@NotNull List<CachedMessage> messages) throws SQLExceptio
6266
* @return Whether there were rows affected by this process.
6367
* @throws SQLException If an error occurs.
6468
*/
65-
public boolean update(@NotNull CachedMessage message) throws SQLException {
66-
try (PreparedStatement stmt = con.prepareStatement("UPDATE message_cache SET message_content = ? WHERE message_id = ?",
67-
Statement.RETURN_GENERATED_KEYS
68-
)) {
69-
stmt.setString(1, message.getMessageContent());
70-
stmt.setLong(2, message.getMessageId());
71-
int rows = stmt.executeUpdate();
72-
return rows > 0;
73-
}
69+
public boolean update(@NotNull CachedMessage message) throws DataAccessException {
70+
int rows = jdbcTemplate.update("UPDATE message_cache SET message_content = ? WHERE message_id = ?",
71+
message.getMessageContent(), message.getMessageId());
72+
return rows > 0;
7473
}
7574

7675
/**
@@ -80,14 +79,9 @@ public boolean update(@NotNull CachedMessage message) throws SQLException {
8079
* @return Whether there were rows affected by this process.
8180
* @throws SQLException If an error occurs.
8281
*/
83-
public boolean delete(long messageId) throws SQLException {
84-
try (PreparedStatement stmt = con.prepareStatement("DELETE FROM message_cache WHERE message_id = ?",
85-
Statement.RETURN_GENERATED_KEYS
86-
)) {
87-
stmt.setLong(1, messageId);
88-
int rows = stmt.executeUpdate();
89-
return rows > 0;
90-
}
82+
public boolean delete(long messageId) throws DataAccessException {
83+
int rows = jdbcTemplate.update("DELETE FROM message_cache WHERE message_id = ?", messageId);
84+
return rows > 0;
9185
}
9286

9387
/**
@@ -96,15 +90,8 @@ public boolean delete(long messageId) throws SQLException {
9690
* @return A {@link List} of {@link CachedMessage}s.
9791
* @throws SQLException If anything goes wrong.
9892
*/
99-
public List<CachedMessage> getAll() throws SQLException {
100-
try (PreparedStatement s = con.prepareStatement("SELECT * FROM message_cache")) {
101-
ResultSet rs = s.executeQuery();
102-
List<CachedMessage> cachedMessages = new ArrayList<>();
103-
while (rs.next()) {
104-
cachedMessages.add(this.read(rs));
105-
}
106-
return cachedMessages;
107-
}
93+
public List<CachedMessage> getAll() throws DataAccessException {
94+
return jdbcTemplate.query("SELECT * FROM message_cache",(RowMapper<CachedMessage>) (rs, rowNum) -> this.read(rs));
10895
}
10996

11097
/**
@@ -114,14 +101,9 @@ public List<CachedMessage> getAll() throws SQLException {
114101
* @return If any rows we're affected.
115102
* @throws SQLException If anything goes wrong.
116103
*/
117-
public boolean delete(int amount) throws SQLException {
118-
try (PreparedStatement stmt = con.prepareStatement("DELETE FROM message_cache LIMIT ?",
119-
Statement.RETURN_GENERATED_KEYS
120-
)) {
121-
stmt.setInt(1, amount);
122-
int rows = stmt.executeUpdate();
123-
return rows > 0;
124-
}
104+
public boolean delete(int amount) throws DataAccessException {
105+
int rows = jdbcTemplate.update("DELETE FROM message_cache LIMIT ?", amount);
106+
return rows > 0;
125107
}
126108

127109
private CachedMessage read(ResultSet rs) throws SQLException {

src/main/java/net/javadiscord/javabot/listener/StateListener.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import net.javadiscord.javabot.data.h2db.DbActions;
1616
import net.javadiscord.javabot.systems.help.ChannelSemanticCheck;
1717
import net.javadiscord.javabot.systems.help.HelpChannelUpdater;
18+
import net.javadiscord.javabot.systems.help.HelpExperienceService;
1819
import net.javadiscord.javabot.systems.notification.NotificationService;
1920
import net.javadiscord.javabot.systems.staff_commands.tags.CustomTagManager;
2021
import net.javadiscord.javabot.util.ExceptionLogger;
@@ -40,6 +41,7 @@ public class StateListener extends ListenerAdapter {
4041
private final BotConfig botConfig;
4142
private final ScheduledExecutorService asyncPool;
4243
private final DbActions dbActions;
44+
private final HelpExperienceService helpExperienceService;
4345

4446
@Override
4547
public void onReady(@NotNull ReadyEvent event) {
@@ -52,7 +54,7 @@ public void onReady(@NotNull ReadyEvent event) {
5254
// Schedule the help channel updater to run periodically for each guild.
5355
HelpConfig helpConfig = botConfig.get(guild).getHelpConfig();
5456
asyncPool.scheduleAtFixedRate(
55-
new HelpChannelUpdater(guild, botConfig, dbActions, asyncPool, channelSemanticChecks),
57+
new HelpChannelUpdater(guild, botConfig, dbActions, asyncPool, channelSemanticChecks, helpExperienceService),
5658
5,
5759
helpConfig.getUpdateIntervalSeconds(),
5860
TimeUnit.SECONDS

0 commit comments

Comments
 (0)