
impl.H2DatabaseConnection Maven / Gradle / Ivy
package impl;
import api.Column;
import api.Table;
import api.sql.SQLDatabaseConnection;
import api.sql.SQLSchema;
import api.sql.SQLTable;
import api.sql.constraint.Constraint;
import api.sql.constraint.NotNull;
import api.sql.constraint.PrimaryKey;
import java.sql.*;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class H2DatabaseConnection implements SQLDatabaseConnection {
private static class H2DatabaseColumn implements Column{
private final String name;
private final String dataType;
private H2DatabaseColumn(String name, String dataType) {
this.name = name;
this.dataType = dataType;
}
@Override
public String getName() {
return name;
}
@Override
public String getDatatype() {
return dataType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
H2DatabaseColumn that = (H2DatabaseColumn) o;
return Objects.equals(name, that.name) &&
Objects.equals(dataType, that.dataType);
}
@Override
public int hashCode() {
return Objects.hash(name, dataType);
}
@Override
public String toString() {
return "H2DatabaseColumn{" +
"name='" + name + '\'' +
", dataType='" + dataType + '\'' +
'}';
}
}
private static class H2DatabaseTable implements SQLTable{
private final Set columns = new HashSet<>();
private final Set constraints = new HashSet<>();
private final String name;
private H2DatabaseTable(String name) {
assert name != null;
this.name = name;
}
@Override
public Set getConstraints() {
return constraints;
}
@Override
public String getName() {
return name;
}
@Override
public Set getColumns() {
return columns;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
H2DatabaseTable that = (H2DatabaseTable) o;
return Objects.equals(columns, that.columns) &&
Objects.equals(constraints, that.constraints) &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(columns, constraints, name);
}
@Override
public String toString() {
return "H2DatabaseTable{" +
"columns=" + columns +
", constraints=" + constraints +
", name='" + name + '\'' +
'}';
}
}
protected abstract String buildDatabaseUrl();
/**
* Initializes the database according to the given schema. Tables which are mentioned in the
* schema will be dropped if they already exist. All other tables will not be touched by this
* commmand.
* @param schemas The schema that the database have to fulfill
*/
@Override
public void init(Collection schemas) {
try(Connection connection = DriverManager.getConnection(buildDatabaseUrl())) {
schemas.forEach(sqlSchema -> initDatabase(sqlSchema, connection));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private void initDatabase(SQLSchema schema, Connection connection) {
schema.getTables().forEach(table -> {
try(Statement statement = connection.createStatement()){
statement.execute(String.format("DROP TABLE IF EXISTS %s CASCADE", table.getName()));
} catch (SQLException e) {
throw new RuntimeException(e);
}
List columns = new ArrayList<>(table.getColumns());
StringBuilder columnDefinition = new StringBuilder();
for (int i = 0; i < columns.size(); i++){
if (i > 0){
columnDefinition.append(",");
}
columnDefinition.append(String.format("%s %s", columns.get(i).getName(), columns.get(i).getDatatype()));
appendConstraints(table, columnDefinition, columns.get(i));
}
try(Statement statement = connection.createStatement()){
String queryString = String.format("CREATE TABLE %s (%s)", table.getName(), columnDefinition);
statement.execute(queryString);
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
}
@Override
public void insert(Row data, Collection> predicates) {
try(Connection connection = DriverManager.getConnection(buildDatabaseUrl())){
queryTableNames(connection).forEach(tableName -> {
try {
Table table = queryTableDefinition(connection, tableName);
if (predicates.stream().allMatch(tablePredicate -> tablePredicate.test(table))){
StringBuilder columnNames = new StringBuilder();
StringBuilder valuesHolder = new StringBuilder();
List values = new ArrayList<>(data.getColumns().size());
final boolean[] isFirstRow = {true};
data.getColumns().forEach(column -> {
if (!isFirstRow[0]){
columnNames.append(",");
valuesHolder.append(",");
}
columnNames.append(column.getName());
valuesHolder.append("?");
Optional value = data.get(column);
assert value.isPresent();
values.add(value.get());
isFirstRow[0] = false;
});
String query = String.format("INSERT INTO %s (%s) VALUES (%s)", table.getName(), columnNames, valuesHolder);
try(PreparedStatement statement = connection.prepareStatement(query)){
for (int i = 0; i < values.size(); i++){
statement.setString(i+1, values.get(i).get());
}
statement.execute();
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Stream read(Collection> predicates) {
try(Connection connection = DriverManager.getConnection(buildDatabaseUrl())) {
List tables = queryTableNames(connection);
if (tables.isEmpty()){
return Stream.empty();
}
List rows = new LinkedList<>();
tables.parallelStream().forEach(tableName -> {
H2DatabaseTable table;
try{
table = queryTableDefinition(connection, tableName);
} catch (SQLException e){
throw new RuntimeException(e);
}
try(Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
ResultSet constraintQueryResult = statement.executeQuery(String.format("SELECT * FROM %s", tableName));
if (constraintQueryResult.last()){
constraintQueryResult.beforeFirst();
while (constraintQueryResult.next()){
Map columnValues = new HashMap<>();
table.columns.forEach(column -> {
try {
String valueString = constraintQueryResult.getString(column.getName());
Value value = new Value("");
if (valueString != null){
value = new Value(valueString);
}
columnValues.put(column, value);
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
if (columnValues.entrySet().stream().allMatch(columnValueEntry -> {
final boolean[] result = {true};
predicates.forEach(tableColumnPredicate -> {
if (!tableColumnPredicate.test(new TableColumn(
table, columnValueEntry.getKey(), columnValueEntry.getValue())
)){
result[0] = false;
}
});
return result[0];
})){
rows.add(new Row(columnValues));
}
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
return rows.stream();
} catch (SQLException e){
throw new RuntimeException(e);
}
}
private H2DatabaseTable queryTableDefinition(Connection connection, String tableName) throws SQLException{
H2DatabaseTable table = new H2DatabaseTable(tableName);
try(Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
ResultSet constraintQueryResult = statement.executeQuery(String.format(
"select * from information_schema.columns where table_name = '%s';", tableName));
if (constraintQueryResult.last()){
//columns have been found for the current table
constraintQueryResult.beforeFirst();
while (constraintQueryResult.next()){
String columnName = constraintQueryResult.getString("COLUMN_NAME");
String[] dataTypeParts = constraintQueryResult.getString("COLUMN_TYPE").split(" ");
String dataType = dataTypeParts[0];
table.columns.add(new H2DatabaseColumn(columnName, dataType));
}
}
}
return table;
}
private List queryTableNames(Connection connection) throws SQLException {
List tables = new LinkedList<>();
try (Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
ResultSet tablesQueryResult = statement.executeQuery("SHOW TABLES");
if (!tablesQueryResult.last()) {
return Collections.emptyList();
}
tablesQueryResult.beforeFirst();
while (tablesQueryResult.next()) {
tables.add(tablesQueryResult.getString(1));
}
}
return tables;
}
/**
* Appends the constraint string to the given column definition string
* @param table The table containing the constraints
* @param columnDefinition The column definition string the constraints shall be appended to
* @param column The column the constraints refer to
*/
private void appendConstraints(SQLTable table, StringBuilder columnDefinition, Column column) {
Set constraints = table.getConstraints().stream().filter(
constraint -> Objects.equals(constraint.getConstrainedColumn(), column)
).collect(Collectors.toSet());
constraints.forEach(constraint -> {
if (constraint instanceof PrimaryKey){
columnDefinition.append(" PRIMARY KEY");
}
if (constraint instanceof NotNull){
columnDefinition.append(" NOT NULL");
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy