Skip to content

Commit a992f7d

Browse files
committed
Manage date types with PostgreSQL, H2 and Oracle databases
1 parent 61c8b4a commit a992f7d

16 files changed

+721
-47
lines changed

THIRD_PARTY.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SQL test data generator uses only for testing and doesn't ship with:
1212
* H2 licensed under Mozilla Public License Version 2.0 or Eclipse Public License v1.0 (https://github.com/h2database/h2database/blob/master/LICENSE.txt)
1313
* MariaDB Docker licenced under GNU General Public License v2.0 (https://github.com/MariaDB/mariadb-docker/blob/master/LICENSE)
1414
* MariaDB java connector licenced under GNU Lesser General Public License version 2.1 (https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/LICENSE)
15+
Please read this: https://mariadb.com/kb/en/licensing-faq/#licenses-used-by-mariadb
1516
* Microsoft SQL Server Docker image licensed under End-User Licensing Agreement (https://hub.docker.com/_/microsoft-mssql-server#environment-variables)
1617
* Microsoft JDBC Driver for SQL Server licensed under MIT License (https://github.com/microsoft/mssql-jdbc/blob/dev/LICENSE)
1718
* PostgreSQL Docker image licensed under MIT License (https://github.com/docker-library/postgres/blob/master/LICENSE)

src/main/java/org/stdg/ColumnValueFormatter.java

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,76 @@
1313

1414
package org.stdg;
1515

16+
import org.stdg.dbtype.DatabaseType;
17+
18+
import java.sql.Time;
19+
import java.sql.Timestamp;
20+
import java.time.OffsetTime;
21+
import java.util.Calendar;
22+
1623
class ColumnValueFormatter {
1724

1825
static final ColumnValueFormatter INSTANCE = new ColumnValueFormatter();
1926

2027
private ColumnValueFormatter() { }
2128

22-
String formatColumnValue(Object columnValue) {
23-
if (columnValue instanceof String || columnValue instanceof java.sql.Date) {
29+
String formatColumnValue(Object columnValue, DatabaseType dbType) {
30+
if(columnValue == null) {
31+
return "NULL";
32+
} else if(DatabaseType.ORACLE.equals(dbType)
33+
&& columnValue instanceof Timestamp) {
34+
Timestamp timeStamp = (Timestamp) columnValue;
35+
return buildOracleToDateFunctionFor(timeStamp);
36+
} else if(DatabaseType.ORACLE.equals(dbType)
37+
&& isOracleSqlTimestamp(columnValue)) {
38+
return buildOracleToTimeStampFunctionFor(columnValue);
39+
} else if (columnValue instanceof String
40+
|| columnValue instanceof java.sql.Date
41+
|| columnValue instanceof Timestamp
42+
|| columnValue instanceof Time
43+
|| columnValue instanceof OffsetTime
44+
|| isTimestampWithTimeZoneH2Type(columnValue)) {
2445
String stringColumnValue = columnValue.toString();
2546
return "'" + stringColumnValue + "'";
26-
} else if (columnValue == null) {
27-
return "NULL";
2847
}
2948
return columnValue.toString();
3049
}
3150

51+
private String buildOracleToDateFunctionFor(Timestamp timeStamp) {
52+
//https://stackoverflow.com/questions/9180014/using-oracle-to-date-function-for-date-string-with-milliseconds
53+
// "An Oracle DATE does not store times with more precision than a second."
54+
Calendar calendar = Calendar.getInstance();
55+
calendar.setTime(timeStamp);
56+
int monthNumber = calendar.get(Calendar.MONTH) + 1;
57+
int secondNumber = calendar.get(Calendar.SECOND);
58+
String toDateString = calendar.get(Calendar.YEAR)
59+
+ "-" + (monthNumber < 10 ? "0" : "") + monthNumber
60+
+ "-" + calendar.get(Calendar.DAY_OF_MONTH)
61+
+ "-" + calendar.get(Calendar.HOUR_OF_DAY)
62+
+ "-" + calendar.get(Calendar.MINUTE)
63+
+ "-" + (secondNumber < 10 ? "0" : "") + secondNumber;
64+
return "TO_DATE('" + toDateString + "', 'yyyy-mm-dd-HH24-mi-ss')";
65+
}
66+
67+
private boolean isOracleSqlTimestamp(Object columnValue) {
68+
Class<?> columnValueClass = columnValue.getClass();
69+
String classCanonicalName = columnValueClass.getCanonicalName();
70+
return classCanonicalName.equals("oracle.sql.TIMESTAMP");
71+
}
72+
73+
private String buildOracleToTimeStampFunctionFor(Object columnValue) {
74+
String oracleTimeStampAsString = columnValue.toString();
75+
String aDateWithMsLessThan100 = "2012-09-17 19:56:47.10";
76+
boolean dateHasMsLessThan100 = oracleTimeStampAsString.length() == aDateWithMsLessThan100.length();
77+
String dateForTimeStampCreation = dateHasMsLessThan100 ? oracleTimeStampAsString + "0" : oracleTimeStampAsString;
78+
return "TO_TIMESTAMP('" + dateForTimeStampCreation
79+
+ "', 'YYYY-MM-DD HH24:MI:SS.FF')";
80+
}
81+
82+
private boolean isTimestampWithTimeZoneH2Type(Object columnValue) {
83+
Class<?> columnValueClass = columnValue.getClass();
84+
String classCanonicalName = columnValueClass.getCanonicalName();
85+
return classCanonicalName.equals("org.h2.api.TimestampWithTimeZone");
86+
}
87+
3288
}

src/main/java/org/stdg/DatasetRowSet.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.stdg;
1515

16+
import org.stdg.dbtype.DatabaseType;
17+
1618
import javax.sql.DataSource;
1719
import java.util.*;
1820
import java.util.function.Function;
@@ -25,9 +27,13 @@ class DatasetRowSet {
2527

2628
private final Collection<DatasetRow> datasetRows = new ArrayDeque<>();
2729

28-
DatasetRowSet(DataSource dataSource, DatabaseMetadataFinder databaseMetadataFinder) {
30+
DatasetRowSet( DataSource dataSource
31+
, DatabaseType dbType
32+
, DatabaseMetadataFinder databaseMetadataFinder) {
2933
this.databaseMetadataFinder = databaseMetadataFinder;
30-
this.missingNotNullColumnsFinder = new MissingNotNullColumnsFinder(dataSource, databaseMetadataFinder);
34+
this.missingNotNullColumnsFinder = new MissingNotNullColumnsFinder(dataSource
35+
, dbType
36+
, databaseMetadataFinder);
3137
}
3238

3339
void add(Collection<DatasetRow> datasetRows) {

src/main/java/org/stdg/DatasetRowsFinder.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import java.util.*;
2525

2626
import static java.util.Collections.emptyList;
27-
import static org.stdg.SelectTransformerFactory.*;
27+
import static org.stdg.SelectTransformerFactory.createSelectTransformer;
2828

2929
class DatasetRowsFinder {
3030

@@ -61,7 +61,8 @@ private Collection<DatasetRow> execute(SqlQuery sqlQuery) {
6161

6262
while (resultSet.next()) {
6363
Collection<DatasetRow> datasetRows =
64-
buildDatasetRowsFrom(resultSet, resultSetMetaData, columnCount, sqlQuery);
64+
buildDatasetRowsFrom(resultSet, resultSetMetaData
65+
, columnCount, sqlQuery);
6566
datasetRowsToReturn.addAll(datasetRows);
6667
}
6768
} catch (SQLException sqlException) {

src/main/java/org/stdg/DatasetRowsGenerator.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.stdg;
1515

16+
import org.stdg.dbtype.DatabaseType;
17+
1618
import javax.sql.DataSource;
1719
import java.util.Collection;
1820
import java.util.List;
@@ -21,19 +23,23 @@ class DatasetRowsGenerator {
2123

2224
private final DataSource dataSource;
2325

26+
private final DatabaseType dbType;
27+
2428
private final DatabaseMetadataFinder databaseMetadataFinder;
2529

2630
private final DatasetRowsFinder datasetRowsFinder;
2731

2832
DatasetRowsGenerator(DataSource dataSource
33+
, DatabaseType dbType
2934
, DatabaseMetadataFinder databaseMetadataFinder) {
3035
this.dataSource = dataSource;
36+
this.dbType = dbType;
3137
this.databaseMetadataFinder = databaseMetadataFinder;
3238
this.datasetRowsFinder = new DatasetRowsFinder(dataSource);
3339
}
3440

3541
List<DatasetRow> generateDatasetRowsFor(List<SqlQuery> sqlQueries) {
36-
DatasetRowSet datasetRowSet = new DatasetRowSet(dataSource, databaseMetadataFinder);
42+
DatasetRowSet datasetRowSet = new DatasetRowSet(dataSource, dbType, databaseMetadataFinder);
3743
for (SqlQuery sqlQuery : sqlQueries) {
3844
Collection<DatasetRow> datasetRows = datasetRowsFinder.findDatasetRowsOf(sqlQuery);
3945
datasetRowSet.add(datasetRows);

src/main/java/org/stdg/InsertStatementsGenerator.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.stdg;
1515

16+
import org.stdg.dbtype.DatabaseType;
17+
1618
import java.util.Collection;
1719
import java.util.List;
1820
import java.util.Set;
@@ -23,9 +25,11 @@
2325

2426
class InsertStatementsGenerator {
2527

26-
public static final InsertStatementsGenerator INSTANCE = new InsertStatementsGenerator();
28+
private final DatabaseType dbType;
2729

28-
private InsertStatementsGenerator() { }
30+
InsertStatementsGenerator(DatabaseType dbType) {
31+
this.dbType = dbType;
32+
}
2933

3034
String generateInsertScriptFor(List<DatasetRow> datasetRows) {
3135
return datasetRows
@@ -50,7 +54,8 @@ private String formatColumnNames(Set<String> columnNames) {
5054
private String formatColumnValues(Collection<Object> columnValues) {
5155
return columnValues
5256
.stream()
53-
.map(ColumnValueFormatter.INSTANCE::formatColumnValue)
57+
.map(columnValue -> ColumnValueFormatter.INSTANCE
58+
.formatColumnValue(columnValue, dbType))
5459
.collect(joining(", "));
5560
}
5661

src/main/java/org/stdg/MissingNotNullColumnsFinder.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.stdg;
1515

16+
import org.stdg.dbtype.DatabaseType;
17+
1618
import javax.sql.DataSource;
1719
import java.util.Collection;
1820
import java.util.Collections;
@@ -24,10 +26,13 @@ class MissingNotNullColumnsFinder {
2426

2527
private final DataSource dataSource;
2628

29+
private final DatabaseType dbType;
30+
2731
private final DatabaseMetadataFinder databaseMetadataFinder;
2832

29-
MissingNotNullColumnsFinder(DataSource dataSource, DatabaseMetadataFinder databaseMetadataFinder) {
33+
MissingNotNullColumnsFinder(DataSource dataSource, DatabaseType dbType, DatabaseMetadataFinder databaseMetadataFinder) {
3034
this.dataSource = dataSource;
35+
this.dbType = dbType;
3136
this.databaseMetadataFinder = databaseMetadataFinder;
3237
}
3338

@@ -43,7 +48,7 @@ Map<String, Object> findMissingNoNullColumnsOf(DatasetRow datasetRow) {
4348
.collect(toList());
4449

4550
if (!missingNotNullColumns.isEmpty()) {
46-
RowFinder rowFinder = new RowFinder(dataSource);
51+
RowFinder rowFinder = new RowFinder(dataSource, dbType);
4752
DatasetRow datasetRowWithMissingNotNullColumns = rowFinder.findOneRowFrom(datasetRow.getTableName(), missingNotNullColumns, datasetRow);
4853
return datasetRowWithMissingNotNullColumns.getColumnValueByColumnName();
4954
}

src/main/java/org/stdg/RowFinder.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.stdg;
1515

16+
import org.stdg.dbtype.DatabaseType;
17+
1618
import javax.sql.DataSource;
1719
import java.sql.Connection;
1820
import java.sql.PreparedStatement;
@@ -24,15 +26,19 @@ class RowFinder {
2426

2527
private final DataSource dataSource;
2628

27-
RowFinder(DataSource dataSource) {
29+
private final DatabaseType dbType;
30+
31+
RowFinder(DataSource dataSource, DatabaseType dbType) {
2832
this.dataSource = dataSource;
33+
this.dbType = dbType;
2934
}
3035

3136
DatasetRow findOneRowFrom(String tableName
3237
, Collection<String> columnNamesToSearch
3338
, DatasetRow rowToSearch) {
3439

35-
SqlQuery missingColumnValuesQuery = SqlQuery.buildFromRow(columnNamesToSearch, rowToSearch);
40+
SqlQuery missingColumnValuesQuery =
41+
SqlQuery.buildFromRow(columnNamesToSearch, rowToSearch, dbType);
3642

3743
DatasetRow missingColumnValues = DatasetRow.ofTable(tableName);
3844
try (Connection connection = dataSource.getConnection();

src/main/java/org/stdg/SqlQuery.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.stdg;
1515

16+
import org.stdg.dbtype.DatabaseType;
17+
1618
import java.util.*;
1719

1820
import static java.util.stream.Collectors.joining;
@@ -33,13 +35,15 @@ public SqlQuery(String queryAsString, List<Object> parameters) {
3335
this.parameters = parameters;
3436
}
3537

36-
static SqlQuery buildFromRow(DatasetRow rowToSearch) {
38+
static SqlQuery buildFromRow(DatasetRow rowToSearch, DatabaseType dbType) {
3739
Set<String> columnNames = rowToSearch.getColumnNames();
38-
return buildFromRow(columnNames, rowToSearch);
40+
return buildFromRow(columnNames, rowToSearch, dbType);
3941

4042
}
4143

42-
static SqlQuery buildFromRow(Collection<String> columnNamesToSearch, DatasetRow rowToSearch) {
44+
static SqlQuery buildFromRow(Collection<String> columnNamesToSearch
45+
, DatasetRow rowToSearch
46+
, DatabaseType dbType) {
4347
Map<String, Object> valuesToMatch = rowToSearch.getColumnValueByColumnName();
4448
String whereConditions =
4549
valuesToMatch.entrySet()
@@ -49,7 +53,8 @@ static SqlQuery buildFromRow(Collection<String> columnNamesToSearch, DatasetRow
4953
return columnName
5054
+ (entry.getValue() == null
5155
? " IS NULL"
52-
: "=" + ColumnValueFormatter.INSTANCE.formatColumnValue(entry.getValue()));
56+
: "=" + ColumnValueFormatter.INSTANCE
57+
.formatColumnValue(entry.getValue(), dbType));
5358
})
5459
.collect(joining(" AND "));
5560
String queryAsString =

src/main/java/org/stdg/SqlTestDataGenerator.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,14 @@ public class SqlTestDataGenerator {
4040

4141
private final DatasetRowsGenerator datasetRowsGenerator;
4242

43-
private SqlTestDataGenerator(DatasetRowsGenerator datasetRowsGenerator) {
43+
private final DatabaseType dbType;
44+
45+
private InsertStatementsGenerator insertStatementGenerator;
46+
47+
private SqlTestDataGenerator(DatasetRowsGenerator datasetRowsGenerator, DatabaseType dbType) {
4448
this.datasetRowsGenerator = datasetRowsGenerator;
49+
this.dbType = dbType;
50+
insertStatementGenerator = new InsertStatementsGenerator(dbType);
4551
}
4652

4753
/**
@@ -55,12 +61,12 @@ public static SqlTestDataGenerator buildFrom(DataSource dataSource) {
5561
DatabaseType dbType = DatabaseType.findFromDbUrl(dbUrl);
5662
DatabaseMetadataFinder databaseMetadataFinder = DatabaseMetadataFinderFactory.createDatabaseMetadataFinderFrom(dataSource, dbType);
5763
DatabaseMetadataFinder databaseMetadataFinderWithCache = DatabaseMetadataFinderWithCache.buildFrom(databaseMetadataFinder);
58-
return buildFrom(dataSource, databaseMetadataFinderWithCache);
64+
return buildFrom(dataSource, dbType, databaseMetadataFinderWithCache);
5965
}
6066

61-
public static SqlTestDataGenerator buildFrom(DataSource dataSource, DatabaseMetadataFinder databaseMetadataFinder) {
62-
DatasetRowsGenerator datasetRowsGenerator = new DatasetRowsGenerator(dataSource, databaseMetadataFinder);
63-
return new SqlTestDataGenerator(datasetRowsGenerator);
67+
public static SqlTestDataGenerator buildFrom(DataSource dataSource, DatabaseType dbType, DatabaseMetadataFinder databaseMetadataFinder) {
68+
DatasetRowsGenerator datasetRowsGenerator = new DatasetRowsGenerator(dataSource, dbType, databaseMetadataFinder);
69+
return new SqlTestDataGenerator(datasetRowsGenerator, dbType);
6470
}
6571

6672
public String generateInsertScriptFor(String sqlQuery) {
@@ -74,7 +80,7 @@ public String generateInsertScriptFor(String query, List<Object> parameters) {
7480

7581
public String generateInsertScriptFor(List<SqlQuery> sqlQueries) {
7682
List<DatasetRow> datasetRows = datasetRowsGenerator.generateDatasetRowsFor(sqlQueries);
77-
return InsertStatementsGenerator.INSTANCE.generateInsertScriptFor(datasetRows);
83+
return insertStatementGenerator.generateInsertScriptFor(datasetRows);
7884
}
7985

8086
public String generateInsertScriptFor(String... sqlQueries) {
@@ -85,7 +91,7 @@ public String generateInsertScriptFor(String... sqlQueries) {
8591
}
8692

8793
public List<String> generateInsertListFor(DatasetRow datasetRow) {
88-
SqlQuery sqlQuery = SqlQuery.buildFromRow(datasetRow);
94+
SqlQuery sqlQuery = SqlQuery.buildFromRow(datasetRow, dbType);
8995
return generateInsertListFor(sqlQuery.toString());
9096
}
9197

@@ -94,7 +100,7 @@ public List<String> generateInsertListFor(String... sqlQueries) {
94100
.map(SqlQuery::new)
95101
.collect(toList());
96102
List<DatasetRow> datasetRows = datasetRowsGenerator.generateDatasetRowsFor(sqlQueryObjects);
97-
return InsertStatementsGenerator.INSTANCE.generateInsertStatementsFor(datasetRows);
103+
return insertStatementGenerator.generateInsertStatementsFor(datasetRows);
98104
}
99105

100106
}

0 commit comments

Comments
 (0)