Skip to content

Commit f49b5f8

Browse files
committed
Enhance MySQL data handling
1 parent 771ca51 commit f49b5f8

File tree

3 files changed

+113
-53
lines changed

3 files changed

+113
-53
lines changed

AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,8 +739,16 @@ public void loadUserAPI(UserStorage storageType) {
739739
database = new Database(this, "Users", table);
740740
table.addCustomColumns();
741741
} else if (storageType.equals(UserStorage.MYSQL)) {
742-
setMysql(new MySQL(javaPlugin, javaPlugin.getName() + "_Users",
743-
getOptions().getYmlConfig().getData().getConfigurationSection("MySQL")));
742+
if (getOptions().getYmlConfig().getData().contains("Database")) {
743+
setMysql(new MySQL(javaPlugin, javaPlugin.getName() + "_Users",
744+
getOptions().getYmlConfig().getData().getConfigurationSection("Database")));
745+
} else {
746+
getLogger().warning(
747+
"Deprecated MySQL config detected, please update your mysql config to use 'Database' section");
748+
setMysql(new MySQL(javaPlugin, javaPlugin.getName() + "_Users",
749+
getOptions().getYmlConfig().getData().getConfigurationSection("MySQL")));
750+
}
751+
744752
} else if (storageType.equals(UserStorage.FLAT)) {
745753
getLogger().severe("Detected using FLAT storage, this will be removed in the future!");
746754
}

AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/mysql/MySQL.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,18 @@ public void forEachUser(java.util.function.BiConsumer<UUID, ArrayList<Column>> p
190190
int processed = 0;
191191
final int pageSize = 500; // tune 250/500/1000
192192

193-
String lastUuid = ""; // start lower than any real uuid string
193+
String lastUuid = ""; // seek cursor (string form)
194194

195195
while (true) {
196196
int rowsThisPage = 0;
197197

198-
String sqlStr = "SELECT * FROM " + qi(tableName) + " WHERE " + qi("uuid") + " > ?" + " ORDER BY "
199-
+ qi("uuid") + " ASC" + " LIMIT " + pageSize + ";";
198+
// Read a page into memory, CLOSE connection, then run callbacks.
199+
// This prevents holding a DB connection while user code runs.
200+
final ArrayList<java.util.AbstractMap.SimpleEntry<UUID, ArrayList<Column>>> page = new ArrayList<>(
201+
pageSize);
202+
203+
String sqlStr = "SELECT * FROM " + qi(tableName) + " WHERE " + qi("uuid") + " > ? ORDER BY " + qi("uuid")
204+
+ " ASC LIMIT " + pageSize + ";";
200205

201206
try (Connection conn = mysql.getConnectionManager().getConnection();
202207
PreparedStatement ps = conn.prepareStatement(sqlStr, ResultSet.TYPE_FORWARD_ONLY,
@@ -274,8 +279,12 @@ public void forEachUser(java.util.function.BiConsumer<UUID, ArrayList<Column>> p
274279
if (uuid != null && uuidStrForSeek != null) {
275280
rowsThisPage++;
276281
processed++;
277-
lastUuid = uuidStrForSeek; // advance seek cursor
278-
perUser.accept(uuid, cols);
282+
283+
// advance seek cursor based on the last row we read
284+
lastUuid = uuidStrForSeek;
285+
286+
// store for later callback (after connection closes)
287+
page.add(new java.util.AbstractMap.SimpleEntry<>(uuid, cols));
279288
}
280289
}
281290
}
@@ -285,6 +294,16 @@ public void forEachUser(java.util.function.BiConsumer<UUID, ArrayList<Column>> p
285294
break;
286295
}
287296

297+
// Run callbacks OUTSIDE of DB resources
298+
for (java.util.AbstractMap.SimpleEntry<UUID, ArrayList<Column>> entry : page) {
299+
try {
300+
perUser.accept(entry.getKey(), entry.getValue());
301+
} catch (Throwable t) {
302+
// Don't kill the whole scan if user-code throws
303+
debug(t);
304+
}
305+
}
306+
288307
// no more rows
289308
if (rowsThisPage == 0) {
290309
break;

AdvancedCore/src/main/java/com/bencodez/advancedcore/api/user/userstorage/sql/UserTable.java

Lines changed: 79 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -56,84 +56,117 @@ public UserTable(AdvancedCorePlugin plugin, String name, Collection<Column> colu
5656
*/
5757
public void forEachUser(BiConsumer<UUID, ArrayList<Column>> perUser, Consumer<Integer> onFinished) {
5858
int processed = 0;
59-
String query = "SELECT * FROM " + getName() + ";";
59+
final String query = "SELECT * FROM " + getName() + ";";
6060

61-
PreparedStatement s = null;
62-
ResultSet rs = null;
61+
try (PreparedStatement ps = sqLite.getSQLConnection().prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY,
62+
ResultSet.CONCUR_READ_ONLY); ResultSet rs = ps.executeQuery()) {
6363

64-
try {
65-
s = sqLite.getSQLConnection().prepareStatement(query);
66-
rs = s.executeQuery();
64+
// Helps on some JDBC drivers (SQLite varies, but harmless)
65+
try {
66+
ps.setFetchSize(500);
67+
} catch (Exception ignored) {
68+
}
69+
70+
// Compute metadata ONCE
71+
final ResultSetMetaData meta = rs.getMetaData();
72+
final int colCount = meta.getColumnCount();
73+
74+
// Precompute how each column should be read
75+
final String[] colNames = new String[colCount + 1];
76+
final byte[] kind = new byte[colCount + 1]; // 0=string, 1=int, 2=bool
77+
int uuidIndex = -1;
78+
79+
for (int i = 1; i <= colCount; i++) {
80+
String name = meta.getColumnLabel(i);
81+
colNames[i] = name;
82+
83+
if ("uuid".equalsIgnoreCase(name)) {
84+
uuidIndex = i;
85+
}
86+
87+
if (plugin.getUserManager().getDataManager().isInt(name)) {
88+
kind[i] = 1;
89+
} else if (plugin.getUserManager().getDataManager().isBoolean(name)) {
90+
kind[i] = 2;
91+
} else {
92+
kind[i] = 0;
93+
}
94+
}
6795

6896
while (rs.next()) {
69-
final ResultSetMetaData meta = rs.getMetaData();
70-
ArrayList<Column> cols = new ArrayList<>();
7197
UUID uuid = null;
7298

73-
for (int i = 1; i <= meta.getColumnCount(); i++) {
74-
String columnName = meta.getColumnLabel(i);
99+
// Fast path: read uuid column directly (avoid scanning for it)
100+
if (uuidIndex > 0) {
101+
String uuidStr = rs.getString(uuidIndex);
102+
if (uuidStr != null && !uuidStr.isEmpty() && !"null".equalsIgnoreCase(uuidStr)) {
103+
try {
104+
uuid = UUID.fromString(uuidStr);
105+
} catch (IllegalArgumentException ignored) {
106+
// bad uuid; skip row below
107+
}
108+
}
109+
}
110+
111+
// Build cols only if we have a valid uuid (saves a ton if db has junk)
112+
if (uuid == null) {
113+
continue;
114+
}
115+
116+
ArrayList<Column> cols = new ArrayList<>(colCount);
117+
118+
for (int i = 1; i <= colCount; i++) {
119+
String columnName = colNames[i];
75120
Column rCol;
76121

77-
if (plugin.getUserManager().getDataManager().isInt(columnName)) {
122+
switch (kind[i]) {
123+
case 1: { // int
78124
rCol = new Column(columnName, DataType.INTEGER);
125+
int v;
79126
try {
80-
rCol.setValue(new DataValueInt(rs.getInt(i)));
127+
v = rs.getInt(i);
128+
if (rs.wasNull())
129+
v = 0;
81130
} catch (Exception e) {
82131
String data = rs.getString(i);
83132
if (data != null) {
84133
try {
85-
rCol.setValue(new DataValueInt(Integer.parseInt(data)));
134+
v = Integer.parseInt(data);
86135
} catch (NumberFormatException ex) {
87-
rCol.setValue(new DataValueInt(0));
136+
v = 0;
88137
}
89138
} else {
90-
rCol.setValue(new DataValueInt(0));
139+
v = 0;
91140
}
92141
}
93-
} else if (plugin.getUserManager().getDataManager().isBoolean(columnName)) {
142+
rCol.setValue(new DataValueInt(v));
143+
break;
144+
}
145+
case 2: { // bool
94146
rCol = new Column(columnName, DataType.BOOLEAN);
147+
// Keep same semantics as your original: Boolean.valueOf(String)
95148
rCol.setValue(new DataValueBoolean(Boolean.valueOf(rs.getString(i))));
96-
} else {
149+
break;
150+
}
151+
default: { // string
97152
rCol = new Column(columnName, DataType.STRING);
98-
String val = rs.getString(i);
99-
rCol.setValue(new DataValueString(val));
100-
101-
if ("uuid".equalsIgnoreCase(columnName)) {
102-
if (val != null && !val.isEmpty() && !"null".equalsIgnoreCase(val)) {
103-
try {
104-
uuid = UUID.fromString(val);
105-
} catch (IllegalArgumentException ignored) {
106-
// bad uuid in db; skip row below
107-
}
108-
}
109-
}
153+
rCol.setValue(new DataValueString(rs.getString(i)));
154+
break;
155+
}
110156
}
111157

112158
cols.add(rCol);
113159
}
114160

115-
if (uuid != null) {
116-
processed++;
117-
perUser.accept(uuid, cols); // <-- per row
118-
}
119-
// cols becomes eligible for GC after this loop iteration
161+
processed++;
162+
perUser.accept(uuid, cols);
120163
}
164+
121165
} catch (SQLException e) {
122166
e.printStackTrace();
123167
} finally {
124-
try {
125-
if (rs != null)
126-
rs.close();
127-
} catch (SQLException ignored) {
128-
}
129-
try {
130-
if (s != null)
131-
s.close();
132-
} catch (SQLException ignored) {
133-
}
134-
135168
if (onFinished != null) {
136-
onFinished.accept(processed); // <-- once
169+
onFinished.accept(processed);
137170
}
138171
}
139172
}

0 commit comments

Comments
 (0)