From e34096b897fdba2bbc1518884dd0f4f405816482 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 09:09:16 +0000 Subject: [PATCH] Optimize getMainMedal to use a JOIN query - Replaces sequential queries (N+1) with a single INNER JOIN in SQLManager.getMainMedal - Adds JUnit 5 and Mockito dependencies for testing - Refactors SQLManager to support dependency injection for testing - Adds SQLManagerTest to verify the optimization and correctness --- modules/MedalCabinet/pom.xml | 18 +++++ .../mcatk/medalcabinet/sql/SQLManager.java | 17 +++- .../medalcabinet/sql/SQLManagerTest.java | 79 +++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 modules/MedalCabinet/src/test/java/com/mcatk/medalcabinet/sql/SQLManagerTest.java diff --git a/modules/MedalCabinet/pom.xml b/modules/MedalCabinet/pom.xml index 904e558..131bc08 100644 --- a/modules/MedalCabinet/pom.xml +++ b/modules/MedalCabinet/pom.xml @@ -81,5 +81,23 @@ 2.11.1 provided + + org.junit.jupiter + junit-jupiter + 5.9.2 + test + + + org.mockito + mockito-core + 4.11.0 + test + + + org.mockito + mockito-junit-jupiter + 4.11.0 + test + diff --git a/modules/MedalCabinet/src/main/java/com/mcatk/medalcabinet/sql/SQLManager.java b/modules/MedalCabinet/src/main/java/com/mcatk/medalcabinet/sql/SQLManager.java index a719230..daa06bf 100644 --- a/modules/MedalCabinet/src/main/java/com/mcatk/medalcabinet/sql/SQLManager.java +++ b/modules/MedalCabinet/src/main/java/com/mcatk/medalcabinet/sql/SQLManager.java @@ -15,6 +15,14 @@ public static SQLManager getInstance() { return instance == null ? instance = new SQLManager() : instance; } + public static void setInstance(SQLManager newInstance) { + instance = newInstance; + } + + protected SQLManager(Connection connection) { + this.connection = connection; + } + private SQLManager() { connectMySQL(); } @@ -168,12 +176,17 @@ public boolean setMainMedal(String playerID, String medalID) { public Medal getMainMedal(String playerID) { Medal medal = null; try (PreparedStatement ps = connection.prepareStatement( - "SELECT medal_id FROM `player_main_medal` WHERE player_id = ?" + "SELECT m.* FROM medal m INNER JOIN player_main_medal pmm ON m.medal_id = pmm.medal_id WHERE pmm.player_id = ?" )) { ps.setString(1, playerID); ResultSet rs = ps.executeQuery(); if (rs.next()) { - medal = getMedal(rs.getString("medal_id")); + medal = new Medal( + rs.getString("medal_id"), + rs.getString("medal_name"), + rs.getString("medal_material"), + rs.getString("medal_description") + ); } } catch (SQLException e) { e.printStackTrace(); diff --git a/modules/MedalCabinet/src/test/java/com/mcatk/medalcabinet/sql/SQLManagerTest.java b/modules/MedalCabinet/src/test/java/com/mcatk/medalcabinet/sql/SQLManagerTest.java new file mode 100644 index 0000000..5a7af95 --- /dev/null +++ b/modules/MedalCabinet/src/test/java/com/mcatk/medalcabinet/sql/SQLManagerTest.java @@ -0,0 +1,79 @@ +package com.mcatk.medalcabinet.sql; + +import com.mcatk.medalcabinet.medal.Medal; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class SQLManagerTest { + + @Mock + private Connection connection; + + @Mock + private PreparedStatement preparedStatement1; + @Mock + private PreparedStatement preparedStatement2; + + @Mock + private ResultSet resultSet1; + @Mock + private ResultSet resultSet2; + + private SQLManager sqlManager; + + @BeforeEach + public void setUp() { + // Use the protected constructor to inject the mock connection + // We can do this because the test class is in the same package + sqlManager = new SQLManager(connection); + // Set the singleton instance + SQLManager.setInstance(sqlManager); + } + + @Test + public void testGetMainMedal_Optimized() throws SQLException { + // Arrange + String playerId = "testPlayer"; + String medalId = "medal123"; + String medalName = "Gold Medal"; + String medalMat = "GOLD_INGOT"; + String medalDesc = "A shiny gold medal"; + + // Mock the JOIN query: SELECT m.* FROM medal m INNER JOIN player_main_medal pmm ... + when(connection.prepareStatement(contains("INNER JOIN"))).thenReturn(preparedStatement1); + when(preparedStatement1.executeQuery()).thenReturn(resultSet1); + when(resultSet1.next()).thenReturn(true); + when(resultSet1.getString("medal_id")).thenReturn(medalId); + when(resultSet1.getString("medal_name")).thenReturn(medalName); + when(resultSet1.getString("medal_material")).thenReturn(medalMat); + when(resultSet1.getString("medal_description")).thenReturn(medalDesc); + + // Act + Medal result = sqlManager.getMainMedal(playerId); + + // Assert + assertNotNull(result); + assertEquals(medalId, result.getId()); + assertEquals(medalName, result.getName()); + + // Verify that prepareStatement was called only once (optimized behavior) + verify(connection, times(1)).prepareStatement(anyString()); + + // Specific verification + verify(connection).prepareStatement(contains("INNER JOIN")); + } +}