results persistance

This commit is contained in:
Mehul Ahal 2023-01-12 10:54:33 +01:00 committed by LahaLuhem
parent b35ffac6fa
commit c7e9824208
10 changed files with 166 additions and 52 deletions

View File

@ -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);
}
}
}

View File

@ -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);
// Add in arguments
for(int argIndex = 0; argIndex < queryDTO.templateArgs().length; argIndex++)
// parameter indexing starts at 1
statement.setString(argIndex + 1, queryDTO.templateArgs()[argIndex].toString());
try {
// Add in arguments
for(int argIndex = 0; argIndex < queryDTO.templateArgs().length; argIndex++) {
final Object curentArg = queryDTO.templateArgs()[argIndex];
// parameter indexing starts at 1
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;
}
statement.execute();
return statement;
}
}

View File

@ -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[]{});

View File

@ -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;
}
}

View 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){}
}

View File

@ -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) {
}

View 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));
}
}

View File

@ -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);

View File

@ -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 {
final PreparedStatement resultStatement = databaseApi.performReadQuery(queryDTO, connection);
/**
* 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<>();
do {
// execute and persist results in a model
resultSets.add(resultStatement.getResultSet());
}
while (resultStatement.getMoreResults());
return new QueryResultsModel(resultSets);
} catch (SQLException e) {
throw new RuntimeException(e);
final QueryResultsRepository resultSets = new QueryResultsRepository();
do {
final ResultSet resultSet = resultStatement.getResultSet();
resultSets.addResult(resultSet);
}
while (resultStatement.getMoreResults());
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;

View File

@ -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.
}
}