diff --git a/ChestsPlusPlus-Database/pom.xml b/ChestsPlusPlus-Database/pom.xml new file mode 100644 index 0000000..9361a15 --- /dev/null +++ b/ChestsPlusPlus-Database/pom.xml @@ -0,0 +1,97 @@ + + + + ChestsPlusPlus-Parent + com.jamesdpeters.minecraft.chests + 1.0-SNAPSHOT + + 4.0.0 + + ChestsPlusPlus-Database + + + 16 + 16 + + + + + maven-google + https://maven.google.com + + + + + + org.hibernate + hibernate-core + 5.5.4.Final + + + org.hibernate + hibernate-jpamodelgen + 5.5.4.Final + + + + + com.h2database + h2 + 1.4.200 + + + org.projectlombok + lombok + 1.18.20 + + + + org.reflections + reflections + 0.9.12 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + + org.hibernate + hibernate-jpamodelgen + 5.5.4.Final + + + org.projectlombok + lombok + 1.18.20 + + + + + + maven-assembly-plugin + + + package + + single + + + + + + jar-with-dependencies + + + + + + + \ No newline at end of file diff --git a/ChestsPlusPlus-Database/src/main/java/com/jamesdpeters/minecraft/database/hibernate/Database.java b/ChestsPlusPlus-Database/src/main/java/com/jamesdpeters/minecraft/database/hibernate/Database.java new file mode 100644 index 0000000..c6c5075 --- /dev/null +++ b/ChestsPlusPlus-Database/src/main/java/com/jamesdpeters/minecraft/database/hibernate/Database.java @@ -0,0 +1,62 @@ +package com.jamesdpeters.minecraft.database.hibernate; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public class Database { + + private final Class clazz; + + public Database(Class clazz) { + this.clazz = clazz; + } + + //** MEMBER METHODS **/ + public void saveEntity(Object object) { + runTransaction(entityManager -> entityManager.persist(object)); + } + + public Optional findEntity(Object id) { + return Optional.ofNullable(HibernateUtil.getEntityManager().find(clazz, id)); + } + + public List findAll() { + var q = HibernateUtil.getCriteriaQuery(clazz); + q.select(q.from(clazz)); + return HibernateUtil.getEntityManager().createQuery(q).getResultList(); + } + + public List findAll(String sql) { + var q = HibernateUtil.getEntityManager().createQuery(sql, clazz); + return q.getResultList(); + } + + public TypedQuery getQuery(String sql) { + return HibernateUtil.getEntityManager().createQuery(sql, clazz); + } + + public void runTransaction(Consumer sessionConsumer) { + var entityManager = HibernateUtil.getEntityManager(); + var transaction = entityManager.getTransaction(); + try { + transaction.begin(); + sessionConsumer.accept(entityManager); + transaction.commit(); + } catch (RuntimeException e) { + transaction.rollback(); + throw e; + } + } + + public void refresh(T entity) { + HibernateUtil.getEntityManager().refresh(entity); + } + + public Class clazz() { + return clazz; + } + +} diff --git a/ChestsPlusPlus-Database/src/main/java/com/jamesdpeters/minecraft/database/hibernate/HibernateUtil.java b/ChestsPlusPlus-Database/src/main/java/com/jamesdpeters/minecraft/database/hibernate/HibernateUtil.java new file mode 100644 index 0000000..2fcd3ba --- /dev/null +++ b/ChestsPlusPlus-Database/src/main/java/com/jamesdpeters/minecraft/database/hibernate/HibernateUtil.java @@ -0,0 +1,76 @@ +package com.jamesdpeters.minecraft.database.hibernate; + +import lombok.experimental.UtilityClass; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import java.io.File; +import java.util.Arrays; +import java.util.Properties; + +@UtilityClass +public class HibernateUtil { + + private static SessionFactory sessionFactory; + private static EntityManager entityManager; + + private static File databaseDirectory; + + private static Properties hibernateProperties() { + Properties properties = new Properties(); + properties.put(Environment.DRIVER, "org.h2.Driver"); + properties.put(Environment.URL, "jdbc:h2:file:"+databaseDirectory.getAbsolutePath()+"/db;AUTO_SERVER=TRUE"); + properties.put(Environment.USER, "sa"); + properties.put(Environment.PASS, "sa"); + properties.put(Environment.FORMAT_SQL, "true"); + properties.put(Environment.DIALECT, "org.hibernate.dialect.H2Dialect"); + properties.put(Environment.SHOW_SQL, "true"); + properties.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread"); + properties.put(Environment.HBM2DDL_AUTO, "update"); + // properties.put(Environment.POOL_SIZE, "5"); + return properties; + } + + private static SessionFactory buildSessionFactory(Class[] entities) { + try { + var config = new Configuration() + .addProperties(hibernateProperties()) + .addPackage("com.jamesdpeters.minecraft.database.entities"); + Arrays.stream(entities).forEach(config::addAnnotatedClass); + return config.buildSessionFactory(); + } catch (Throwable ex) { + System.err.println("build SeesionFactory failed :" + ex); + throw new ExceptionInInitializerError(ex); + } + } + + public void init(File databaseDirectory, Class[] entities) { + HibernateUtil.databaseDirectory = databaseDirectory; + sessionFactory = buildSessionFactory(entities); + entityManager = sessionFactory.createEntityManager(); + } + + public EntityManager getEntityManager() { + return entityManager; + } + + public CriteriaQuery getCriteriaQuery(Class clazz) { + return entityManager.getCriteriaBuilder().createQuery(clazz); + } + + public CriteriaBuilder getCriteriaBuilder() { + return entityManager.getCriteriaBuilder(); + } + + public void close() { + if (sessionFactory != null) + sessionFactory.close(); + if (entityManager != null) + entityManager.close(); + } + +} diff --git a/ChestsPlusPlus_Main/pom.xml b/ChestsPlusPlus_Main/pom.xml index bd16a93..8511d5a 100644 --- a/ChestsPlusPlus_Main/pom.xml +++ b/ChestsPlusPlus_Main/pom.xml @@ -154,6 +154,14 @@ jar compile + + + + + + + + org.jetbrains annotations @@ -169,6 +177,12 @@ provided + + com.jamesdpeters.minecraft.chests + ChestsPlusPlus-Database + 1.0-SNAPSHOT + jar-with-dependencies + @@ -190,6 +204,7 @@ net.wesjd:anvilgui* org.bstats*:* com.jamesdpeters.minecraft.chests + com.jamesdpeters*:* diff --git a/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/ChestsPlusPlus.java b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/ChestsPlusPlus.java index 7f3f6fb..d64d8a6 100644 --- a/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/ChestsPlusPlus.java +++ b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/ChestsPlusPlus.java @@ -5,6 +5,7 @@ import com.jamesdpeters.minecraft.chests.commands.AutoCraftCommand; import com.jamesdpeters.minecraft.chests.commands.ChestLinkCommand; import com.jamesdpeters.minecraft.chests.commands.ChestsPlusPlusCommand; import com.jamesdpeters.minecraft.chests.crafting.Crafting; +import com.jamesdpeters.minecraft.chests.database.DBUtil; import com.jamesdpeters.minecraft.chests.lang.LangFileProperties; import com.jamesdpeters.minecraft.chests.listeners.HopperListener; import com.jamesdpeters.minecraft.chests.listeners.InventoryListener; @@ -27,6 +28,7 @@ import com.jamesdpeters.minecraft.chests.serialize.SpigotConfig; import com.jamesdpeters.minecraft.chests.storage.autocraft.AutoCraftingStorage; import com.jamesdpeters.minecraft.chests.storage.chestlink.ChestLinkStorage; import com.jamesdpeters.minecraft.chests.versionchecker.UpdateChecker; +import com.jamesdpeters.minecraft.database.hibernate.HibernateUtil; import fr.minuskube.inv.InventoryManager; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; @@ -42,6 +44,8 @@ import org.bukkit.plugin.java.annotation.plugin.Description; import org.bukkit.plugin.java.annotation.plugin.Plugin; import org.bukkit.plugin.java.annotation.plugin.author.Author; +import java.util.UUID; + @Plugin(name = "ChestsPlusPlus", version = BuildConstants.VERSION) @ApiVersion(ApiVersion.Target.v1_14) @Description(value = "Minecraft Spigot mod that enhances chests and hoppers, with ChestLinks and Hopper filters!") @@ -83,7 +87,6 @@ public class ChestsPlusPlus extends JavaPlugin { } - @SuppressWarnings("ConstantConditions") @Override public void onEnable() { int pluginId = 7166; @@ -133,6 +136,20 @@ public class ChestsPlusPlus extends JavaPlugin { // Remove armour stands if disabled Utils.fixEntities(); + DBUtil.init(); + var playerDb = DBUtil.PLAYER; + var partyDb = DBUtil.PARTIES; + + var test = playerDb.findPlayer(UUID.fromString("e0e93eb6-2ca4-4ac2-803f-684ce0b69b2c")); + getLogger().info("Original parties: "+test.getOwnedParties()); + + var party = partyDb.findParty(test, "Test Party 2"); + party = party == null ? partyDb.createParty(test, "Test Party 2") : party; + + getLogger().info("Found party: "+party); + getLogger().info("Players parties: "+test.getOwnedParties()); + + //Load storages after load. Bukkit.getScheduler().scheduleSyncDelayedTask(this, () -> { Crafting.load(); @@ -153,6 +170,7 @@ public class ChestsPlusPlus extends JavaPlugin { public void onDisable() { super.onDisable(); Config.save(); + HibernateUtil.close(); // //Remove entities that could have been left behind from bad save files/crashes etc. // Utils.fixEntities(); } diff --git a/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/DBUtil.java b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/DBUtil.java new file mode 100644 index 0000000..28e867a --- /dev/null +++ b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/DBUtil.java @@ -0,0 +1,27 @@ +package com.jamesdpeters.minecraft.chests.database; + +import com.jamesdpeters.minecraft.chests.ChestsPlusPlus; +import com.jamesdpeters.minecraft.chests.database.dao.PlayerDatabase; +import com.jamesdpeters.minecraft.chests.database.dao.PlayerPartyDatabase; +import com.jamesdpeters.minecraft.chests.database.entities.Player; +import com.jamesdpeters.minecraft.chests.database.entities.PlayerParty; +import com.jamesdpeters.minecraft.database.hibernate.HibernateUtil; + +public class DBUtil { + + /** + * ALL HIBERNATE ENTITIES + */ + private final static Class[] entities = { + Player.class, + PlayerParty.class + }; + + public final static PlayerDatabase PLAYER = new PlayerDatabase(); + public final static PlayerPartyDatabase PARTIES = new PlayerPartyDatabase(); + + public static void init() { + HibernateUtil.init(ChestsPlusPlus.PLUGIN.getDataFolder(), entities); + } + +} diff --git a/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/dao/PlayerDatabase.java b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/dao/PlayerDatabase.java new file mode 100644 index 0000000..7a204a6 --- /dev/null +++ b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/dao/PlayerDatabase.java @@ -0,0 +1,22 @@ +package com.jamesdpeters.minecraft.chests.database.dao; + +import com.jamesdpeters.minecraft.chests.database.entities.Player; +import com.jamesdpeters.minecraft.database.hibernate.Database; +import org.bukkit.OfflinePlayer; + +import java.util.UUID; + +public class PlayerDatabase extends Database { + + public PlayerDatabase() { + super(Player.class); + } + + public Player findPlayer(UUID uuid) { + return findEntity(uuid).orElseGet(() -> new Player(uuid)); + } + + public Player findPlayer(OfflinePlayer player) { + return findPlayer(player.getUniqueId()); + } +} diff --git a/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/dao/PlayerPartyDatabase.java b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/dao/PlayerPartyDatabase.java new file mode 100644 index 0000000..95e4b61 --- /dev/null +++ b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/dao/PlayerPartyDatabase.java @@ -0,0 +1,36 @@ +package com.jamesdpeters.minecraft.chests.database.dao; + +import com.jamesdpeters.minecraft.chests.database.DBUtil; +import com.jamesdpeters.minecraft.chests.database.entities.Player; +import com.jamesdpeters.minecraft.chests.database.entities.PlayerParty; +import com.jamesdpeters.minecraft.database.hibernate.Database; + +public class PlayerPartyDatabase extends Database { + + public PlayerPartyDatabase() { + super(PlayerParty.class); + } + + public PlayerParty createParty(Player owner, String name) { + PlayerParty playerParty = findParty(owner, name); + + if (playerParty != null) + return playerParty; + + playerParty = new PlayerParty(); + playerParty.setOwner(owner); + playerParty.setName(name); + + saveEntity(playerParty); + DBUtil.PLAYER.refresh(owner); + return playerParty; + } + + public PlayerParty findParty(Player owner, String name) { + var query = getQuery("from PlayerParty where owner.playerUUID = :ownerId and name = :name"); + query.setParameter("ownerId", owner.getPlayerUUID()); + query.setParameter("name", name); + return query.getSingleResult(); + } + +} diff --git a/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/entities/Player.java b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/entities/Player.java new file mode 100644 index 0000000..caf9e02 --- /dev/null +++ b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/entities/Player.java @@ -0,0 +1,40 @@ +package com.jamesdpeters.minecraft.chests.database.entities; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.OneToMany; +import java.util.List; +import java.util.UUID; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class Player { + + public Player(UUID uuid) { + playerUUID = uuid; + } + + @Id + private UUID playerUUID; + + @OneToMany(mappedBy = "owner") + @ToString.Exclude + private List ownedParties; + + @ManyToMany(mappedBy = "members") + @ToString.Exclude + private List playerParties; + + public OfflinePlayer getOfflinePlayer() { + return Bukkit.getOfflinePlayer(playerUUID); + } +} diff --git a/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/entities/PlayerParty.java b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/entities/PlayerParty.java new file mode 100644 index 0000000..be0a81d --- /dev/null +++ b/ChestsPlusPlus_Main/src/main/java/com/jamesdpeters/minecraft/chests/database/entities/PlayerParty.java @@ -0,0 +1,43 @@ +package com.jamesdpeters.minecraft.chests.database.entities; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import java.util.List; + +@Entity +@Getter +@Setter +@ToString +@NoArgsConstructor +@Table(uniqueConstraints = { + @UniqueConstraint(columnNames = {"NAME", "OWNER_PLAYERUUID"}) +}) +public class PlayerParty { + + @Id + @GeneratedValue + private Long partyId; + + @Column(nullable = false) + private String name; + + @JoinColumn(nullable = false) + @ManyToOne(optional = false) + private Player owner; + + @ManyToMany + @ToString.Exclude + private List members; +} diff --git a/pom.xml b/pom.xml index 901dce9..106332b 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ + ChestsPlusPlus-Database ChestsPlusPlusAPI ChestsPlusPlus_1_17_R1 ChestsPlusPlus_1_16_R3