package de.hsel.itech.db; import de.hsel.itech.config.Configuration; import de.hsel.itech.db.pojo.Author; import de.hsel.itech.db.pojo.Book; import de.hsel.itech.db.pojo.Category; import de.hsel.itech.db.pojo.Publisher; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.mariadb.jdbc.MariaDbPoolDataSource; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.sql.*; import java.time.Year; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * base class for everything regarding the database. * * @author Johannes Theiner * @version 0.1 * @since 0.1 */ public class Database { private final String book = "book"; private final String author = "author"; private final String authorBook = "author_book"; private final String publisher = "publisher"; private final String category = "category"; private static Database instance; /** * Singleton for database access. * * @return {@link Database} */ public static Database getInstance() { if (instance == null) instance = new Database(); return instance; } private MariaDbPoolDataSource dataSource; /** * initializes connection pool and executes database setup. */ private Database() { Configuration config = Configuration.get(); dataSource = new MariaDbPoolDataSource("jdbc:mysql://" + config.getDatabase().getHostname() + ":" + config.getDatabase().getPort() + "/" + config.getDatabase().getDatabase() + "?useUnicode=true&characterEncoding=UTF-8"); try { dataSource.setUser(config.getDatabase().getUsername()); dataSource.setPassword(config.getDatabase().getPassword()); dataSource.initialize(); Connection connection = getConnection(); assert connection != null; URL file = getClass().getClassLoader().getResource("database.sql"); assert file != null; try (BufferedReader br = new BufferedReader(new FileReader(file.getFile()))) { for (String line = br.readLine(); line != null; line = br.readLine()) { PreparedStatement statement = connection.prepareStatement(line); statement.executeUpdate(); statement.close(); } } catch (IOException e) { e.printStackTrace(); } finally { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } /** * gets connection from connection pool. * * @return {@link java.sql.Connection} */ @Nullable private Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; } /** * gets author by id. * * @param id ID from database * @return {@link de.hsel.itech.db.pojo.Author} */ @Nullable public Author getAuthor(long id) { Map.Entry entry = getResultSet(id, author); assert entry != null; ResultSet rs = entry.getKey(); Author author = null; try { author = new Author(rs.getLong("id"), rs.getString("name")); entry.getValue().close(); } catch (SQLException e) { e.printStackTrace(); } return author; } /** * gets author by name. * * @param name author name * @return {@link de.hsel.itech.db.pojo.Author} */ @Nullable public Author getAuthor(String name) { Map.Entry entry = getResultSets(author, "name", name); assert entry != null; assert entry.getKey() != null; Author author = null; ResultSet rs = entry.getKey(); try { if(rs.next()) author = new Author(rs.getLong("id"), rs.getString("name")); entry.getValue().close(); } catch (SQLException e) { e.printStackTrace(); } return author; } /** * gets category by id. * * @param id ID from database * @return {@link de.hsel.itech.db.pojo.Category} */ @Nullable public Category getCategory(long id) { Map.Entry entry = getResultSet(id, category); assert entry != null; ResultSet rs = entry.getKey(); try { rs.beforeFirst(); } catch (SQLException e) { e.printStackTrace(); } return getCategory(entry); } /** * get category by name. * * @param name category name * @return {@link de.hsel.itech.db.pojo.Category} */ @Nullable public Category getCategory(String name) { Map.Entry entry = getResultSets(category, "name", name); assert entry != null; return getCategory(entry); } @Nullable private Category getCategory(@NotNull Map.Entry entry) { ResultSet rs = entry.getKey(); Category category = null; try{ while(rs.next()) { category = new Category(rs.getLong("id"), rs.getString("name"), rs.getString("image")); } entry.getValue().close(); }catch (SQLException ex) { ex.printStackTrace(); } return category; } /** * gets publisher by id. * * @param id ID from database * @return {@link de.hsel.itech.db.pojo.Publisher} */ @Nullable public Publisher getPublisher(long id) { Map.Entry entry = getResultSet(id, publisher); assert entry != null; ResultSet rs = entry.getKey(); Publisher publisher = null; try { publisher = new Publisher(rs.getLong("id"), rs.getString("name")); entry.getValue().close(); } catch (SQLException e) { e.printStackTrace(); } return publisher; } /** * get publisher by name. * * @param name publisher name * @return {@link de.hsel.itech.db.pojo.Publisher} */ @Nullable public Publisher getPublisher(String name) { Map.Entry entry = getResultSets(publisher, "name", name); assert entry != null; assert entry.getKey() != null; Publisher publisher = null; ResultSet rs = entry.getKey(); try { while(rs.next()) { publisher = new Publisher(rs.getLong("id"), rs.getString("name")); } entry.getValue().close(); } catch (SQLException e) { e.printStackTrace(); } return publisher; } /** * gets list of author ids from database. * * @param id book id from database * @return {@link java.util.List} */ @Nullable private List getAuthors(long id) { Map.Entry entry = getResultSets(id, authorBook, book); assert entry != null; try { List list = new ArrayList<>(); ResultSet rs = entry.getKey(); while (rs.next()) { list.add(rs.getInt(author)); } entry.getValue().close(); return list; } catch (SQLException e) { e.printStackTrace(); } return null; } /** * gets book by id. * * @param id ID from database * @return {@link de.hsel.itech.db.pojo.Book} */ @Nullable public Book getBook(long id) { Map.Entry entry = getResultSet(id, book); assert entry != null; ResultSet rs = entry.getKey(); Book book = null; try { Category category = getCategory(rs.getLong(this.category)); Publisher publisher = getPublisher(rs.getLong(this.publisher)); List authorIds = getAuthors(id); assert category != null; assert publisher != null; assert authorIds != null; List authors = new ArrayList<>(); for (int i : authorIds) { Author author = getAuthor(i); assert author != null; authors.add(author); } book = new Book(rs.getLong("id"), rs.getLong("isbn"), authors, publisher, category, rs.getString("title"), Year.of(rs.getInt("year")), rs.getInt("price"), rs.getString("description"), rs.getString("image")); entry.getValue().close(); } catch (SQLException e) { e.printStackTrace(); } return book; } /** * gets all books. * * @return {@link java.util.List} */ @Nullable public List getBooks() { Map.Entry entry = getResultSets(book, "id"); assert entry != null; List ids = new ArrayList<>(); try { ResultSet rs = entry.getKey(); while (rs.next()) { ids.add(rs.getLong("id")); } entry.getValue().close(); } catch (SQLException ex) { ex.printStackTrace(); } List books = new ArrayList<>(); for (long id : ids) { books.add(getBook(id)); } return books; } /** * gets books by category. * * @param category category id * @return {@link java.util.List} */ @Nullable public List getBooks(long category) { Connection connection = getConnection(); assert connection != null; List books = new ArrayList<>(); try { PreparedStatement statement = connection.prepareStatement("SELECT (id) FROM book WHERE category = ?"); statement.setLong(1, category); ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { books.add(getBook(resultSet.getLong("id"))); } connection.close(); } catch (SQLException e) { e.printStackTrace(); } return books; } /** * get books by category. * * @param category Category object * @return {@link java.util.List} */ @Nullable public List getBooks(@NotNull Category category) { return getBooks(category.getId()); } /** * Inserts book into database. * authors, publishers, and category have to exist. * * @param book Book * @return insert count */ public int insert(@NotNull Book book) { Connection connection = getConnection(); assert connection != null; int insertCount = 0; //author exists ? List authors = new ArrayList<>(); for (Author author : book.getAuthors()) { if (author.getId() == 0) { Author dbAuthor = getAuthor(author.getName()); if (dbAuthor == null) { insertCount += insert(author); dbAuthor = getAuthor(author.getName()); } authors.add(dbAuthor); }else authors.add(author); } //publisher exists ? Publisher publisher; if(book.getPublisher().getId() == 0) { Publisher dbPublisher = getPublisher(book.getPublisher().getName()); if(dbPublisher != null) publisher = dbPublisher; else { insertCount += insert(new Publisher(book.getPublisher().getName())); publisher = getPublisher(book.getPublisher().getName()); } }else publisher = book.getPublisher(); //category exists ? Category category; if(book.getCategory().getId() == 0) { Category dbCategory = getCategory(book.getCategory().getName()); if(dbCategory != null) category = dbCategory; else { insertCount += insert(new Category(book.getCategory().getName(), book.getCategory().getImage())); category = getCategory(book.getCategory().getName()); } }else category = book.getCategory(); assert publisher != null; assert category != null; try { PreparedStatement statement = connection.prepareStatement("INSERT INTO book(isbn, title, description, " + "price, year, publisher, category, image) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); statement.setLong(1, book.getIsbn()); statement.setString(2, book.getTitle()); statement.setString(3, book.getDescription()); statement.setInt(4, book.getPrice()); statement.setInt(5, book.getYear().getValue()); statement.setLong(6, publisher.getId()); statement.setLong(7, category.getId()); statement.setString(8, book.getImage()); insertCount += statement.executeUpdate(); ResultSet resultSet = statement.getGeneratedKeys(); long lastId = -1; while(resultSet.next()) { lastId = resultSet.getLong("id"); } for (Author author : authors) { PreparedStatement authorStatement = connection.prepareStatement("INSERT INTO " + authorBook + " (author, book) VALUES (?, ?);"); authorStatement.setLong(1, author.getId()); authorStatement.setLong(2, lastId); insertCount += authorStatement.executeUpdate(); } connection.close(); } catch (SQLException ex) { ex.printStackTrace(); } return insertCount; } /** * inserts Category into database. * * @param category Category * @return insert count */ public int insert(@NotNull Category category) { Connection connection = getConnection(); assert connection != null; int insertCount = 0; try { PreparedStatement statement = connection.prepareStatement("INSERT INTO category (name, image) VALUES (?, ?)"); statement.setString(1, category.getName()); statement.setString(2, category.getImage()); insertCount += statement.executeUpdate(); connection.close(); } catch (SQLException ex) { ex.printStackTrace(); } return insertCount; } /** * inserts Author. * * @param author Author * @return insert count */ public int insert(@NotNull Author author) { Connection connection = getConnection(); assert connection != null; int insertCount = 0; try { PreparedStatement statement = connection.prepareStatement("INSERT INTO author (name) VALUES (?)"); statement.setString(1, author.getName()); insertCount += statement.executeUpdate(); connection.close(); } catch (SQLException ex) { ex.printStackTrace(); } return insertCount; } /** * inserts Publisher. * * @param publisher Publisher * @return insert count */ public int insert(@NotNull Publisher publisher) { Connection connection = getConnection(); assert connection != null; int insertCount = 0; try { PreparedStatement statement = connection.prepareStatement("INSERT INTO publisher (name) VALUES (?)"); statement.setString(1, publisher.getName()); insertCount += statement.executeUpdate(); connection.close(); } catch (SQLException ex) { ex.printStackTrace(); } return insertCount; } /** * deletes book from database. * * @param id book id * @return deletion count */ public int deleteBook(long id) { int deleteCount = 0; try { Connection connection = getConnection(); assert connection != null; PreparedStatement statement = connection.prepareStatement("DELETE FROM author_book WHERE book = ?"); statement.setLong(1, id); deleteCount = +statement.executeUpdate(); connection.close(); } catch (SQLException ex) { ex.printStackTrace(); } deleteCount += delete(id, book); return deleteCount; } /** * deletes author from database. * * @param id author id * @return deletion count */ public int deleteAuthor(long id) { return delete(id, author); } /** * deletes publisher from database. * * @param id publisher id * @return deletion count */ public int deletePublisher(long id) { return delete(id, publisher); } /** * deletes category from database. * * @param id category count * @return deletion count */ public int deleteCategory(long id) { return delete(id, category); } /** * deletes id from table. * * @param id database id * @param table table name * @return deletion count */ private int delete(long id, String table) { Connection connection = getConnection(); assert connection != null; int deleteCount = 0; try { PreparedStatement statement = connection.prepareStatement("DELETE FROM " + table + " WHERE id=?"); statement.setLong(1, id); deleteCount = +statement.executeUpdate(); } catch (SQLException ex) { ex.printStackTrace(); } return deleteCount; } /** * gets specific entry from database. * * @param id id from database * @param table table name * @return {@link java.util.Map.Entry} */ @Nullable private Map.Entry getResultSet(long id, String table) { Connection connection = getConnection(); try { assert connection != null; PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + table + " WHERE id = ?"); statement.setLong(1, id); ResultSet resultSet = statement.executeQuery(); if (resultSet.next()) { return new AbstractMap.SimpleEntry<>(resultSet, connection); } return null; } catch (SQLException ex) { ex.printStackTrace(); } return null; } /** * gets specific entries from table. * * @param id id all entries should reference * @param table table name * @param column column to match * @return {@link java.util.Map.Entry} */ @Nullable private Map.Entry getResultSets(long id, @NotNull String table, @NotNull String column) { Connection connection = getConnection(); try { assert connection != null; PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + table + " WHERE " + column + " = ?"); statement.setLong(1, id); return getAllEntries(connection, statement); } catch (SQLException ex) { ex.printStackTrace(); } return null; } /** * gets specific entries from table. * * @param table table name * @param column column to match * @return {@link java.util.Map.Entry} */ @Nullable private Map.Entry getResultSets(@NotNull String table, @NotNull String column, @NotNull String value) { Connection connection = getConnection(); try { assert connection != null; PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + table + " WHERE " + column + " = ?"); statement.setString(1, value); return getAllEntries(connection, statement); } catch (SQLException ex) { ex.printStackTrace(); } return null; } /** * gets all specified columns from table. * * @param table table name * @param columns columns as String array * @return {@link java.util.Map.Entry} */ @Nullable private Map.Entry getResultSets(@NotNull String table, @NotNull String... columns) { Connection connection = getConnection(); try { assert connection != null; PreparedStatement statement = connection.prepareStatement("SELECT (" + String.join(",", columns) + ") FROM " + table); return getAllEntries(connection, statement); } catch (SQLException ex) { ex.printStackTrace(); } return null; } /** * gets all entries contained in result set. * * @param connection Connection * @param statement query statement * @return {@link java.util.Map.Entry} */ @Nullable private Map.Entry getAllEntries(@NotNull Connection connection, @NotNull PreparedStatement statement) { try { ResultSet resultSet = statement.executeQuery(); return new AbstractMap.SimpleEntry<>(resultSet, connection); } catch (SQLException ex) { ex.printStackTrace(); } return null; } }