From 78384ebc7b139616e14b0da80058f33105ee738d Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 1 Aug 2018 12:20:10 +0200 Subject: [PATCH] Implement HikariCP for database --- pom.xml | 10 + .../exceptions/WorldNotFoundException.java | 4 +- .../listeners/ShopInteractListener.java | 8 +- .../de/epiceric/shopchest/sql/Database.java | 489 +++++++++--------- .../java/de/epiceric/shopchest/sql/MySQL.java | 41 +- .../de/epiceric/shopchest/sql/SQLite.java | 35 +- 6 files changed, 286 insertions(+), 301 deletions(-) diff --git a/pom.xml b/pom.xml index e338e7c..7c6c57b 100644 --- a/pom.xml +++ b/pom.xml @@ -140,6 +140,16 @@ 2.4.0 provided + + com.zaxxer + HikariCP + 3.2.0 + + + org.slf4j + slf4j-jdk14 + 1.7.25 + diff --git a/src/main/java/de/epiceric/shopchest/exceptions/WorldNotFoundException.java b/src/main/java/de/epiceric/shopchest/exceptions/WorldNotFoundException.java index 57ec094..4578846 100644 --- a/src/main/java/de/epiceric/shopchest/exceptions/WorldNotFoundException.java +++ b/src/main/java/de/epiceric/shopchest/exceptions/WorldNotFoundException.java @@ -2,8 +2,8 @@ package de.epiceric.shopchest.exceptions; public class WorldNotFoundException extends Exception { - public WorldNotFoundException(String message) { - super(message); + public WorldNotFoundException(String worldName) { + super("Could not find world with name \"" + worldName + "\""); } } diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java index ca4d4be..f9eda64 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java @@ -1028,7 +1028,7 @@ public class ShopInteractListener implements Listener { return; } - database.logEconomy(executor, newProduct, shop.getVendor(), shop.getShopType(), shop.getLocation(), newPrice, ShopBuySellEvent.Type.BUY, null); + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.BUY, null); addToInventory(inventory, newProduct); removeFromInventory(c.getInventory(), newProduct); @@ -1072,7 +1072,7 @@ public class ShopInteractListener implements Listener { return; } - database.logEconomy(executor, newProduct, shop.getVendor(), shop.getShopType(), shop.getLocation(), newPrice, ShopBuySellEvent.Type.BUY, null); + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.BUY, null); addToInventory(inventory, newProduct); executor.updateInventory(); @@ -1192,7 +1192,7 @@ public class ShopInteractListener implements Listener { return; } - database.logEconomy(executor, newProduct, shop.getVendor(), shop.getShopType(), shop.getLocation(), newPrice, ShopBuySellEvent.Type.SELL, null); + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.SELL, null); addToInventory(inventory, newProduct); removeFromInventory(executor.getInventory(), newProduct); @@ -1237,7 +1237,7 @@ public class ShopInteractListener implements Listener { return; } - database.logEconomy(executor, newProduct, shop.getVendor(), shop.getShopType(), shop.getLocation(), newPrice, ShopBuySellEvent.Type.SELL, null); + database.logEconomy(executor, shop, newProduct, newPrice, ShopBuySellEvent.Type.SELL, null); removeFromInventory(executor.getInventory(), newProduct); executor.updateInventory(); diff --git a/src/main/java/de/epiceric/shopchest/sql/Database.java b/src/main/java/de/epiceric/shopchest/sql/Database.java index 1248f45..466813c 100644 --- a/src/main/java/de/epiceric/shopchest/sql/Database.java +++ b/src/main/java/de/epiceric/shopchest/sql/Database.java @@ -3,6 +3,7 @@ package de.epiceric.shopchest.sql; import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.config.Config; import de.epiceric.shopchest.event.ShopBuySellEvent; +import de.epiceric.shopchest.event.ShopBuySellEvent.Type; import de.epiceric.shopchest.exceptions.WorldNotFoundException; import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.shop.Shop; @@ -28,86 +29,83 @@ import java.util.HashSet; import java.util.Set; import java.util.UUID; +import com.zaxxer.hikari.HikariDataSource; + public abstract class Database { private static Set notFoundWorlds = new HashSet<>(); ShopChest plugin; - Connection connection; + HikariDataSource dataSource; - Database(ShopChest plugin) { + protected Database(ShopChest plugin) { this.plugin = plugin; } - /** - * @return New connection to the database - */ - public abstract Connection getConnection(); + abstract HikariDataSource getDataSource(); /** * (Re-)Connects to the the database and initializes it.
* Creates the table (if doesn't exist) and tests the connection - * @param callback Callback that - if succeeded - returns the amount of shops that were found (as {@code int}) + * + * @param callback Callback that - if succeeded - returns the amount of shops + * that were found (as {@code int}) */ public void connect(final Callback callback) { new BukkitRunnable() { @Override public void run() { + disconnect(); + + dataSource = getDataSource(); + + String queryCreateTableShopList = + "CREATE TABLE IF NOT EXISTS shops (" + + "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + + "vendor TINYTEXT NOT NULL," + + "product TEXT NOT NULL," + + "world TINYTEXT NOT NULL," + + "x INTEGER NOT NULL," + + "y INTEGER NOT NULL," + + "z INTEGER NOT NULL," + + "buyprice FLOAT NOT NULL," + + "sellprice FLOAT NOT NULL," + + "shoptype TINYTEXT NOT NULL)"; + + String queryCreateTableShopLog = + "CREATE TABLE IF NOT EXISTS `shop_log` (" + + "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + + "timestamp TINYTEXT NOT NULL," + + "executor TINYTEXT NOT NULL," + + "product TINYTEXT NOT NULL," + + "vendor TINYTEXT NOT NULL," + + "world TINYTEXT NOT NULL," + + "x INTEGER NOT NULL," + + "y INTEGER NOT NULL," + + "z INTEGER NOT NULL," + + "price FLOAT NOT NULL," + + "type TINYTEXT NOT NULL)"; + + String queryCreateTablePlayerLogout = + "CREATE TABLE IF NOT EXISTS player_logout (" + + "player VARCHAR(36) PRIMARY KEY NOT NULL," + + "time LONG NOT NULL)"; + try { - disconnect(); - - plugin.debug("Connecting to database..."); - connection = getConnection(); - - String queryCreateTableShopList = - "CREATE TABLE IF NOT EXISTS shops (" + - "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + - "vendor TINYTEXT NOT NULL," + - "product TEXT NOT NULL," + - "world TINYTEXT NOT NULL," + - "x INTEGER NOT NULL," + - "y INTEGER NOT NULL," + - "z INTEGER NOT NULL," + - "buyprice FLOAT NOT NULL," + - "sellprice FLOAT NOT NULL," + - "shoptype TINYTEXT NOT NULL" + - ")"; - - String queryCreateTableShopLog = - "CREATE TABLE IF NOT EXISTS `shop_log` (" + - "id INTEGER PRIMARY KEY " + (Database.this instanceof SQLite ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," + - "timestamp TINYTEXT NOT NULL," + - "executor TINYTEXT NOT NULL," + - "product TINYTEXT NOT NULL," + - "vendor TINYTEXT NOT NULL," + - "world TINYTEXT NOT NULL," + - "x INTEGER NOT NULL," + - "y INTEGER NOT NULL," + - "z INTEGER NOT NULL," + - "price FLOAT NOT NULL," + - "type TINYTEXT NOT NULL" + - ")"; - - String queryCreateTablePlayerLogout = - "CREATE TABLE IF NOT EXISTS player_logout (" + - "player VARCHAR(36) PRIMARY KEY NOT NULL," + - "time LONG NOT NULL" + - ")"; - // Create table "shops" - Statement s = connection.createStatement(); - s.executeUpdate(queryCreateTableShopList); - s.close(); + try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { + s.executeUpdate(queryCreateTableShopList); + } // Create table "shop_log" - Statement s2 = connection.createStatement(); - s2.executeUpdate(queryCreateTableShopLog); - s2.close(); + try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { + s.executeUpdate(queryCreateTableShopLog); + } // Create table "player_logout" - Statement s3 = connection.createStatement(); - s3.executeUpdate(queryCreateTablePlayerLogout); - s3.close(); + try (Connection con = dataSource.getConnection(); Statement s = con.createStatement()) { + s.executeUpdate(queryCreateTablePlayerLogout); + } // Clean up economy log if (Config.cleanupEconomyLogDays > 0) { @@ -115,23 +113,28 @@ public abstract class Database { } // Count entries in table "shops" - PreparedStatement ps = connection.prepareStatement("SELECT * FROM shops"); - ResultSet rs2 = ps.executeQuery(); + try (Connection con = dataSource.getConnection(); Statement s = con.createStatement();) { + ResultSet rs = s.executeQuery("SELECT id FROM shops"); - int count = 0; - while (rs2.next()) { - if (rs2.getString("vendor") != null) count++; + int count = 0; + while (rs.next()) { + count++; + } + + plugin.debug("Initialized database with " + count + " entries"); + + if (callback != null) { + callback.callSyncResult(count); + } } - plugin.debug("Initialized database with " + count + " entries"); - - close(ps, rs2); - - if (callback != null) callback.callSyncResult(count); - } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); + } catch (SQLException e) { + if (callback != null) { + callback.callSyncError(e); + } + plugin.getLogger().severe("Failed to initialize database"); plugin.debug("Failed to initialize database"); - plugin.debug(ex); + plugin.debug(e); } } }.runTaskAsynchronously(plugin); @@ -140,65 +143,70 @@ public abstract class Database { /** * Remove a shop from the database * - * @param shop Shop to remove + * @param shop Shop to remove * @param callback Callback that - if succeeded - returns {@code null} */ public void removeShop(final Shop shop, final Callback callback) { new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - - try { - ps = connection.prepareStatement("DELETE FROM shops WHERE id = ?"); + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement("DELETE FROM shops WHERE id = ?")) { ps.setInt(1, shop.getID()); ps.executeUpdate(); plugin.debug("Removing shop from database (#" + shop.getID() + ")"); - if (callback != null) callback.callSyncResult(null); + + if (callback != null) { + callback.callSyncResult(null); + } } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to remove shop from database (#" + shop.getID() + ")"); plugin.debug(ex); - } finally { - close(ps, null); } } }.runTaskAsynchronously(plugin); } /** - * @param id ID of the shop - * @param callback Callback that - if succeeded - returns whether a shop with the given ID exists (as {@code boolean}) + * @param id ID of the shop + * @param callback Callback that - if succeeded - returns whether a shop with + * the given ID exists (as {@code boolean}) */ public void isShop(final int id, final Callback callback) { new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - ResultSet rs = null; - - try { - ps = connection.prepareStatement("SELECT * FROM shops WHERE id = ?"); + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT * FROM shops WHERE id = ?")) { ps.setInt(1, id); - rs = ps.executeQuery(); + ResultSet rs = ps.executeQuery(); while (rs.next()) { if (rs.getInt("id") == id) { - if (callback != null) callback.callSyncResult(true); + if (callback != null) { + callback.callSyncResult(true); + } return; } } - if (callback != null) callback.callSyncResult(false); + if (callback != null) { + callback.callSyncResult(false); + } } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to check if shop with ID exists (#" + id + ")"); plugin.debug(ex); - } finally { - close(ps, rs); } } }.runTaskAsynchronously(plugin); @@ -206,21 +214,22 @@ public abstract class Database { /** * Get all shops from the database - * @param callback Callback that - if succeeded - returns a read-only collection of all shops (as {@code Collection}) - * @param showConsoleMessages Whether console messages (errors or warnings) should be shown + * + * @param callback Callback that - if succeeded - returns a read-only + * collection of all shops (as + * {@code Collection}) + * @param showConsoleMessages Whether console messages (errors or warnings) + * should be shown */ public void getShops(final boolean showConsoleMessages, final Callback> callback) { new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - ResultSet rs = null; - ArrayList shops = new ArrayList<>(); - try { - ps = connection.prepareStatement("SELECT * FROM shops"); - rs = ps.executeQuery(); + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT * FROM shops")) { + ResultSet rs = ps.executeQuery(); while (rs.next()) { int id = rs.getInt("id"); @@ -234,9 +243,11 @@ public abstract class Database { int z = rs.getInt("z"); if (world == null) { - WorldNotFoundException ex = new WorldNotFoundException("Could not find world with name \"" + worldName + "\""); - if (showConsoleMessages && !notFoundWorlds.contains(worldName)) plugin.getLogger().warning(ex.getMessage()); - notFoundWorlds.add(worldName); + WorldNotFoundException ex = new WorldNotFoundException(worldName); + if (showConsoleMessages && !notFoundWorlds.contains(worldName)) { + plugin.getLogger().warning(ex.getMessage()); + notFoundWorlds.add(worldName); + } plugin.debug("Failed to get shop (#" + id + ")"); plugin.debug(ex); continue; @@ -255,80 +266,81 @@ public abstract class Database { shops.add(new Shop(id, plugin, vendor, product, location, buyPrice, sellPrice, shopType)); } - if (callback != null) callback.callSyncResult(Collections.unmodifiableCollection(shops)); + if (callback != null) { + callback.callSyncResult(Collections.unmodifiableCollection(shops)); + } } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to get shops"); plugin.debug(ex); - } finally { - close(ps, rs); } - } }.runTaskAsynchronously(plugin); } /** * Adds a shop to the database - * @param shop Shop to add - * @param callback Callback that - if succeeded - returns the ID the shop was given (as {@code int}) + * + * @param shop Shop to add + * @param callback Callback that - if succeeded - returns the ID the shop was + * given (as {@code int}) */ public void addShop(final Shop shop, final Callback callback) { + final String queryNoId = "REPLACE INTO shops (vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?)"; + final String queryWithId = "REPLACE INTO shops (id,vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?)"; + new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - ResultSet rs = null; + String query = shop.hasId() ? queryWithId : queryNoId; + + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) { + int i = 0; + if (shop.hasId()) { + i = 1; + ps.setInt(1, shop.getID()); + } + + ps.setString(i+1, shop.getVendor().getUniqueId().toString()); + ps.setString(i+2, Utils.encode(shop.getProduct())); + ps.setString(i+3, shop.getLocation().getWorld().getName()); + ps.setInt(i+4, shop.getLocation().getBlockX()); + ps.setInt(i+5, shop.getLocation().getBlockY()); + ps.setInt(i+6, shop.getLocation().getBlockZ()); + ps.setDouble(i+7, shop.getBuyPrice()); + ps.setDouble(i+8, shop.getSellPrice()); + ps.setString(i+9, shop.getShopType().toString()); + ps.executeUpdate(); - try { if (!shop.hasId()) { - ps = connection.prepareStatement("REPLACE INTO shops (vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?)", Statement.RETURN_GENERATED_KEYS); - - ps.setString(1, shop.getVendor().getUniqueId().toString()); - ps.setString(2, Utils.encode(shop.getProduct())); - ps.setString(3, shop.getLocation().getWorld().getName()); - ps.setInt(4, shop.getLocation().getBlockX()); - ps.setInt(5, shop.getLocation().getBlockY()); - ps.setInt(6, shop.getLocation().getBlockZ()); - ps.setDouble(7, shop.getBuyPrice()); - ps.setDouble(8, shop.getSellPrice()); - ps.setString(9, shop.getShopType().toString()); - ps.executeUpdate(); - int shopId = -1; - rs = ps.getGeneratedKeys(); + ResultSet rs = ps.getGeneratedKeys(); if (rs.next()) { shopId = rs.getInt(1); } shop.setId(shopId); - } else { - ps = connection.prepareStatement("REPLACE INTO shops (id,vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?)"); - - ps.setInt(1, shop.getID()); - ps.setString(2, shop.getVendor().getUniqueId().toString()); - ps.setString(3, Utils.encode(shop.getProduct())); - ps.setString(4, shop.getLocation().getWorld().getName()); - ps.setInt(5, shop.getLocation().getBlockX()); - ps.setInt(6, shop.getLocation().getBlockY()); - ps.setInt(7, shop.getLocation().getBlockZ()); - ps.setDouble(8, shop.getBuyPrice()); - ps.setDouble(9, shop.getSellPrice()); - ps.setString(10, shop.getShopType().toString()); - ps.executeUpdate(); } - if (callback != null) callback.callSyncResult(shop.getID()); + if (callback != null) { + callback.callSyncResult(shop.getID()); + } + plugin.debug("Adding shop to database (#" + shop.getID() + ")"); } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to add shop to database (#" + shop.getID() + ")"); plugin.debug(ex); - } finally { - close(ps, rs); } } }.runTaskAsynchronously(plugin); @@ -336,65 +348,67 @@ public abstract class Database { /** * Log an economy transaction to the database + * * @param executor Player who bought/sold something - * @param product {@link ItemStack} that was bought/sold - * @param vendor Vendor of the shop - * @param shopType {@link ShopType} of the shop - * @param location Location of the shop - * @param price Price (buyprice or sellprice, depends on {@code type}) - * @param type Whether the player bought or sold something + * @param shop The {@link Shop} the player bought from or sold to + * @param product The {@link ItemStack} that was bought/sold + * @param price The price the product was bought or sold for + * @param type Whether the executor bought or sold * @param callback Callback that - if succeeded - returns {@code null} */ - public void logEconomy(final Player executor, final ItemStack product, final OfflinePlayer vendor, final ShopType shopType, final Location location, final double price, final ShopBuySellEvent.Type type, final Callback callback) { + public void logEconomy(final Player executor, Shop shop, ItemStack product, double price, Type type, final Callback callback) { + final String query = "INSERT INTO shop_log (timestamp,executor,product,vendor,world,x,y,z,price,type) VALUES(?,?,?,?,?,?,?,?,?,?)"; if (Config.enableEconomyLog) { new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - - try { - ps = connection.prepareStatement("INSERT INTO shop_log (timestamp,executor,product,vendor,world,x,y,z,price,type) VALUES(?,?,?,?,?,?,?,?,?,?)"); + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement(query)) { ps.setString(1, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime())); ps.setString(2, executor.getUniqueId().toString() + " (" + executor.getName() + ")"); ps.setString(3, product.getAmount() + " x " + LanguageUtils.getItemName(product)); - ps.setString(4, vendor.getUniqueId().toString() + " (" + vendor.getName() + ")" + (shopType == ShopType.ADMIN ? " (ADMIN)" : "")); - ps.setString(5, location.getWorld().getName()); - ps.setInt(6, location.getBlockX()); - ps.setInt(7, location.getBlockY()); - ps.setInt(8, location.getBlockZ()); + ps.setString(4, shop.getVendor().getUniqueId().toString() + " (" + shop.getVendor().getName() + ")" + (shop.getShopType() == ShopType.ADMIN ? " (ADMIN)" : "")); + ps.setString(5, shop.getLocation().getWorld().getName()); + ps.setInt(6, shop.getLocation().getBlockX()); + ps.setInt(7, shop.getLocation().getBlockY()); + ps.setInt(8, shop.getLocation().getBlockZ()); ps.setDouble(9, price); ps.setString(10, type.toString()); ps.executeUpdate(); - if (callback != null) callback.callSyncResult(null); + if (callback != null) { + callback.callSyncResult(null); + } + plugin.debug("Logged economy transaction to database"); - } catch (final SQLException ex) { - if (callback != null) callback.callSyncError(ex); + } catch (SQLException ex) { + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to log economy transaction to database"); plugin.debug(ex); - } finally { - close(ps, null); } } }.runTaskAsynchronously(plugin); } else { - if (callback != null) callback.callSyncResult(null); + if (callback != null) { + callback.callSyncResult(null); + } } } /** * Cleans up the economy log to reduce file size + * * @param async Whether the call should be executed asynchronously */ public void cleanUpEconomy(boolean async) { BukkitRunnable runnable = new BukkitRunnable() { @Override public void run() { - Statement s = null; - Statement s2 = null; - Calendar cal = Calendar.getInstance(); long time = System.currentTimeMillis(); cal.add(Calendar.DATE, -Config.cleanupEconomyLogDays); @@ -403,22 +417,18 @@ public abstract class Database { String queryCleanUpLog = "DELETE FROM shop_log WHERE timestamp < '" + logPurgeLimit + "'"; String queryCleanUpPlayers = "DELETE FROM player_logout WHERE time < " + String.valueOf(time); - try { - s = connection.createStatement(); + try (Connection con = dataSource.getConnection(); + Statement s = con.createStatement(); + Statement s2 = con.createStatement()) { s.executeUpdate(queryCleanUpLog); - - s2 = connection.createStatement(); s2.executeUpdate(queryCleanUpPlayers); plugin.getLogger().info("Cleaned up economy log"); plugin.debug("Cleaned up economy log"); - } catch (final SQLException ex) { + } catch (SQLException ex) { plugin.getLogger().severe("Failed to clean up economy log"); plugin.debug("Failed to clean up economy log"); plugin.debug(ex); - } finally { - close(s, null); - close(s2, null); } } }; @@ -432,32 +442,32 @@ public abstract class Database { /** * Get the revenue a player got while he was offline - * @param player Player whose revenue to get + * + * @param player Player whose revenue to get * @param logoutTime Time in milliseconds when he logged out the last time - * @param callback Callback that - if succeeded - returns the revenue the player made while offline (as {@code double}) + * @param callback Callback that - if succeeded - returns the revenue the + * player made while offline (as {@code double}) */ public void getRevenue(final Player player, final long logoutTime, final Callback callback) { new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - ResultSet rs = null; - String vendor = String.format("%s (%s)", player.getUniqueId().toString(), player.getName()); - double revenue = 0; - try { - ps = connection.prepareStatement("SELECT * FROM shop_log WHERE vendor = ?;"); + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT * FROM shop_log WHERE vendor = ?")) { ps.setString(1, vendor); - rs = ps.executeQuery(); + ResultSet rs = ps.executeQuery(); while (rs.next()) { if (rs.getString("vendor").equals(vendor)) { double singleRevenue = rs.getDouble("price"); ShopBuySellEvent.Type type = ShopBuySellEvent.Type.valueOf(rs.getString("type")); - if (type == ShopBuySellEvent.Type.SELL) singleRevenue = -singleRevenue; + if (type == ShopBuySellEvent.Type.SELL) { + singleRevenue = -singleRevenue; + } long timestamp; @@ -475,14 +485,17 @@ public abstract class Database { } } - if (callback != null) callback.callSyncResult(revenue); + if (callback != null) { + callback.callSyncResult(revenue); + } } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to get revenue from player \"" + player.getUniqueId().toString() + "\""); plugin.debug(ex); - } finally { - close(ps, rs); } } }.runTaskAsynchronously(plugin); @@ -490,32 +503,35 @@ public abstract class Database { /** * Log a logout to the database - * @param player Player who logged out + * + * @param player Player who logged out * @param timestamp Time in milliseconds when the player logged out - * @param callback Callback that - if succeeded - returns {@code null} + * @param callback Callback that - if succeeded - returns {@code null} */ public void logLogout(final Player player, final long timestamp, final Callback callback) { new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - - try { - ps = connection.prepareStatement("REPLACE INTO player_logout (player,time) VALUES(?,?)"); + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement("REPLACE INTO player_logout (player,time) VALUES(?,?)")) { ps.setString(1, player.getUniqueId().toString()); ps.setLong(2, timestamp); ps.executeUpdate(); - if (callback != null) callback.callSyncResult(null); + if (callback != null) { + callback.callSyncResult(null); + } + plugin.debug("Logged logout to database"); } catch (final SQLException ex) { - if (callback != null) callback.callSyncError(ex); + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to log logout to database"); plugin.debug(ex); - } finally { - close(ps, null); } } }.runTaskAsynchronously(plugin); @@ -523,78 +539,57 @@ public abstract class Database { /** * Get the last logout of a player - * @param player Player who logged out - * @param callback Callback that - if succeeded - returns the time in milliseconds the player logged out (as {@code long}) + * + * @param player Player who logged out + * @param callback Callback that - if succeeded - returns the time in + * milliseconds the player logged out (as {@code long}) */ public void getLastLogout(final Player player, final Callback callback) { new BukkitRunnable() { @Override public void run() { - PreparedStatement ps = null; - ResultSet rs = null; + String uuid = player.getUniqueId().toString(); - String playerUuid = player.getUniqueId().toString(); - - try { - ps = connection.prepareStatement("SELECT * FROM player_logout WHERE player=?;"); - ps.setString(1, playerUuid); - rs = ps.executeQuery(); + try (Connection con = dataSource.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT * FROM player_logout WHERE player = ?")) { + ps.setString(1, uuid); + ResultSet rs = ps.executeQuery(); while (rs.next()) { - if (rs.getString("player").equals(playerUuid)) { - if (callback != null) callback.callSyncResult(rs.getLong("time")); + if (rs.getString("player").equals(uuid)) { + if (callback != null) { + callback.callSyncResult(rs.getLong("time")); + } return; } } - if (callback != null) callback.callSyncResult(-1L); + if (callback != null) { + callback.callSyncResult(-1L); + } } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); + if (callback != null) { + callback.callSyncError(ex); + } + plugin.getLogger().severe("Failed to access database"); - plugin.debug("Failed to get last logout from player \"" + playerUuid + "\""); + plugin.debug("Failed to get last logout from player \"" + player.getName() + "\""); plugin.debug(ex); - } finally { - close(ps, rs); } } }.runTaskAsynchronously(plugin); } /** - * Closes a {@link Statement} and a {@link ResultSet} - * @param s {@link Statement} to close - * @param rs {@link ResultSet} to close - */ - void close(Statement s, ResultSet rs) { - try { - if (s != null) - s.close(); - if (rs != null) - rs.close(); - } catch (SQLException ex) { - plugin.debug("Failed to close PreparedStatement/ResultSet"); - plugin.debug(ex); - } - } - - /** - * Closes the connection to the database + * Closes the data source */ public void disconnect() { - try { - if (connection != null && !connection.isClosed()) { - plugin.debug("Disconnecting from database..."); - connection.close(); - } - } catch (SQLException e) { - plugin.getLogger().severe("Failed to disconnect from database"); - plugin.debug("Failed to disconnect from database"); - plugin.debug(e); + if (dataSource != null) { + dataSource.close(); } } public enum DatabaseType { - SQLite, - MySQL + SQLite, MySQL } } \ No newline at end of file diff --git a/src/main/java/de/epiceric/shopchest/sql/MySQL.java b/src/main/java/de/epiceric/shopchest/sql/MySQL.java index baf293c..4997eba 100644 --- a/src/main/java/de/epiceric/shopchest/sql/MySQL.java +++ b/src/main/java/de/epiceric/shopchest/sql/MySQL.java @@ -5,9 +5,11 @@ import de.epiceric.shopchest.config.Config; import org.bukkit.scheduler.BukkitRunnable; import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Statement; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; public class MySQL extends Database { @@ -16,36 +18,27 @@ public class MySQL extends Database { } @Override - public Connection getConnection() { - try { - if (connection != null && !connection.isClosed()) { - return connection; - } + HikariDataSource getDataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl(String.format("jdbc:mysql://%s:%d/%s?autoReconnect=true&useSSL=false", + Config.databaseMySqlHost, Config.databaseMySqlPort, Config.databaseMySqlDatabase)); + config.setUsername(Config.databaseMySqlUsername); + config.setPassword(Config.databaseMySqlPassword); - Class.forName("com.mysql.jdbc.Driver"); - - String connectUrl = "jdbc:mysql://" + Config.databaseMySqlHost + ":" + Config.databaseMySqlPort + "/" + Config.databaseMySqlDatabase + "?autoReconnect=true&useSSL=false"; - plugin.debug("Connecting to MySQL Server \"" + connectUrl + "\" as user \"" + Config.databaseMySqlUsername + "\""); - - connection = DriverManager.getConnection(connectUrl, Config.databaseMySqlUsername, Config.databaseMySqlPassword); - - return connection; - } catch (Exception ex) { - plugin.getLogger().severe("Failed to get database connection"); - plugin.debug("Failed to get database connection"); - plugin.debug(ex); - } - - return null; + return new HikariDataSource(config); } + /** + * Sends an asynchronous ping to the database + */ public void ping() { new BukkitRunnable() { @Override public void run() { - try (PreparedStatement ps = connection.prepareStatement("/* ping */ SELECT 1")) { + try (Connection con = dataSource.getConnection(); + Statement s = con.createStatement()) { plugin.debug("Pinging to MySQL server..."); - ps.executeQuery(); + s.execute("/* ping */ SELECT 1"); } catch (SQLException ex) { plugin.getLogger().severe("Failed to ping to MySQL server. Trying to reconnect..."); plugin.debug("Failed to ping to MySQL server. Trying to reconnect..."); diff --git a/src/main/java/de/epiceric/shopchest/sql/SQLite.java b/src/main/java/de/epiceric/shopchest/sql/SQLite.java index 8175854..d289f99 100644 --- a/src/main/java/de/epiceric/shopchest/sql/SQLite.java +++ b/src/main/java/de/epiceric/shopchest/sql/SQLite.java @@ -6,10 +6,12 @@ import org.bukkit.scheduler.BukkitRunnable; import java.io.File; import java.io.IOException; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + public class SQLite extends Database { public SQLite(ShopChest plugin) { @@ -17,7 +19,7 @@ public class SQLite extends Database { } @Override - public Connection getConnection() { + HikariDataSource getDataSource() { File folder = plugin.getDataFolder(); File dbFile = new File(folder, "shops.db"); @@ -30,37 +32,24 @@ public class SQLite extends Database { plugin.debug(ex); } } + + HikariConfig config = new HikariConfig(); + config.setJdbcUrl(String.format("jdbc:sqlite:" + dbFile)); - try { - if (connection != null && !connection.isClosed()) { - return connection; - } - - Class.forName("org.sqlite.JDBC"); - connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile); - - return connection; - } catch (ClassNotFoundException | SQLException ex) { - plugin.getLogger().severe("Failed to get database connection"); - plugin.debug("Failed to get database connection"); - plugin.debug(ex); - } - - return null; + return new HikariDataSource(config); } /** * Vacuums the database to reduce file size + * * @param async Whether the call should be executed asynchronously */ public void vacuum(boolean async) { BukkitRunnable runnable = new BukkitRunnable() { @Override public void run() { - Statement s = null; - - try { - s = connection.createStatement(); + try (Connection con = dataSource.getConnection(); + Statement s = con.createStatement()) { s.executeUpdate("VACUUM"); plugin.debug("Vacuumed SQLite database"); @@ -68,8 +57,6 @@ public class SQLite extends Database { plugin.getLogger().severe("Failed to access database"); plugin.debug("Failed to vacuum database"); plugin.debug(ex); - } finally { - close(s, null); } } };