diff --git a/src/main/java/com/Acrobot/ChestShop/Database/Account.java b/src/main/java/com/Acrobot/ChestShop/Database/Account.java index 4163e68..441796c 100644 --- a/src/main/java/com/Acrobot/ChestShop/Database/Account.java +++ b/src/main/java/com/Acrobot/ChestShop/Database/Account.java @@ -11,6 +11,7 @@ import java.util.UUID; * @author Andrzej Pomirski (Acrobot) */ @DatabaseTable(tableName = "accounts") +@DatabaseFileName("users.db") public class Account { @DatabaseField(canBeNull = false) diff --git a/src/main/java/com/Acrobot/ChestShop/Database/DaoCreator.java b/src/main/java/com/Acrobot/ChestShop/Database/DaoCreator.java new file mode 100644 index 0000000..94a0ab2 --- /dev/null +++ b/src/main/java/com/Acrobot/ChestShop/Database/DaoCreator.java @@ -0,0 +1,59 @@ +package com.Acrobot.ChestShop.Database; + +import com.Acrobot.ChestShop.ChestShop; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.dao.LruObjectCache; +import com.j256.ormlite.db.SqliteDatabaseType; +import com.j256.ormlite.jdbc.JdbcConnectionSource; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; + +import java.security.InvalidParameterException; +import java.sql.SQLException; + +/** + * Creates a DAO appropriate for the plugin + * + * @author Andrzej Pomirski + */ +public class DaoCreator { + + /** + * Returns a DAO for the given entity and with the given ID + * @param entity Entity's class + * @param Type of the entity + * @return Dao + * @throws InvalidParameterException + * @throws SQLException + */ + public static Dao getDao(Class entity) throws InvalidParameterException, SQLException { + if (!entity.isAnnotationPresent(DatabaseFileName.class)) { + throw new InvalidParameterException("Entity not annotated with @DatabaseFileName!"); + } + + String fileName = entity.getAnnotation(DatabaseFileName.class).value(); + String uri = ConnectionManager.getURI(ChestShop.loadFile(fileName)); + + ConnectionSource connectionSource = new JdbcConnectionSource(uri, new SqliteDatabaseType()); + + Dao dao = DaoManager.createDao(connectionSource, entity); + dao.setObjectCache(new LruObjectCache(200)); + + return dao; + } + + /** + * Creates a dao as well as a default table, if doesn't exist + * @see #getDao(Class) + * @throws SQLException + * @throws InvalidParameterException + */ + public static Dao getDaoAndCreateTable(Class entity) throws SQLException, InvalidParameterException { + Dao dao = getDao(entity); + + TableUtils.createTableIfNotExists(dao.getConnectionSource(), entity); + + return dao; + } +} diff --git a/src/main/java/com/Acrobot/ChestShop/Database/DatabaseFileName.java b/src/main/java/com/Acrobot/ChestShop/Database/DatabaseFileName.java new file mode 100644 index 0000000..d44e03c --- /dev/null +++ b/src/main/java/com/Acrobot/ChestShop/Database/DatabaseFileName.java @@ -0,0 +1,13 @@ +package com.Acrobot.ChestShop.Database; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents the filename of the database (inside ChestShop's folder) + * @author Andrzej Pomirski + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface DatabaseFileName { + String value(); +} diff --git a/src/main/java/com/Acrobot/ChestShop/Database/Item.java b/src/main/java/com/Acrobot/ChestShop/Database/Item.java new file mode 100644 index 0000000..2055803 --- /dev/null +++ b/src/main/java/com/Acrobot/ChestShop/Database/Item.java @@ -0,0 +1,39 @@ +package com.Acrobot.ChestShop.Database; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +/** + * Mapping for enchanted items + * @author Andrzej Pomirski + */ +@DatabaseTable(tableName = "items") +@DatabaseFileName("items.db") +public class Item { + + @DatabaseField(canBeNull = false, generatedId = true) + private int id; + + @DatabaseField(columnName = "code", canBeNull = false, unique = true) + private String base64ItemCode; + + public Item() { + //empty constructor + } + + public Item(String base64ItemCode) { + this.base64ItemCode = base64ItemCode; + } + + public int getId() { + return id; + } + + public String getBase64ItemCode() { + return base64ItemCode; + } + + public void setBase64ItemCode(String base64ItemCode) { + this.base64ItemCode = base64ItemCode; + } +} diff --git a/src/main/java/com/Acrobot/ChestShop/Database/Migrations.java b/src/main/java/com/Acrobot/ChestShop/Database/Migrations.java new file mode 100644 index 0000000..7b4eac8 --- /dev/null +++ b/src/main/java/com/Acrobot/ChestShop/Database/Migrations.java @@ -0,0 +1,56 @@ +package com.Acrobot.ChestShop.Database; + +import com.Acrobot.ChestShop.ChestShop; +import com.j256.ormlite.dao.Dao; + +import java.sql.SQLException; + +/** + * File handling the database migrations + * + * @author Andrzej Pomirski + */ +public class Migrations { + public static final int CURRENT_DATABASE_VERSION = 2; + + /** + * Migrates a database from the given version + * + * @param currentVersion Current version of the database + * @return Current database version + */ + public static int migrate(int currentVersion) { + if (currentVersion != CURRENT_DATABASE_VERSION) { + ChestShop.getBukkitLogger().info("Updating database..."); + } else { + return CURRENT_DATABASE_VERSION; + } + + switch (currentVersion) { + case 1: + boolean migrated = migrateTo2(); + + if (migrated) { + currentVersion++; + } + + case 2: + default: + //do nothing + } + + return currentVersion; + } + + private static boolean migrateTo2() { + try { + Dao accounts = DaoCreator.getDao(Account.class); + + accounts.executeRaw("ALTER TABLE `accounts` ADD COLUMN lastSeenName VARCHAR"); + return true; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/src/main/java/com/Acrobot/ChestShop/Metadata/ItemDatabase.java b/src/main/java/com/Acrobot/ChestShop/Metadata/ItemDatabase.java index 314cf22..95a2736 100644 --- a/src/main/java/com/Acrobot/ChestShop/Metadata/ItemDatabase.java +++ b/src/main/java/com/Acrobot/ChestShop/Metadata/ItemDatabase.java @@ -1,22 +1,19 @@ package com.Acrobot.ChestShop.Metadata; -import com.Acrobot.Breeze.Database.Database; -import com.Acrobot.Breeze.Database.Row; -import com.Acrobot.Breeze.Database.Table; import com.Acrobot.Breeze.Utils.Encoding.Base62; import com.Acrobot.Breeze.Utils.Encoding.Base64; -import com.Acrobot.ChestShop.ChestShop; +import com.Acrobot.ChestShop.Database.DaoCreator; +import com.Acrobot.ChestShop.Database.Item; +import com.j256.ormlite.dao.Dao; import org.bukkit.configuration.file.YamlConstructor; import org.bukkit.configuration.file.YamlRepresenter; import org.bukkit.inventory.ItemStack; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.nodes.Tag; import java.io.IOException; import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; -import java.util.Map; /** * Saves items with Metadata in database, which allows for saving items on signs easily. @@ -24,33 +21,15 @@ import java.util.Map; * @author Acrobot */ public class ItemDatabase { - private static final Map METADATA_CACHE = new HashMap(); + private Dao itemDao; private final Yaml yaml; - private Table table; public ItemDatabase() { - try { - Class.forName("org.sqlite.JDBC"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - ChestShop.getBukkitLogger().severe("You haven't got any SQLite JDBC installed!"); - } - - Database database = new Database("jdbc:sqlite:" + ChestShop.loadFile("items.db").getAbsolutePath()); - yaml = new Yaml(new YamlConstructor(), new YamlRepresenter(), new DumperOptions()); + yaml = new Yaml(new YamlBukkitConstructor(), new YamlRepresenter(), new DumperOptions()); try { - Statement statement = database.getConnection().createStatement(); - statement.executeUpdate("PRAGMA user_version = 1"); //We'll be able to change it later if we need to - statement.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - - try { - table = database.getTable("items"); - table.create("id INTEGER PRIMARY KEY, code VARCHAR UNIQUE ON CONFLICT IGNORE"); + itemDao = DaoCreator.getDaoAndCreateTable(Item.class); } catch (SQLException e) { e.printStackTrace(); } @@ -66,21 +45,29 @@ public class ItemDatabase { try { ItemStack clone = new ItemStack(item); clone.setAmount(1); + clone.setDurability((short) 0); String code = Base64.encodeObject(yaml.dump(clone)); - table.insertRow("null, '" + code + '\''); + Item itemEntity = itemDao.queryBuilder().where().eq("code", code).queryForFirst(); + + if (itemEntity != null) { + return Base62.encode(itemEntity.getId()); + } + + itemEntity = new Item(code); + + itemDao.create(itemEntity); + + int id = itemEntity.getId(); - int id = Integer.parseInt(table.getRow("code='" + code + '\'').get("id")); return Base62.encode(id); - } catch (IOException e) { - e.printStackTrace(); - return null; } catch (SQLException e) { e.printStackTrace(); - return null; - } catch (IllegalArgumentException e) { - return null; + } catch (IOException e) { + e.printStackTrace(); } + + return null; } /** @@ -90,32 +77,32 @@ public class ItemDatabase { * @return ItemStack represented by this code */ public ItemStack getFromCode(String code) { - if (METADATA_CACHE.containsKey(code)) { - return METADATA_CACHE.get(code); - } - try { - Row row = table.getRow("id='" + Base62.decode(code) + '\''); + int id = Base62.decode(code); + Item item = itemDao.queryBuilder().where().eq("id", id).queryForFirst(); - if (row.getSize() == 0) { + if (item == null) { return null; } - String serialized = row.get("code"); + String serialized = item.getBase64ItemCode(); - ItemStack item = (ItemStack) yaml.load((String) Base64.decodeToObject(serialized)); - METADATA_CACHE.put(code, item); - - return item; + return yaml.loadAs((String) Base64.decodeToObject(serialized), ItemStack.class); } catch (SQLException e) { e.printStackTrace(); - return null; } catch (IOException e) { e.printStackTrace(); - return null; } catch (ClassNotFoundException e) { e.printStackTrace(); - return null; } + + return null; + } + + private class YamlBukkitConstructor extends YamlConstructor { + public YamlBukkitConstructor() { + this.yamlConstructors.put(new Tag(Tag.PREFIX + "org.bukkit.inventory.ItemStack"), yamlConstructors.get(Tag.MAP)); + } + } } diff --git a/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java b/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java index a537936..be3cdd6 100644 --- a/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java +++ b/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java @@ -1,23 +1,17 @@ package com.Acrobot.ChestShop.UUIDs; import com.Acrobot.Breeze.Utils.NameUtil; -import com.Acrobot.ChestShop.ChestShop; import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Database.Account; -import com.Acrobot.ChestShop.Database.ConnectionManager; +import com.Acrobot.ChestShop.Database.DaoCreator; import com.Acrobot.ChestShop.Permission; import com.Acrobot.ChestShop.Signs.ChestShopSign; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.j256.ormlite.dao.Dao; -import com.j256.ormlite.dao.DaoManager; -import com.j256.ormlite.jdbc.JdbcConnectionSource; -import com.j256.ormlite.support.ConnectionSource; -import com.j256.ormlite.table.TableUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import java.io.File; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; @@ -28,6 +22,7 @@ import java.util.UUID; * * @author Andrzej Pomirski (Acrobot) */ +@SuppressWarnings("UnusedAssignment") //I deliberately set the variables to null while initializing public class NameManager { private static Dao accounts; @@ -229,19 +224,12 @@ public class NameManager { } public static boolean isAdminShop(UUID uuid) { - return getUsername(uuid).equals(Properties.ADMIN_SHOP_NAME); + return Properties.ADMIN_SHOP_NAME.equals(getUsername(uuid)); } public static void load() { - File databaseFile = ChestShop.loadFile("users.db"); - String uri = ConnectionManager.getURI(databaseFile); - ConnectionSource connection; - try { - connection = new JdbcConnectionSource(uri); - accounts = DaoManager.createDao(connection, Account.class); - - TableUtils.createTableIfNotExists(connection, Account.class); + accounts = DaoCreator.getDaoAndCreateTable(Account.class); Account adminAccount = new Account(Properties.ADMIN_SHOP_NAME, Bukkit.getOfflinePlayer(Properties.ADMIN_SHOP_NAME).getUniqueId()); accounts.createOrUpdate(adminAccount);