1919 *
2020 * <p>All public methods are {@code synchronized} for thread safety.
2121 */
22- public class SQLPlayerJoinTracker implements PlayerJoinTracker {
22+ public class SQLPlayerJoinTracker extends SQLHandler implements PlayerJoinTracker {
2323
2424 // MySQL / MariaDB uses INSERT … ON DUPLICATE KEY UPDATE.
2525 // PostgreSQL uses INSERT … ON CONFLICT DO NOTHING.
@@ -29,17 +29,9 @@ public class SQLPlayerJoinTracker implements PlayerJoinTracker {
2929 private final String UPSERT_MYSQL ;
3030 private final String UPSERT_POSTGRES ;
3131
32- private final CoreLogger logger ;
33- private final SQLConfig sqlConfig ;
34- private final boolean isPostgres ;
35- private Connection connection ;
36-
37- public SQLPlayerJoinTracker (CoreLogger logger , SQLConfig sqlConfig , Path pluginDataFolder )
32+ public SQLPlayerJoinTracker (CoreLogger logger , SQLConfig sqlConfig , Path dataFolder )
3833 throws SQLException , SQLDriverLoader .DriverLoadException {
39- this .logger = logger ;
40- this .sqlConfig = sqlConfig ;
41- this .isPostgres = "postgresql" .equals (sqlConfig .driver ());
42- new SQLDriverLoader (logger , pluginDataFolder ).ensureLoaded (sqlConfig .driver ());
34+ super (logger , sqlConfig , dataFolder );
4335
4436 String tableName = sqlConfig .tablePrefix () + "players_joined" ;
4537
@@ -64,13 +56,17 @@ public SQLPlayerJoinTracker(CoreLogger logger, SQLConfig sqlConfig, Path pluginD
6456 "INSERT INTO " + tableName + " (player_uuid, player_name) VALUES (?, ?) " +
6557 "ON CONFLICT (player_uuid) DO UPDATE SET player_name = EXCLUDED.player_name" ;
6658
67- setUpConnection ();
59+ }
60+
61+ @ Override
62+ protected String createTableSql () {
63+ return isPostgres ? CREATE_TABLE_POSTGRES : CREATE_TABLE_MYSQL ;
6864 }
6965
7066 @ Override
7167 public synchronized boolean hasJoined (UUID playerUuid ) {
7268 if (isConnectionInvalid ()) return false ;
73- try (PreparedStatement ps = connection .prepareStatement (SELECT_SQL )) {
69+ try (PreparedStatement ps = connection () .prepareStatement (SELECT_SQL )) {
7470 ps .setString (1 , playerUuid .toString ());
7571 try (ResultSet rs = ps .executeQuery ()) {
7672 return rs .next ();
@@ -85,76 +81,12 @@ public synchronized boolean hasJoined(UUID playerUuid) {
8581 public synchronized void markAsJoined (UUID playerUuid , String playerName ) {
8682 if (isConnectionInvalid ()) return ;
8783 String upsert = isPostgres ? UPSERT_POSTGRES : UPSERT_MYSQL ;
88- try (PreparedStatement ps = connection .prepareStatement (upsert )) {
84+ try (PreparedStatement ps = connection () .prepareStatement (upsert )) {
8985 ps .setString (1 , playerUuid .toString ());
9086 ps .setString (2 , playerName );
9187 ps .executeUpdate ();
9288 } catch (SQLException e ) {
9389 logger .severe ("[SQLPlayerJoinTracker] SQL failure marking player '" + playerName + "' (" + playerUuid + ") as joined: " + e .getMessage ());
9490 }
9591 }
96-
97- @ Override
98- public synchronized void close () throws SQLException {
99- if (connection != null && !connection .isClosed ()) {
100- connection .close ();
101- }
102- }
103-
104- // --- Internal helpers ---
105-
106- /**
107- * Opens a new connection and ensures the table exists.
108- */
109- private void setUpConnection () throws SQLException {
110- String url = buildJdbcUrl ();
111- this .connection = DriverManager .getConnection (url , sqlConfig .username (), sqlConfig .password ());
112- try (Statement stmt = connection .createStatement ()) {
113- stmt .execute (isPostgres ? CREATE_TABLE_POSTGRES : CREATE_TABLE_MYSQL );
114- }
115- logger .debug ("[SQLPlayerJoinTracker] Connected to " + sqlConfig .driver () + " at " + sqlConfig .host () + ":" + sqlConfig .port ());
116- }
117-
118- /**
119- * Returns {@code true} if the connection is unusable, attempting a reconnect first.
120- */
121- private boolean isConnectionInvalid () {
122- try {
123- if (connection == null || connection .isClosed () || !connection .isValid (sqlConfig .connectionTimeout ())) {
124- logger .info ("[SQLPlayerJoinTracker] Connection lost — attempting reconnect..." );
125- setUpConnection ();
126- }
127- return false ;
128- } catch (SQLException e ) {
129- logger .severe ("[SQLPlayerJoinTracker] Cannot reach SQL server at '" + sqlConfig .host () + "': " + e .getMessage ());
130- return true ;
131- }
132- }
133-
134- /**
135- * Builds a JDBC URL from the {@link SQLConfig}.
136- *
137- * <ul>
138- * <li>MySQL: {@code jdbc:mysql://host:port/db?...}</li>
139- * <li>MariaDB: {@code jdbc:mariadb://host:port/db?...}</li>
140- * <li>PostgreSQL: {@code jdbc:postgresql://host:port/db?...}</li>
141- * </ul>
142- */
143- private String buildJdbcUrl () {
144- // Driver is already expected to be one of "mysql", "mariadb", or "postgresql"
145- StringBuilder url = new StringBuilder ()
146- .append ("jdbc:" ).append (sqlConfig .driver ()).append ("://" )
147- .append (sqlConfig .host ()).append (':' ).append (sqlConfig .port ())
148- .append ('/' ).append (sqlConfig .database ())
149- .append ("?autoReconnect=true" )
150- .append ("&connectTimeout=" ).append (sqlConfig .connectionTimeout () * 1000 )
151- .append ("&allowPublicKeyRetrieval=true" );
152-
153- if (!isPostgres ) {
154- url .append ("&useSSL=" ).append (sqlConfig .useSSL ());
155- url .append ("&characterEncoding=utf8" );
156- }
157-
158- return url .toString ();
159- }
16092}
0 commit comments