From 1e38edc7a0219bf0b6eb3e4df05e3537312a8c4c Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 9 Feb 2017 21:51:39 +0100 Subject: [PATCH] Improved database interaction --- .../de/epiceric/shopchest/ShopCommand.java | 2 +- .../listeners/ShopInteractListener.java | 73 +++-- .../java/de/epiceric/shopchest/shop/Shop.java | 45 +-- .../de/epiceric/shopchest/sql/Database.java | 262 ++++++++---------- .../epiceric/shopchest/utils/ShopUtils.java | 45 +-- 5 files changed, 187 insertions(+), 240 deletions(-) diff --git a/src/main/java/de/epiceric/shopchest/ShopCommand.java b/src/main/java/de/epiceric/shopchest/ShopCommand.java index 5b8ab36..4aac551 100644 --- a/src/main/java/de/epiceric/shopchest/ShopCommand.java +++ b/src/main/java/de/epiceric/shopchest/ShopCommand.java @@ -473,7 +473,7 @@ class ShopCommand implements CommandExecutor { plugin.debug(p.getName() + " can pay the creation price"); - ShopPreCreateEvent event = new ShopPreCreateEvent(p, Shop.createImaginaryShop(p, itemStack, null, buyPrice, sellPrice, shopType)); + ShopPreCreateEvent event = new ShopPreCreateEvent(p, new Shop(plugin, p, itemStack, null, buyPrice, sellPrice, shopType)); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java index 5630393..fa5c0ee 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java @@ -17,7 +17,10 @@ import de.epiceric.shopchest.nms.Hologram; import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.sql.Database; -import de.epiceric.shopchest.utils.*; +import de.epiceric.shopchest.utils.ClickType; +import de.epiceric.shopchest.utils.Permissions; +import de.epiceric.shopchest.utils.ShopUtils; +import de.epiceric.shopchest.utils.Utils; import de.epiceric.shopchest.worldguard.ShopFlag; import fr.xephi.authme.AuthMe; import net.milkbowl.vault.economy.Economy; @@ -447,53 +450,43 @@ public class ShopInteractListener implements Listener { private void create(final Player executor, final Location location, final ItemStack product, final double buyPrice, final double sellPrice, final ShopType shopType) { plugin.debug(executor.getName() + " is creating new shop..."); - database.getNextFreeID(new Callback(plugin) { - @Override - public void onResult(Object result) { - if (result instanceof Integer) { - int id = (int) result; - double creationPrice = (shopType == ShopType.NORMAL) ? config.shop_creation_price_normal : config.shop_creation_price_admin; - Shop shop = new Shop(id, plugin, executor, product, location, buyPrice, sellPrice, shopType); + double creationPrice = (shopType == ShopType.NORMAL) ? config.shop_creation_price_normal : config.shop_creation_price_admin; + Shop shop = new Shop(plugin, executor, product, location, buyPrice, sellPrice, shopType); - ShopCreateEvent event = new ShopCreateEvent(executor, shop, creationPrice); - Bukkit.getPluginManager().callEvent(event); + ShopCreateEvent event = new ShopCreateEvent(executor, shop, creationPrice); + Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) { - plugin.debug("Create event cancelled (#" + id + ")"); - return; - } + if (event.isCancelled()) { + plugin.debug("Create event cancelled"); + return; + } - EconomyResponse r = plugin.getEconomy().withdrawPlayer(executor, location.getWorld().getName(), creationPrice); - if (!r.transactionSuccess()) { - plugin.debug("Economy transaction failed: " + r.errorMessage); - executor.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.ERROR_OCCURRED, new LocalizedMessage.ReplacedRegex(Regex.ERROR, r.errorMessage))); - return; - } + EconomyResponse r = plugin.getEconomy().withdrawPlayer(executor, location.getWorld().getName(), creationPrice); + if (!r.transactionSuccess()) { + plugin.debug("Economy transaction failed: " + r.errorMessage); + executor.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.ERROR_OCCURRED, new LocalizedMessage.ReplacedRegex(Regex.ERROR, r.errorMessage))); + return; + } - shop.create(); + shop.create(); - plugin.debug("Shop created (#" + id + ")"); - shopUtils.addShop(shop, true); - executor.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.SHOP_CREATED)); + plugin.debug("Shop created"); + shopUtils.addShop(shop, true); - for (Player p : location.getWorld().getPlayers()) { - if (p.getLocation().distanceSquared(location) <= Math.pow(config.maximal_distance, 2)) { - if (shop.getHologram() != null) { - shop.getHologram().showPlayer(p); - } - } - if (p.getLocation().distanceSquared(location) <= Math.pow(config.maximal_item_distance, 2)) { - if (shop.getItem() != null) { - shop.getItem().setVisible(p, true); - } - } - } + executor.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.SHOP_CREATED)); + + for (Player p : location.getWorld().getPlayers()) { + if (p.getLocation().distanceSquared(location) <= Math.pow(config.maximal_distance, 2)) { + if (shop.getHologram() != null) { + shop.getHologram().showPlayer(p); } } - - @Override - public void onError(Throwable throwable) {} - }); + if (p.getLocation().distanceSquared(location) <= Math.pow(config.maximal_item_distance, 2)) { + if (shop.getItem() != null) { + shop.getItem().setVisible(p, true); + } + } + } } /** diff --git a/src/main/java/de/epiceric/shopchest/shop/Shop.java b/src/main/java/de/epiceric/shopchest/shop/Shop.java index dfe1ef3..29d7bcc 100644 --- a/src/main/java/de/epiceric/shopchest/shop/Shop.java +++ b/src/main/java/de/epiceric/shopchest/shop/Shop.java @@ -44,18 +44,14 @@ public class Shop { this.shopType = shopType; } - private Shop(OfflinePlayer vendor, ItemStack product, Location location, double buyPrice, double sellPrice, ShopType shopType) { - this.id = 0; - this.vendor = vendor; - this.product = product; - this.location = location; - this.buyPrice = buyPrice; - this.sellPrice = sellPrice; - this.shopType = shopType; + public Shop(ShopChest plugin, OfflinePlayer vendor, ItemStack product, Location location, double buyPrice, double sellPrice, ShopType shopType) { + this(-1, plugin, vendor, product, location, buyPrice, sellPrice, shopType); } - public void create() { - if (created) return; + public boolean create() { + if (created) return false; + + plugin.debug("Creating shop (#" + id + ")"); Block b = location.getBlock(); if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) { @@ -64,20 +60,21 @@ public class Shop { plugin.getLogger().severe(ex.getMessage()); plugin.debug("Failed to create shop (#" + id + ")"); plugin.debug(ex); - return; + return false; } else if ((b.getRelative(BlockFace.UP).getType() != Material.AIR) && plugin.getShopChestConfig().show_shop_items) { NotEnoughSpaceException ex = new NotEnoughSpaceException("No space above chest at location: " + b.getX() + "; " + b.getY() + "; " + b.getZ()); plugin.getShopUtils().removeShop(this, plugin.getShopChestConfig().remove_shop_on_error); plugin.getLogger().severe(ex.getMessage()); plugin.debug("Failed to create shop (#" + id + ")"); plugin.debug(ex); - return; + return false; } if (hologram == null || !hologram.exists()) createHologram(); if (item == null) createItem(); created = true; + return true; } /** @@ -223,6 +220,23 @@ public class Shop { hologram = new Hologram(plugin, holoText, holoLocation); } + /** + * @return Whether an ID has been assigned to the shop + */ + public boolean hasId() { + return id != -1; + } + + /** + * Assign an ID to the shop.
+ * Only works for the first time! + */ + public void setId(int id) { + if (this.id == -1) { + this.id = id; + } + } + /** * @return Whether the shop has already been created */ @@ -307,13 +321,6 @@ public class Shop { return null; } - /** - * @return A shop, which is not really a shop. It's just for "storing" the data (used in some events). - */ - public static Shop createImaginaryShop(OfflinePlayer vendor, ItemStack product, Location location, double buyPrice, double sellPrice, ShopType shopType) { - return new Shop(vendor, product, location, buyPrice, sellPrice, shopType); - } - public enum ShopType { NORMAL, ADMIN diff --git a/src/main/java/de/epiceric/shopchest/sql/Database.java b/src/main/java/de/epiceric/shopchest/sql/Database.java index 6c3c069..cf377ff 100644 --- a/src/main/java/de/epiceric/shopchest/sql/Database.java +++ b/src/main/java/de/epiceric/shopchest/sql/Database.java @@ -18,6 +18,7 @@ import org.bukkit.scheduler.BukkitRunnable; import java.sql.*; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.UUID; @@ -50,18 +51,17 @@ public abstract class Database { connection = getConnection(); String queryCreateTableShopList = - "CREATE TABLE IF NOT EXISTS shop_list (" + - "`id` int(11) NOT NULL," + - "`vendor` tinytext NOT NULL," + - "`product` text NOT NULL," + - "`world` tinytext NOT NULL," + - "`x` int(11) NOT NULL," + - "`y` int(11) NOT NULL," + - "`z` int(11) NOT NULL," + - "`buyprice` float(32) NOT NULL," + - "`sellprice` float(32) NOT NULL," + - "`shoptype` tinytext NOT NULL," + - "PRIMARY KEY (`id`)" + + "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 = @@ -79,113 +79,68 @@ public abstract class Database { "`type` TINYTEXT NOT NULL" + ");"; - // Create table "shop_list" + String queryCheckIfTableExists = + (Database.this instanceof SQLite ? + "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'shop_list'" : + "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'shop_list'"); + + String queryCopyTableShopList = "INSERT INTO shops (vendor,product,world,x,y,z,buyprice,sellprice,shoptype) SELECT vendor,product,world,x,y,z,buyprice,sellprice,shoptype FROM shop_list"; + String queryRenameTableShopList = "ALTER TABLE shop_list RENAME TO shop_list_old"; + + // Create table "shops" Statement s = connection.createStatement(); s.executeUpdate(queryCreateTableShopList); s.close(); - // Create table "shop_log" + // Check if old table "shop_list" exists Statement s2 = connection.createStatement(); - s2.executeUpdate(queryCreateTableShopLog); - s2.close(); + ResultSet rs = s2.executeQuery(queryCheckIfTableExists); - // Count entries in table "shop_list" - PreparedStatement ps = connection.prepareStatement("SELECT * FROM shop_list"); - ResultSet rs = ps.executeQuery(); + if (rs.next()) { + plugin.debug("Table 'shop_list' exists: Copying contents..."); + // Table exists: Copy contents to new table + PreparedStatement ps = connection.prepareStatement(queryCopyTableShopList); + ps.executeUpdate(); + ps.close(); + + plugin.debug("Renaming table..."); + // Rename/Backup old table + PreparedStatement ps2 = connection.prepareStatement(queryRenameTableShopList); + ps2.executeUpdate(); + ps2.close(); + } + + s2.close(); + rs.close(); + + // Create table "shop_log" + Statement s3 = connection.createStatement(); + s3.executeUpdate(queryCreateTableShopLog); + s3.close(); + + // Count entries in table "shops" + PreparedStatement ps = connection.prepareStatement("SELECT * FROM shops"); + ResultSet rs2 = ps.executeQuery(); int count = 0; - while (rs.next()) { - if (rs.getString("vendor") != null) count++; + while (rs2.next()) { + if (rs2.getString("vendor") != null) count++; } plugin.debug("Initialized database with " + count + " entries"); - close(ps, rs); + close(ps, rs2); if (callback != null) callback.callSyncResult(count); } catch (SQLException ex) { if (callback != null) callback.callSyncError(ex); - plugin.getLogger().severe("Failed to connect to database"); - plugin.debug("Failed to connect to database"); + plugin.getLogger().severe("Failed to initialize database"); + plugin.debug("Failed to initialize database"); plugin.debug(ex); } } }.runTaskAsynchronously(plugin); } - /** - * @return Lowest possible ID which is not used (> 0) - */ - public void getNextFreeID(final Callback callback) { - getHighestID(new Callback(plugin) { - @Override - public void onResult(Object result) { - if (result instanceof Integer) { - int highestId = (int) result; - - for (int i = 1; i <= highestId + 1; i++) { - final int id = i; - isShop(i, new Callback(plugin) { - @Override - public void onResult(Object result) { - if (result instanceof Boolean) { - boolean isShop = (boolean) result; - if (!isShop) { - if (callback != null) callback.callSyncResult(id); - } - } - } - - @Override - public void onError(Throwable throwable) { - if (callback != null) callback.callSyncError(throwable); - } - }); - } - } - } - - @Override - public void onError(Throwable throwable) { - if (callback != null) callback.callSyncError(throwable); - } - }); - } - - /** - * @return Highest ID which is used - */ - public void getHighestID(final Callback callback) { - new BukkitRunnable() { - @Override - public void run() { - PreparedStatement ps = null; - ResultSet rs = null; - - int highestID = 0; - - try { - ps = connection.prepareStatement("SELECT * FROM shop_list;"); - rs = ps.executeQuery(); - - while (rs.next()) { - if (rs.getInt("id") > highestID) { - highestID = rs.getInt("id"); - } - } - - plugin.debug("Highest used ID: " + highestID); - if (callback != null) callback.callSyncResult(highestID); - } catch (SQLException ex) { - if (callback != null) callback.callSyncError(ex); - plugin.debug("Failed to get highest used ID"); - plugin.getLogger().severe("Failed to access database"); - } finally { - close(ps, rs); - } - } - }.runTaskAsynchronously(plugin); - } - /** * Remove a shop from the database * @@ -198,7 +153,7 @@ public abstract class Database { PreparedStatement ps = null; try { - ps = connection.prepareStatement("DELETE FROM shop_list WHERE id = " + shop.getID() + ";"); + ps = connection.prepareStatement("DELETE FROM shops WHERE id = " + shop.getID() + ";"); plugin.debug("Removing shop from database (#" + shop.getID() + ")"); ps.executeUpdate(); if (callback != null) callback.callSyncResult(null); @@ -226,7 +181,7 @@ public abstract class Database { ResultSet rs = null; try { - ps = connection.prepareStatement("SELECT * FROM shop_list WHERE id = " + id + ";"); + ps = connection.prepareStatement("SELECT * FROM shops WHERE id = " + id + ";"); rs = ps.executeQuery(); while (rs.next()) { @@ -250,72 +205,71 @@ public abstract class Database { } /** - * @param id ID of the shop - * @return Shop with the given ID + * Get all shops from the database */ - public void getShop(final int id, final Callback callback) { + public void getShops(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 shop_list WHERE id = " + id + ";"); + ps = connection.prepareStatement("SELECT * FROM shops"); rs = ps.executeQuery(); while (rs.next()) { - if (rs.getInt("id") == id) { - plugin.debug("Getting Shop... (#" + id + ")"); + int id = rs.getInt("id"); - String worldName = rs.getString("world"); - World world = Bukkit.getWorld(worldName); - int x = rs.getInt("x"); - int y = rs.getInt("y"); - int z = rs.getInt("z"); + plugin.debug("Getting Shop... (#" + id + ")"); - if (world == null) { - WorldNotFoundException ex = new WorldNotFoundException("Could not find world with name \"" + worldName + "\""); - callback.callSyncError(ex); - plugin.getLogger().warning(ex.getMessage()); - plugin.debug("Failed to get shop (#" + id + ")"); - plugin.debug(ex); - return; - } + String worldName = rs.getString("world"); + World world = Bukkit.getWorld(worldName); + int x = rs.getInt("x"); + int y = rs.getInt("y"); + int z = rs.getInt("z"); - Location location = new Location(world, x, y, z); + if (world == null) { + WorldNotFoundException ex = new WorldNotFoundException("Could not find world with name \"" + worldName + "\""); + if (callback != null) callback.callSyncError(ex); + plugin.getLogger().warning(ex.getMessage()); + plugin.debug("Failed to get shop (#" + id + ")"); + plugin.debug(ex); + continue; + } - Shop shop = plugin.getShopUtils().getShop(location); - if (shop != null) { - plugin.debug("Shop already exists, returning existing one (#" + id + ")."); - if (callback != null) callback.callSyncResult(shop); - } else { - plugin.debug("Creating new shop... (#" + id + ")"); + Location location = new Location(world, x, y, z); - OfflinePlayer vendor = Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("vendor"))); - ItemStack product = Utils.decode(rs.getString("product")); - double buyPrice = rs.getDouble("buyprice"); - double sellPrice = rs.getDouble("sellprice"); - ShopType shopType = ShopType.valueOf(rs.getString("shoptype")); + Shop shop = plugin.getShopUtils().getShop(location); + if (shop != null) { + plugin.debug("Shop already exists, returning existing one (#" + id + ")."); + if (callback != null) callback.callSyncResult(shop); + } else { + plugin.debug("Initializing new shop... (#" + id + ")"); - if (callback != null) callback.callSyncResult(new Shop(id, plugin, vendor, product, location, buyPrice, sellPrice, shopType)); - } + OfflinePlayer vendor = Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("vendor"))); + ItemStack product = Utils.decode(rs.getString("product")); + double buyPrice = rs.getDouble("buyprice"); + double sellPrice = rs.getDouble("sellprice"); + ShopType shopType = ShopType.valueOf(rs.getString("shoptype")); - return; + shops.add(new Shop(id, plugin, vendor, product, location, buyPrice, sellPrice, shopType)); } } - plugin.debug("Shop with ID not found, returning null. (#" + id + ")"); + if (callback != null) callback.callSyncResult(shops.toArray(new Shop[shops.size()])); } catch (SQLException ex) { if (callback != null) callback.callSyncError(ex); plugin.getLogger().severe("Failed to access database"); - plugin.debug("Failed to get shop (#" + id + ")"); + plugin.debug("Failed to get shops"); plugin.debug(ex); } finally { close(ps, rs); } - if (callback != null) callback.callSyncResult(null); + } }.runTaskAsynchronously(plugin); } @@ -329,23 +283,31 @@ public abstract class Database { @Override public void run() { PreparedStatement ps = null; + ResultSet rs = null; try { - ps = connection.prepareStatement("REPLACE INTO shop_list (id,vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?,?)"); + ps = connection.prepareStatement("REPLACE INTO shops (vendor,product,world,x,y,z,buyprice,sellprice,shoptype) VALUES(?,?,?,?,?,?,?,?,?)", Statement.RETURN_GENERATED_KEYS); - 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.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(); - if (callback != null) callback.callSyncResult(null); + int shopId = -1; + rs = ps.getGeneratedKeys(); + if (rs.next()) { + shopId = rs.getInt(1); + } + + shop.setId(shopId); + + if (callback != null) callback.callSyncResult(shopId); plugin.debug("Adding shop to database (#" + shop.getID() + ")"); } catch (SQLException ex) { if (callback != null) callback.callSyncError(ex); @@ -353,7 +315,7 @@ public abstract class Database { plugin.debug("Failed to add shop to database (#" + shop.getID() + ")"); plugin.debug(ex); } finally { - close(ps, null); + close(ps, rs); } } }.runTaskAsynchronously(plugin); diff --git a/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java b/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java index f344828..fa5371e 100644 --- a/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java +++ b/src/main/java/de/epiceric/shopchest/utils/ShopUtils.java @@ -210,42 +210,27 @@ public class ShopUtils { plugin.debug("Removed shop (#" + shop.getID() + ")"); } - plugin.getShopDatabase().getHighestID(new Callback(plugin) { + plugin.getShopDatabase().getShops(new Callback(plugin) { @Override public void onResult(Object result) { - if (result instanceof Integer) { - int highestId = (int) result; - - int count = 0; - for (int i = 1; i <= highestId; i++) { - final int id = i; - - plugin.debug("Trying to add shop. (#" + id + ")"); - plugin.getShopDatabase().getShop(id, new Callback(plugin) { - @Override - public void onResult(Object result) { - if (result instanceof Shop) { - Shop shop = (Shop) result; - shop.create(); - addShop(shop, false); - } - } - - @Override - public void onError(Throwable throwable) { - plugin.debug("Error while adding shop (#" + id + "):"); - plugin.debug(throwable); - } - }); - - count++; + if (result instanceof Shop[]) { + Shop[] shops = (Shop[]) result; + for (Shop shop : shops) { + if (shop.create()) { + addShop(shop, false); + } } - - if (callback != null) callback.callSyncResult(count); + if (callback != null) callback.callSyncResult(shops.length); } } - }); + @Override + public void onError(Throwable throwable) { + callback.callSyncError(throwable); + plugin.debug("Error while adding shops"); + plugin.debug(throwable); + } + }); } }); }