results persistance
This commit is contained in:
parent
b35ffac6fa
commit
c7e9824208
10 changed files with 166 additions and 52 deletions
|
@ -1,11 +1,12 @@
|
|||
import data.dtos.QueryDTO;
|
||||
import data.enums.CustomPreparedStatementsRead;
|
||||
import data.models.QueryResultsModel;
|
||||
import data.repos.QueryResultsRepository;
|
||||
import services.AppConfigService;
|
||||
import services.DatabaseService;
|
||||
import services.ReportGenerationService;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
|
@ -18,13 +19,33 @@ class App {
|
|||
public App() {
|
||||
databaseService = new DatabaseService();
|
||||
appConfigService = new AppConfigService();
|
||||
reportGenerationService = new ReportGenerationService();
|
||||
}
|
||||
|
||||
final DatabaseService databaseService;
|
||||
final AppConfigService appConfigService;
|
||||
final private DatabaseService databaseService;
|
||||
final private AppConfigService appConfigService;
|
||||
final private ReportGenerationService reportGenerationService;
|
||||
|
||||
private static final CustomPreparedStatementsRead[] reportEndpoints = {
|
||||
CustomPreparedStatementsRead.HighestAndLowest10Vaccination,
|
||||
CustomPreparedStatementsRead.HighestInfectionsPer100K,
|
||||
//CustomPreparedStatementsRead.CountriesLaggingBehind,
|
||||
};
|
||||
|
||||
private static final CustomPreparedStatementsRead[] queryEndpoints = {
|
||||
CustomPreparedStatementsRead.DailyInfectionsAndDeathAggregate,
|
||||
};
|
||||
|
||||
public void run() {
|
||||
//appConfigService.promptUserArguments();
|
||||
final QueryResultsModel resultSetsQuery1 = databaseService.executeReadReportsEndpoint(new QueryDTO(CustomPreparedStatementsRead.HighestAndLowest10Vaccination));
|
||||
try {
|
||||
QueryResultsRepository resultsRepository;
|
||||
for (final CustomPreparedStatementsRead query : reportEndpoints) {
|
||||
resultsRepository = databaseService.executeReadReportsEndpoint(new QueryDTO(query));
|
||||
reportGenerationService.reportResults(resultsRepository);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,42 @@
|
|||
package apis;
|
||||
|
||||
import data.dtos.QueryDTO;
|
||||
import org.postgresql.util.PSQLException;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Date;
|
||||
import java.util.MissingFormatArgumentException;
|
||||
|
||||
/**
|
||||
* Communicates with the database to perform final CRUD operations, returning any results.
|
||||
* When there is a need for more than a single READ action, the Api can be abstracted, and implement into constituent action-specific APIs.
|
||||
*/
|
||||
public class DatabaseApi {
|
||||
public PreparedStatement performReadQuery(QueryDTO queryDTO, Connection connection) throws SQLException {
|
||||
// Set template
|
||||
PreparedStatement statement = connection.prepareStatement(queryDTO.statement().statementTemplate);
|
||||
|
||||
try {
|
||||
// Add in arguments
|
||||
for(int argIndex = 0; argIndex < queryDTO.templateArgs().length; argIndex++)
|
||||
for(int argIndex = 0; argIndex < queryDTO.templateArgs().length; argIndex++) {
|
||||
final Object curentArg = queryDTO.templateArgs()[argIndex];
|
||||
|
||||
// parameter indexing starts at 1
|
||||
statement.setString(argIndex + 1, queryDTO.templateArgs()[argIndex].toString());
|
||||
if (curentArg instanceof Date)
|
||||
statement.setDate(argIndex + 1, new java.sql.Date(((Date) curentArg).getTime()));
|
||||
else
|
||||
statement.setString(argIndex + 1, curentArg.toString());
|
||||
|
||||
}
|
||||
|
||||
statement.execute();
|
||||
} catch (PSQLException e) {
|
||||
if (e.getMessage().startsWith("No value specified for parameter")) throw new MissingFormatArgumentException("The templates present in the query, were not correctly mapped to their replacement variables.");
|
||||
else if (e.getMessage().startsWith("The column index is out of range:")) throw new MissingFormatArgumentException("No replacement variables needed, but were they were still supplied.");
|
||||
throw e;
|
||||
}
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,11 @@ package data.dtos;
|
|||
|
||||
import data.enums.CustomPreparedStatementsRead;
|
||||
|
||||
/**
|
||||
* Container for storing SQL statements to be executed, along with their aruments.
|
||||
* @param statement
|
||||
* @param templateArgs
|
||||
*/
|
||||
public record QueryDTO(CustomPreparedStatementsRead statement, Object[] templateArgs) {
|
||||
public QueryDTO(CustomPreparedStatementsRead customPreparedStatementsRead) {
|
||||
this(customPreparedStatementsRead, new Object[]{});
|
||||
|
|
|
@ -7,15 +7,17 @@ package data.enums;
|
|||
public enum CustomPreparedStatementsRead {
|
||||
DailyInfectionsAndDeathAggregate("""
|
||||
WITH matched_countries AS (
|
||||
SELECT c.name, c.code\s
|
||||
SELECT c.name, c.code
|
||||
FROM countries c
|
||||
WHERE c.name LIKE CONCAT('%',?, '%')
|
||||
)
|
||||
SELECT recorded_date, infections, deaths
|
||||
FROM cases
|
||||
WHERE iso_country IN (SELECT code FROM matched_countries) AND recorded_date <= ?
|
||||
ORDER BY recorded_date;
|
||||
"""
|
||||
ORDER BY recorded_date
|
||||
;
|
||||
""",
|
||||
"10 countries with the highest vaccinations (with count), and countries with lowest number of vaccinations."
|
||||
),
|
||||
|
||||
HighestAndLowest10Vaccination("""
|
||||
|
@ -42,7 +44,8 @@ public enum CustomPreparedStatementsRead {
|
|||
ORDER BY total_vaccinations ASC
|
||||
LIMIT 10
|
||||
;
|
||||
"""
|
||||
""",
|
||||
"10 countries with the highest infections per 100k inhabitants (with count)"
|
||||
),
|
||||
|
||||
HighestInfectionsPer100K("""
|
||||
|
@ -53,7 +56,8 @@ public enum CustomPreparedStatementsRead {
|
|||
ORDER BY infections_per_100k DESC
|
||||
LIMIT 10
|
||||
;
|
||||
"""
|
||||
""",
|
||||
""
|
||||
),
|
||||
|
||||
CountriesLaggingBehind("""
|
||||
|
@ -63,12 +67,15 @@ public enum CustomPreparedStatementsRead {
|
|||
GROUP BY c.name
|
||||
HAVING SUM(v.daily_vaccinations) < c.population
|
||||
;
|
||||
"""
|
||||
""",
|
||||
""
|
||||
);
|
||||
|
||||
public final String statementTemplate;
|
||||
public final String description;
|
||||
|
||||
private CustomPreparedStatementsRead(String statementTemplate) {
|
||||
private CustomPreparedStatementsRead(String statementTemplate, String description) {
|
||||
this.statementTemplate = statementTemplate;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
|
41
src/data/models/PersistentResultModel.java
Normal file
41
src/data/models/PersistentResultModel.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
package data.models;
|
||||
|
||||
import java.sql.Array;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PersistentResultModel {
|
||||
/**
|
||||
* Factory constructor, that adapts a given ResultSet to PersistentResultModel.
|
||||
* @param resultSet
|
||||
* @throws SQLException
|
||||
*/
|
||||
public PersistentResultModel (ResultSet resultSet) throws SQLException {
|
||||
final ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
|
||||
final int columnCount = resultSetMetaData.getColumnCount();
|
||||
|
||||
final ArrayList<Object[]> grwingResultsGrid = new ArrayList<>();
|
||||
while (resultSet.next()) {
|
||||
final Object[] currentRow = new Object[columnCount];
|
||||
for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
|
||||
currentRow[columnIndex] = resultSet.getObject(columnIndex+1);
|
||||
}
|
||||
grwingResultsGrid.add(currentRow);
|
||||
}
|
||||
|
||||
resultColumns = new ResultColumnModel[columnCount];
|
||||
for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
|
||||
int finalColumnIndex = columnIndex;
|
||||
final var entries = grwingResultsGrid.stream().map(row -> row[finalColumnIndex]).toList().toArray();
|
||||
resultColumns[columnIndex] = new ResultColumnModel(resultSetMetaData.getColumnName(columnIndex+1), entries);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final public ResultColumnModel[] resultColumns;
|
||||
public record ResultColumnModel(String columnName, Object[] entries){}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package data.models;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
/**
|
||||
* Stores the results obtained from executing an SQL query.
|
||||
* Essentially a type-def, which is not natively supported in Java.
|
||||
* @param resultSets Result of the query
|
||||
*/
|
||||
public record QueryResultsModel(java.util.ArrayList<ResultSet> resultSets) {
|
||||
}
|
28
src/data/repos/QueryResultsRepository.java
Normal file
28
src/data/repos/QueryResultsRepository.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package data.repos;
|
||||
|
||||
|
||||
import data.models.PersistentResultModel;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Stores the results obtained from executing an SQL query.
|
||||
*/
|
||||
public class QueryResultsRepository {
|
||||
/**
|
||||
* Store for the results
|
||||
*/
|
||||
final private ArrayList<PersistentResultModel> persistentResults = new ArrayList<>();
|
||||
public PersistentResultModel[] getResults () {return persistentResults.toArray(new PersistentResultModel[]{});}
|
||||
|
||||
/**
|
||||
* Adds a ResultSet to the store to persist.
|
||||
* @param resultSet
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void addResult(ResultSet resultSet) throws SQLException {
|
||||
persistentResults.add(new PersistentResultModel(resultSet));
|
||||
}
|
||||
}
|
|
@ -12,8 +12,8 @@ import java.util.Scanner;
|
|||
* Prompts and stores the configuration that the app needs to be run with.
|
||||
*/
|
||||
public class AppConfigService {
|
||||
String countryName;
|
||||
Date date;
|
||||
public String countryName;
|
||||
public Date date;
|
||||
|
||||
public void promptUserArguments() {
|
||||
final Scanner scanner = new Scanner(System.in);
|
||||
|
|
|
@ -2,14 +2,12 @@ package services;
|
|||
|
||||
import apis.DatabaseApi;
|
||||
import data.dtos.QueryDTO;
|
||||
import data.enums.CustomPreparedStatementsRead;
|
||||
import data.models.QueryResultsModel;
|
||||
import data.repos.QueryResultsRepository;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Stores the active connection between the app and the local SQL Server.
|
||||
* Stores the active connection between the app and the SQL Server.
|
||||
*/
|
||||
public class DatabaseService {
|
||||
private static final String url = "jdbc:postgresql://localhost:5432/lunatech_covid";
|
||||
|
@ -20,25 +18,27 @@ public class DatabaseService {
|
|||
private final Connection connection;
|
||||
|
||||
public DatabaseService() {
|
||||
connection = connect();
|
||||
connection = getConnection();
|
||||
databaseApi = new DatabaseApi();
|
||||
}
|
||||
|
||||
public QueryResultsModel executeReadReportsEndpoint(QueryDTO queryDTO) {
|
||||
try {
|
||||
/**
|
||||
* Fetches read-query results and packages them to persist them (and so free the connection).
|
||||
*
|
||||
* @param queryDTO The query with its arguments.
|
||||
* @return Persisting (non-lazy) results from executing the query.
|
||||
*/
|
||||
public QueryResultsRepository executeReadReportsEndpoint(QueryDTO queryDTO) throws SQLException {
|
||||
final PreparedStatement resultStatement = databaseApi.performReadQuery(queryDTO, connection);
|
||||
|
||||
final ArrayList<ResultSet> resultSets = new ArrayList<>();
|
||||
final QueryResultsRepository resultSets = new QueryResultsRepository();
|
||||
do {
|
||||
// execute and persist results in a model
|
||||
resultSets.add(resultStatement.getResultSet());
|
||||
final ResultSet resultSet = resultStatement.getResultSet();
|
||||
resultSets.addResult(resultSet);
|
||||
}
|
||||
while (resultStatement.getMoreResults());
|
||||
|
||||
return new QueryResultsModel(resultSets);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return resultSets;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,13 +46,14 @@ public class DatabaseService {
|
|||
*
|
||||
* @return a Connection object
|
||||
*/
|
||||
private Connection connect() {
|
||||
private Connection getConnection() {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = DriverManager.getConnection(url, user, password);
|
||||
System.out.println("Connected to the PostgreSQL server successfully.");
|
||||
} catch (SQLException e) {
|
||||
System.out.println(e.getMessage());
|
||||
System.out.println("Unable to connect to PostgreSQL server.");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
return connection;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package services;
|
||||
|
||||
import data.models.QueryResultsModel;
|
||||
import data.repos.QueryResultsRepository;
|
||||
|
||||
public class ReportGenerationService {
|
||||
public static void printResults(QueryResultsModel queryResultsModel) {
|
||||
public void reportResults(QueryResultsRepository queryResultsRepository) {
|
||||
//queryResultsModel.
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue