· 4 years ago · Jul 24, 2021, 05:52 PM
1package de.schmidi.serverLibrary.database;
2
3import java.sql.Connection;
4import java.sql.DriverManager;
5import java.sql.SQLException;
6import java.sql.Statement;
7import java.util.List;
8import java.util.stream.Collectors;
9
10import com.mysql.jdbc.PreparedStatement;
11
12import de.schmidi.serverLibrary.Plant4ForestPlugin;
13import de.schmidi.serverLibrary.model.DefaultErrorBehavior;
14import de.schmidi.serverLibrary.model.Identifiable;
15import de.schmidi.serverLibrary.model.P4FDatabaseErrorHandler;
16import de.schmidi.serverLibrary.service.AbstractP4FService;
17
18/***
19 *
20 * Is an abstract class which can be used to create a Repository class in a
21 * simple low effort way. You will only have to implement the given methods and
22 * an {@link AbstractP4FService} to register the repository.
23 *
24 * @author Schmidi
25 *
26 * @param <T> Represents the data type recieved by the database table.
27 * @param <D> Represents the data type from entries which can be deleted.
28 */
29public abstract class AbstractP4FRepository<T, D extends Identifiable> {
30
31 /***
32 * Represents the table name of an database to connect to.
33 */
34 protected final String tableName;
35
36 /***
37 * Represents the column name of the primary key.
38 */
39 private final String idColumnName;
40
41 /***
42 * Represents the structure of the database table. <div> <b>Example:</b>
43 *
44 * <pre>
45 * "(uuid VARCHAR(255), playtime BIGINT, PRIMARY KEY (uuid))"
46 * </pre>
47 *
48 * </div>
49 */
50 private final String defaultStructure;
51
52 /***
53 * Represents the {@link Plant4ForestPlugin} extending class.
54 */
55 private final Plant4ForestPlugin plugin;
56
57 /***
58 * Represents the Errorhandler
59 *
60 * @see {@link P4FDatabaseErrorHandler}
61 */
62 private P4FDatabaseErrorHandler errorHandler;
63
64 /***
65 *
66 * @param plugin Represents {@link #plugin}
67 * @param tableName Represents {@link #tableName}
68 * @param idColumnName Represents {@link #idColumnName}
69 * @param defaultStructure Represents {@link #defaultStructure}
70 */
71 public AbstractP4FRepository(Plant4ForestPlugin plugin, String tableName, String idColumnName,
72 String defaultStructure) {
73 this.plugin = plugin;
74 this.tableName = tableName;
75 this.idColumnName = idColumnName;
76 this.defaultStructure = defaultStructure;
77
78 this.errorHandler = new DefaultErrorBehavior(plugin, "Ein Datenbank Fehler ist aufgetreten.");
79 this.initializeDatabaseConnection();
80 }
81
82 /***
83 * Deletes unused data and stores service data to the connected database table.
84 *
85 * @param dataToRemove Represents the a list of data to remove.
86 * @param dataToStore Represents the data to store into the connected database
87 * table.
88 */
89 public void deleteAndSaveNew(List<D> dataToRemove, T dataToStore) {
90 this.cleanUpRemovedEntries(dataToRemove);
91 try {
92 this.saveAll(dataToStore);
93 } catch (SQLException e) {
94 e.printStackTrace();
95 }
96
97 }
98
99 /***
100 * Has to be implemented to save data to the connected database table.
101 *
102 * @param dataToStore Represents the data to store into the connected database
103 * table.
104 * @throws SQLException
105 */
106 protected abstract void saveAll(T dataToStore) throws SQLException;
107
108 /***
109 * Has to be implemented to recieve data from the connected database table.
110 *
111 * @return The found data with the given implementation.
112 */
113 public abstract T findAll();
114
115 /***
116 * Removes data from the connected database table.
117 *
118 * @param dataToRemove Represents a list of data to remove
119 */
120 private void cleanUpRemovedEntries(List<D> dataToRemove) {
121 if (!dataToRemove.isEmpty()) {
122 this.executeAndClose(connection -> {
123 String sqlDelete = String.format("DELETE FROM `%s` WHERE `%s` IN (%s)", this.tableName,
124 this.idColumnName, dataToRemove.stream().map(Identifiable::getIdentifier).map(Object::toString)
125 .collect(Collectors.joining(", ")));
126 Statement stmt = connection.createStatement();
127 stmt.execute(sqlDelete);
128
129 });
130 }
131 }
132
133 /***
134 * This method can be used to execute {@link PreparedStatement} within it. <div>
135 * <h5>Example</h5>
136 *
137 * <pre>
138 * this.executeAndClose(connection -> {
139 * connection.setAutoCommit(false);
140 * String insertQueryStatement = String.format("INSERT INTO `%s` VALUES(?,?)",this.tableName);
141 *
142 *
143 * PreparedStatement preparedStatement = connection.prepareStatement(insertQueryStatement);
144 * preparedStatement.setString(1, "id");
145 * long playtime = 1L;
146 * preparedStatement.setLong(2, playtime);
147 * preparedStatement.setLong(3, playtime);
148 * preparedStatement.executeUpdate();
149 * }
150 *
151 * connection.commit();
152 * });
153 * </pre>
154 *
155 * </div>
156 *
157 * @param task Represents the {@link ConnectionConsumer}
158 */
159 protected void executeAndClose(final ConnectionConsumer task) {
160 try (Connection con = getConnection()) {
161 task.accept(con);
162 } catch (SQLException e) {
163 if (this.errorHandler != null) {
164 this.errorHandler.onDatabaseError(e);
165 }
166 }
167 }
168
169 /***
170 *
171 */
172 private void initializeDatabaseConnection() {
173 this.executeAndClose(con -> {
174 this.createDatabase(con);
175 this.createTable(this.defaultStructure, con);
176 });
177 }
178
179 /***
180 *
181 * @param con
182 * @throws SQLException
183 */
184 private void createDatabase(Connection con) throws SQLException {
185 String sqlCreateDb = "CREATE DATABASE IF NOT EXISTS `" + this.plugin.getDatabaseNameInformation() + "`";
186 Statement stmt = con.createStatement();
187 stmt.execute(sqlCreateDb);
188 }
189
190 /***
191 *
192 * @param defaultTableStructure
193 * @param con
194 * @throws SQLException
195 */
196 private void createTable(String defaultTableStructure, Connection con) throws SQLException {
197 String sqlCreate = "CREATE TABLE IF NOT EXISTS `" + this.tableName + "`" + defaultTableStructure;
198 Statement stmt = con.createStatement();
199 stmt.execute(sqlCreate);
200 }
201
202 /***
203 *
204 * @return
205 * @throws SQLException
206 */
207 private Connection createDatabaseConnection() throws SQLException {
208
209 Connection connection = DriverManager.getConnection(
210 "jdbc:mysql://" + this.plugin.getDatabaseURL() + "?createDatabaseIfNotExist=true",
211 this.plugin.getUsernameInformation(), this.plugin.getPasswordInformation());
212 if (connection == null) {
213 System.out.println("Error on SQL");
214 }
215 return connection;
216
217 }
218
219 private Connection getConnection() throws SQLException {
220 return this.createDatabaseConnection();
221
222 }
223
224}