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.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; 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()); 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; } /** * testing method. * * @return String */ @Deprecated public String getHello() { Connection connection = getConnection(); String hello = ""; try { assert connection != null; PreparedStatement statement = connection.prepareStatement("SELECT * FROM test WHERE id = 1"); ResultSet resultSet = statement.executeQuery(); if (resultSet.next()) hello = resultSet.getString("hello"); connection.close(); } catch (SQLException e) { e.printStackTrace(); } return hello; } /** * 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 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(); Category category = null; try { category = new Category(rs.getLong("id"), rs.getString("name"), rs.getString("image")); entry.getValue().close(); } catch (SQLException e) { e.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; } /** * 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, Connection> entry = getResultSets(id, authorBook, book); assert entry != null; try { List list = new ArrayList<>(); for (ResultSet rs : entry.getKey()) { 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 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"), 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, Connection> entry = getResultSets(book, "id"); assert entry != null; List ids = new ArrayList<>(); try { for (ResultSet resultSet : entry.getKey()) { ids.add(resultSet.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; try { PreparedStatement statement = connection.prepareStatement("INSERT INTO book(title, description, " + "price, year, publisher, category, image) VALUES (?, ?, ?, ?, ?, ?, ?)"); statement.setString(1, book.getTitle()); statement.setString(2, book.getDescription()); statement.setInt(3, book.getPrice()); statement.setInt(4, book.getYear().getValue()); statement.setLong(5, book.getPublisher().getId()); statement.setLong(6, book.getCategory().getId()); statement.setString(7, book.getImage()); insertCount += statement.executeUpdate(); for (Author author : book.getAuthors()) { PreparedStatement authorStatement = connection.prepareStatement("INSERT INTO author_book (author, book) VALUES (?, ?)"); statement.setLong(1, author.getId()); statement.setLong(2, book.getIsbn()); 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 ? WHERE id=?"); statement.setString(1, table); statement.setLong(2, 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 ? WHERE ID = ?"); statement.setString(1, table); statement.setLong(2, 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, Connection> getResultSets(long id, @NotNull String table, @NotNull String column) { Connection connection = getConnection(); try { assert connection != null; PreparedStatement statement = connection.prepareStatement("SELECT * FROM ? WHERE ? = ?"); statement.setString(1, table); statement.setString(2, column); statement.setLong(3, id); 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, Connection> getResultSets(@NotNull String table, @NotNull String... columns) { Connection connection = getConnection(); try { assert connection != null; PreparedStatement statement = connection.prepareStatement("SELECT (?) FROM ?"); statement.setString(1, String.join(",", columns)); statement.setString(2, 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, Connection> getAllEntries(@NotNull Connection connection, @NotNull PreparedStatement statement) { try { ResultSet resultSet = statement.executeQuery(); Map.Entry, Connection> entry = new AbstractMap.SimpleEntry<>(new ArrayList<>(), connection); while (resultSet.next()) { entry.getKey().add(resultSet); } return entry; } catch (SQLException ex) { ex.printStackTrace(); } return null; } }