Internet-Technologien/src/main/java/de/hsel/itech/db/Database.java

816 lines
24 KiB
Java

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.json.JSONObject;
import org.mariadb.jdbc.MariaDbPoolDataSource;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
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<ResultSet, Connection> entry = getResultSetById(author, id);
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<ResultSet, Connection> entry = getResultSetsByValue(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<ResultSet, Connection> entry = getResultSetById(category, id);
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<ResultSet, Connection> entry = getResultSetsByValue(category, "name", name);
assert entry != null;
return getCategory(entry);
}
/**
* get Category from database entry.
* @param entry entry
* @return Category
*/
@Nullable
private Category getCategory(@NotNull Map.Entry<ResultSet, Connection> 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;
}
/**
* get all categories.
*
* @return list of categories
*/
@Nullable
public List<Category> getCategories() {
List<Long> ids = getIds(category);
List<Category> categories = new ArrayList<>();
for (long id : ids) {
categories.add(getCategory(id));
}
return categories;
}
/**
* 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<ResultSet, Connection> entry = getResultSetById(publisher, id);
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<ResultSet, Connection> entry = getResultSetsByValue(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<Integer> getAuthors(long id) {
Map.Entry<ResultSet, Connection> entry = getResultSetsById(authorBook, book, id);
assert entry != null;
try {
List<Integer> 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<ResultSet, Connection> entry = getResultSetById(book, id);
assert entry != null;
return buildBook(entry);
}
/**
* builds book from entry.
*
* @param entry Map.Entry
* @return Book
*/
@Nullable
private Book buildBook(@NotNull Map.Entry<ResultSet, Connection> entry) {
Book book = null;
ResultSet rs = entry.getKey();
try {
Category category = getCategory(rs.getLong(this.category));
Publisher publisher = getPublisher(rs.getLong(this.publisher));
List<Integer> authorIds = getAuthors(rs.getLong("id"));
assert category != null;
assert publisher != null;
assert authorIds != null;
List<Author> 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<Book> getBooks() {
List<Long> ids = getIds(book);
List<Book> 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<Book> getBooks(long category) {
Connection connection = getConnection();
assert connection != null;
List<Book> 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<Book> getBooks(@NotNull Category category) {
return getBooks(category.getId());
}
/**
* exists book with isbn ?
*
* @param isbn isbn
* @return true if book with isbn exists
*/
public boolean existsBook(long isbn) {
Map.Entry<ResultSet, Connection> entry = getResultSetByValue(book, "isbn", isbn);
try {
if (entry != null && entry.getKey() != null) {
entry.getValue().close();
return true;
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return false;
}
/**
* 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<Author> 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(), getRandomImage()));
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;
}
/**
* get all ids from specified table.
*
* @param table table
* @return list of ids
*/
private List<Long> getIds(String table) {
Map.Entry<ResultSet, Connection> entry = getColumnsFromResultSet(table, "id");
assert entry != null;
List<Long> ids = new ArrayList<>();
try {
ResultSet rs = entry.getKey();
while (rs.next()) {
ids.add(rs.getLong("id"));
}
entry.getValue().close();
} catch (SQLException ex) {
ex.printStackTrace();
}
return ids;
}
/**
* gets specific entry from database.
*
* @param table table name
* @param id id from database
* @return {@link java.util.Map.Entry}
*/
@Nullable
private Map.Entry<ResultSet, Connection> getResultSetById(String table, long id) {
return getResultSetByValue(table, "id", id);
}
@Nullable
private Map.Entry<ResultSet, Connection> getResultSetByValue(String table, String column, long value) {
Connection connection = getConnection();
try {
assert connection != null;
PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + table + " WHERE " + column + " = ?");
statement.setLong(1, value);
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 table table name
* @param column column to match
* @param id id all entries should reference
* @return {@link java.util.Map.Entry}
*/
@Nullable
private Map.Entry<ResultSet, Connection> getResultSetsById(@NotNull String table, @NotNull String column, long id) {
Connection connection = getConnection();
try {
assert connection != null;
PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + table + " WHERE " + column + " = ?");
statement.setLong(1, id);
return getAllEntriesFromResultSet(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<ResultSet, Connection> getResultSetsByValue(@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 getAllEntriesFromResultSet(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<ResultSet, Connection> getColumnsFromResultSet(@NotNull String table, @NotNull String... columns) {
Connection connection = getConnection();
try {
assert connection != null;
PreparedStatement statement = connection.prepareStatement("SELECT (" + String.join(",", columns) + ") FROM " + table);
return getAllEntriesFromResultSet(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<ResultSet, Connection> getAllEntriesFromResultSet(@NotNull Connection connection, @NotNull PreparedStatement statement) {
try {
ResultSet resultSet = statement.executeQuery();
return new AbstractMap.SimpleEntry<>(resultSet, connection);
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}
@Nullable
private String getRandomImage() {
try {
InputStream is = new URL("https://api.unsplash.com/photos/random?client_id=bb18673959b1cfd053a4ecb58e85bb8d99a0050ae5b71dac3e7632916eead9aa").openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
JSONObject json = new JSONObject(sb.toString());
return json.getString("id");
} finally {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}