package xyz.veronie.bgg.localdb; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import xyz.veronie.bgg.result.Thing; import xyz.veronie.bgg.result.ThingMetaData; import xyz.veronie.bgg.ui.helpers.Resources; public class SqliteController { private static final SqliteController dbcontroller = new SqliteController(); private static Connection connection; private String dbPath; static { try { Class.forName("org.sqlite.JDBC"); } catch (ClassNotFoundException e) { System.err.println("Error while loading JDBC driver"); e.printStackTrace(); } } public Path getDbPath() { return Paths.get(dbPath); } private SqliteController() { dbPath = Resources.INSTANCE.getTmpDir() + File.separator + "bggtool.db"; initDBConnection(); } public static SqliteController getInstance() { return dbcontroller; } private void initDBConnection() { try { if (connection != null) return; System.out.println("Creating Connection to Database..."); connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath); if (!connection.isClosed()) System.out.println("...Connection established"); // init connection Statement stmt = connection.createStatement(); stmt.execute("PRAGMA foreign_keys = ON;"); stmt.close(); } catch (SQLException e) { // throw new RuntimeException(e); System.out.println("Could not init local DB."); } Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { if (!connection.isClosed() && connection != null) { connection.close(); if (connection.isClosed()) System.out.println("Connection to Database closed"); } } catch (SQLException e) { e.printStackTrace(); } } }); } public void createSchema() { if(connection == null) { throw new RuntimeException("ERROR: Couldn't create Schema, connection is null."); } try { Statement stmt = connection.createStatement(); stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Thing (ThingId INTEGER PRIMARY KEY, Name, ImgUrl, ThumbUrl, Comment, NumPlays);"); stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingList (ListId INTEGER PRIMARY KEY, Name);"); stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idx1 ON ThingList(Name)"); stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingListToThing (ListId INTEGER, ThingId INTEGER, FOREIGN KEY (ListId) REFERENCES ThingList(ListId), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idx2 ON ThingListToThing(ListId, ThingId)"); stmt.close(); } catch (SQLException e) { System.err.println("Couldn't create Schema"); e.printStackTrace(); } } public void createThingListIfNotExists(String name) { if(connection == null) { throw new RuntimeException("ERROR: Couldn't create Schema, connection is null."); } try { Statement stmt = connection.createStatement(); // insert new thing list if one with that name does not exist already System.err.println("Opening thing list '" + name + "'."); stmt.execute("INSERT OR IGNORE INTO ThingList (name) VALUES ('" + name + "');"); stmt.close(); } catch (SQLException e) { System.err.println("Couldn't add to thing list with handle " + name); e.printStackTrace(); } } public void deleteThingList(String name) throws SQLException { if(connection == null) { throw new RuntimeException("ERROR: Couldn't create Schema, connection is null."); } if(!hasThingList(name)) { System.err.println("INFO: deleteThingList: Thing list '" + name + "' does not exist."); return; } int listId = getThingListId(name); Statement stmt = connection.createStatement(); // insert new thing list if one with that name does not exist already System.err.println("INFO: deleteThingList: Deleting thing list '" + name + "'."); stmt.execute("DELETE FROM ThingListToThing where ListId = " + Integer.toString(listId) + ";"); stmt.execute("DELETE FROM ThingList where ListId = " + Integer.toString(listId) + ";"); stmt.close(); } public boolean hasThingList(String name) throws SQLException { if(getThingListId(name) != -1) { return true; } return false; } public int getThingListId(String name) throws SQLException { if(connection == null) { throw new RuntimeException("ERROR: Couldn't create Schema, connection is null."); } Statement stmt = connection.createStatement(); String countStr = "SELECT COUNT(*) as count from ThingList where name = '" + name + "'"; ResultSet res = stmt.executeQuery(countStr); int count = res.getInt("count"); if(count == 0) { stmt.close(); return -1; } String str = "SELECT ListId from ThingList where name = '" + name + "'"; System.out.println("TRACE: executeQuery: " + str); res = stmt.executeQuery(str); int listId = res.getInt(1); System.out.println("DEBUG: ListId = " + listId); stmt.close(); return listId; } public void addToThingList(String name, List things) throws SQLException { if(connection == null) { throw new RuntimeException("ERROR: Couldn't create Schema, connection is null."); } int listId = getThingListId(name); PreparedStatement thingStatement = connection .prepareStatement("INSERT OR REPLACE INTO Thing VALUES (?, ?, ?, ?, ?, ?);"); PreparedStatement listToThingStatement = connection .prepareStatement("INSERT OR IGNORE INTO ThingListToThing VALUES (?, ?);"); for (Thing thing : things) { ThingMetaData metaData = thing.getMetaData(); thingStatement.setInt(1, metaData.getId()); thingStatement.setString(2, metaData.getName()); thingStatement.setString(3, metaData.getImgURL()); thingStatement.setString(4, metaData.getThumbURL()); thingStatement.setString(5, metaData.getComment()); if(metaData.getNumPlays() != null) { thingStatement.setInt(6, metaData.getNumPlays()); } else { thingStatement.setNull(6, java.sql.Types.INTEGER); } thingStatement.execute(); // get generated id ResultSet keys = thingStatement.getGeneratedKeys(); int thingId = keys.getInt(1); System.out.println("TRACE: ThingId = " + thingId + ", ListId = " + listId); listToThingStatement.setInt(1, listId); listToThingStatement.setInt(2, thingId); listToThingStatement.addBatch(); } connection.setAutoCommit(false); listToThingStatement.executeBatch(); connection.setAutoCommit(true); } /// Retrieve the list of names of ThingLists. public List getThingLists() throws SQLException { if(connection == null) { throw new RuntimeException("ERROR: Couldn't create Schema, connection is null."); } Statement stmt = null; stmt = connection.createStatement(); String str = "SELECT Name from ThingList order by Name asc"; System.out.println("TRACE: executeQuery: " + str); ResultSet res = stmt.executeQuery(str); List listNames = new ArrayList(); while(res.next()) { listNames.add(res.getString(1)); } stmt.close(); return listNames; } /// Retrieve all Things (with metadata, no details) for a given ThingList. public List getThingList(String name) throws SQLException { if(connection == null) { throw new RuntimeException("ERROR: Couldn't create Schema, connection is null."); } Statement stmt = null; stmt = connection.createStatement(); String str = "SELECT t.ThingId, t.Name, ImgUrl, ThumbUrl, Comment, NumPlays from Thing t " + "join ThingListToThing tltt on t.ThingId = tltt.ThingId " + "join ThingList tl on tl.ListId = tltt.ListId " + "where tl.Name = '" + name + "' order by t.Name asc"; System.out.println("TRACE: executeQuery: " + str); ResultSet res = stmt.executeQuery(str); List thingList = new ArrayList(); while(res.next()) { int id = res.getInt(1); ThingMetaData metaData = new ThingMetaData(id, res.getString(2), res.getString(3), res.getString(4), res.getString(5), res.getInt(6)); Thing thing = new Thing(id, metaData); thingList.add(thing); } stmt.close(); return thingList; } }