diff --git a/pom.xml b/pom.xml
index 2728584..935abdf 100755
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.erudika
para-dao-sql
- 1.27.1-SNAPSHOT
+ 1.28.1-SNAPSHOT
jar
Para SQL DAO
@@ -69,7 +69,7 @@
com.erudika
para-core
- 1.28.0
+ 1.28.1
provided
diff --git a/src/main/java/com/erudika/para/persistence/SqlUtils.java b/src/main/java/com/erudika/para/persistence/SqlUtils.java
index 1ae8068..782c195 100755
--- a/src/main/java/com/erudika/para/persistence/SqlUtils.java
+++ b/src/main/java/com/erudika/para/persistence/SqlUtils.java
@@ -32,6 +32,7 @@
import org.slf4j.LoggerFactory;
import java.sql.Connection;
+import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -52,8 +53,25 @@
public final class SqlUtils {
private static final Logger logger = LoggerFactory.getLogger(SqlUtils.class);
+ private static boolean useMySqlSyntax = false;
private static HikariDataSource hikariDataSource;
+ private static final String JSON_FIELD_NAME = "json";
+ private static final String SQL_SCHEMA = Utils.formatMessage(
+ "{0} NVARCHAR(64) PRIMARY KEY NOT NULL," +
+ "{1} NVARCHAR(64) NOT NULL," +
+ "{2} NVARCHAR(64) DEFAULT NULL," +
+ "{3} TIMESTAMP NOT NULL," +
+ "{4} TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP," +
+ "{5} LONGTEXT NOT NULL",
+ Config._ID,
+ Config._TYPE,
+ Config._CREATORID,
+ Config._TIMESTAMP,
+ Config._UPDATED,
+ JSON_FIELD_NAME
+ );
+
private SqlUtils() { }
/**
@@ -65,10 +83,40 @@ static Connection getConnection() throws SQLException {
return hikariDataSource.getConnection();
}
+ String sqlUrl = Config.getConfigParam("sql.url", null);
+ String sqlDriver = Config.getConfigParam("sql.driver", null);
+ String sqlUser = Config.getConfigParam("sql.user", "user");
+ String sqlPassword = Config.getConfigParam("sql.password", "secret");
+
+ if (StringUtils.isBlank(sqlUrl)) {
+ logger.error("Missing required configuration parameter \"para.sql.url\" for the SqlDAO");
+ }
+ if (Config.getConfigParam("sql.driver", null) == null) {
+ logger.error("Missing requiredconfiguration parameter \"para.sql.driver\" for the SqlDAO");
+ }
+
+ // verify the SQL driver can be loaded from the classpath
+ try {
+ Class.forName(sqlDriver);
+ useMySqlSyntax = sqlDriver.contains("mysql");
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Error loading SQL driver \"{" + sqlDriver + "}\", class not found.");
+ }
+
+ // verify a connection can be made to the SQL server
+ try {
+ Connection conn = DriverManager.getConnection("jdbc:" + sqlUrl, sqlUser, sqlPassword);
+ conn.close();
+ } catch (SQLException e) {
+ throw new IllegalStateException("Failed to connect to SQL database: " + e.getMessage());
+ }
+
+ // connection and driver are valid, so establish a connection pool
HikariConfig hikariConfig = new HikariConfig();
- hikariConfig.setJdbcUrl("jdbc:" + Config.getConfigParam("sql.url", null));
- hikariConfig.setUsername(Config.getConfigParam("sql.user", "user"));
- hikariConfig.setPassword(Config.getConfigParam("sql.password", "secret"));
+ hikariConfig.setJdbcUrl("jdbc:" + sqlUrl);
+ hikariConfig.setUsername(sqlUser);
+ hikariConfig.setPassword(sqlPassword);
+ hikariConfig.setDriverClassName(sqlDriver);
/*
hikariConfig.addDataSourceProperty("cachePrepStmts", Config.getConfigBoolean("sql.cachePrepStmts"), true);
hikariConfig.addDataSourceProperty("prepStmtCacheSize", Config.getConfigInt("sql.prepStmtCacheSize"), 500);
@@ -120,7 +168,7 @@ public static boolean existsTable(String appid) {
return name != null;
}
} catch (Exception e) {
- logger.error("Failed to check if table exists for appid '{}'", appid, logSqlError(e));
+ logger.error("Failed to check if table exists for appid '{}'{}", appid, logSqlError(e));
}
return false;
}
@@ -137,14 +185,12 @@ public static boolean createTable(String appid) {
try (Connection connection = getConnection()) {
String tableName = getTableNameForAppid(appid);
Statement statement = connection.createStatement();
- String sql = Utils.formatMessage("CREATE TABLE IF NOT EXISTS {0} ({1} NVARCHAR PRIMARY KEY,"
- + "{2} NVARCHAR,{3} NVARCHAR,{4} TIMESTAMP,{5} TIMESTAMP,json NVARCHAR(MAX))",
- tableName, Config._ID, Config._TYPE, Config._CREATORID, Config._TIMESTAMP, Config._UPDATED);
+ String sql = Utils.formatMessage("CREATE TABLE IF NOT EXISTS {0} ({1})", tableName, SQL_SCHEMA);
statement.execute(sql);
logger.info("Created SQL database table named '{}'.", tableName);
return true;
} catch (Exception e) {
- logger.error("Failed to create a new table for appid '{}' in the SQL database", appid, logSqlError(e));
+ logger.error("Failed to create a new table for appid '{}' in the SQL database{}", appid, logSqlError(e));
}
return false;
}
@@ -164,7 +210,7 @@ public static boolean deleteTable(String appid) {
s.execute("DROP TABLE IF EXISTS " + tableName);
logger.info("Deleted table named '{}' from the SQL database.", tableName);
} catch (Exception e) {
- logger.error("Failed to delete the table for appid '{}' in the SQL database", appid, logSqlError(e));
+ logger.error("Failed to delete the table for appid '{}' in the SQL database{}", appid, logSqlError(e));
}
return true;
}
@@ -197,10 +243,10 @@ protected static
Map readRows(String appid, Li
}
try (Connection connection = getConnection()) {
Map results = new LinkedHashMap<>();
- String table = getTableNameForAppid(appid);
+ String tableName = getTableNameForAppid(appid);
PreparedStatement p = connection.prepareStatement(
Utils.formatMessage("SELECT json FROM {0} WHERE {1} IN ({2})",
- table, Config._ID, StringUtils.repeat("?", ",", ids.size())));
+ tableName, Config._ID, StringUtils.repeat("?", ",", ids.size())));
for (int i = 0; i < ids.size(); i++) {
p.setString(i + 1, ids.get(i));
results.put(ids.get(i), null);
@@ -214,7 +260,7 @@ protected static Map readRows(String appid, Li
}
return results;
} catch (Exception e) {
- logger.error("Failed to read rows for appid '{}' in the SQL database.", appid, logSqlError(e));
+ logger.error("Failed to read rows for appid '{}' in the SQL database{}", appid, logSqlError(e));
}
return Collections.emptyMap();
}
@@ -231,7 +277,19 @@ protected static void createRows(String appid, List
ob
}
try (Connection connection = getConnection()) {
String tableName = getTableNameForAppid(appid);
- PreparedStatement p = connection.prepareStatement("MERGE INTO " + tableName + " VALUES (?,?,?,?,?,?)");
+ String sql;
+ if (useMySqlSyntax) {
+ sql = Utils.formatMessage("INSERT INTO {0} VALUES (?,?,?,?,?,?) " +
+ "ON DUPLICATE KEY UPDATE {1}=?,{2}=?,{3}=?,{4}=?",
+ tableName,
+ Config._TYPE,
+ Config._CREATORID,
+ Config._UPDATED,
+ JSON_FIELD_NAME);
+ } else {
+ sql = "MERGE INTO " + tableName + " VALUES (?,?,?,?,?,?)";
+ }
+ PreparedStatement p = connection.prepareStatement(sql);
for (P object : objects) {
if (StringUtils.isBlank(object.getId())) {
@@ -246,18 +304,32 @@ protected static
void createRows(String appid, List
ob
p.setString(2, object.getType());
p.setString(3, object.getCreatorid());
p.setTimestamp(4, new Timestamp(object.getTimestamp()));
- if (object.getUpdated() == null) {
+ final Timestamp updateTimetamp = object.getUpdated() == null ? null : new Timestamp(object.getUpdated());
+ if (updateTimetamp == null) {
p.setNull(5, Types.TIMESTAMP);
} else {
- p.setTimestamp(5, new Timestamp(object.getUpdated()));
+ p.setTimestamp(5, updateTimetamp);
+ }
+ final String objectJson = ParaObjectUtils.getJsonWriterNoIdent().
+ writeValueAsString(ParaObjectUtils.getAnnotatedFields(object, false));
+ p.setString(6, objectJson);
+
+ if (useMySqlSyntax) {
+ p.setString(7, object.getType());
+ p.setString(8, object.getCreatorid());
+ if (updateTimetamp == null) {
+ p.setNull(9, Types.TIMESTAMP);
+ } else {
+ p.setTimestamp(9, updateTimetamp);
+ }
+ p.setString(10, objectJson);
}
- p.setString(6, ParaObjectUtils.getJsonWriterNoIdent().
- writeValueAsString(ParaObjectUtils.getAnnotatedFields(object, false)));
p.addBatch();
}
+ logger.info("statement:" + p.toString());
p.executeBatch();
} catch (Exception e) {
- logger.error("Failed to create rows for appid '{}' in the SQL database.", appid, logSqlError(e), e);
+ logger.error("Failed to create rows for appid '{}' in the SQL database{}", appid, logSqlError(e), e);
}
}
@@ -310,7 +382,7 @@ protected static
void updateRows(String appid, List
ob
}
p.executeBatch();
} catch (Exception e) {
- logger.error("Failed to update rows for appid '{}' in the SQL database.", appid, logSqlError(e));
+ logger.error("Failed to update rows for appid '{}' in the SQL database{}", appid, logSqlError(e));
}
}
@@ -334,7 +406,7 @@ protected static
void deleteRows(String appid, List
ob
}
p.execute();
} catch (Exception e) {
- logger.error("Failed to delete rows for appid '{}' in the SQL database.", appid, logSqlError(e));
+ logger.error("Failed to delete rows for appid '{}' in the SQL database{}", appid, logSqlError(e));
}
}
@@ -378,14 +450,14 @@ protected static
List
scanRows(String appid, Pager pag
}
return results;
} catch (Exception e) {
- logger.error("Failed to scan a page for appid '{}' from the SQL database.", appid, logSqlError(e));
+ logger.error("Failed to scan a page for appid '{}' from the SQL database{}", appid, logSqlError(e));
}
return Collections.emptyList();
}
private static String logSqlError(Exception e) {
if (e == null || !SQLException.class.isAssignableFrom(e.getClass())) {
- return null;
+ return "";
}
SQLException sqlException = (SQLException) e;
return " (Error Code: " + sqlException.getErrorCode() + ", SQLState: " + sqlException.getSQLState() + ")";