[jdbc] Add support for case sensitive table names reflecting item names 1:1 (#13544)
* Do not append number when using real item names * Extract getTableName to separate class * Add initial test coverage * Extract migration logic to separate class * Support migration from real names back to numbered * Simplify zero-padding * Fix NullPointerException * Fix MySQL compatibility when CLIENT_MULTI_STATEMENTS option is not set * Add option for case sensitive table names * Add real name with suffix mode for backwards compatibility * Remove real name in lower case without suffix mode * Map directly from item name to table name * Fix ambiguous table name scenario * Add additional testcase * Add migration path for changed table prefix * Drop items table when using direct mapping * Add configuration note * Fix table alignment * Extend description as more migration paths are now supported * Do not stop halfway through a migration * For clarity, do not use abbreviation for operating system Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
parent
f880ca91f9
commit
70abb5d1f6
|
@ -38,38 +38,39 @@ The following databases are currently supported and tested:
|
||||||
|
|
||||||
This service can be configured in the file `services/jdbc.cfg`.
|
This service can be configured in the file `services/jdbc.cfg`.
|
||||||
|
|
||||||
| Property | Default | Required | Description |
|
| Property | Default | Required | Description |
|
||||||
| ------------------------- | ------------------------------------------------------------ | :-------: | ------------------------------------------------------------ |
|
| --------------------------- | ------------------------------------------------------------ | :-------: | ------------------------------------------------------------ |
|
||||||
| url | | Yes | JDBC URL to establish a connection to your database. Examples:<br/><br/>`jdbc:derby:./testDerby;create=true`<br/>`jdbc:h2:./testH2`<br/>`jdbc:hsqldb:./testHsqlDb`<br/>`jdbc:mariadb://192.168.0.1:3306/testMariadb`<br/>`jdbc:mysql://192.168.0.1:3306/testMysql?serverTimezone=UTC`<br/>`jdbc:postgresql://192.168.0.1:5432/testPostgresql`<br/>`jdbc:timescaledb://192.168.0.1:5432/testPostgresql`<br/>`jdbc:sqlite:./testSqlite.db`.<br/><br/>If no database is available it will be created; for example the url `jdbc:h2:./testH2` creates a new H2 database in openHAB folder. Example to create your own MySQL database directly:<br/><br/>`CREATE DATABASE 'yourDB' CHARACTER SET utf8 COLLATE utf8_general_ci;` |
|
| url | | Yes | JDBC URL to establish a connection to your database. Examples:<br/><br/>`jdbc:derby:./testDerby;create=true`<br/>`jdbc:h2:./testH2`<br/>`jdbc:hsqldb:./testHsqlDb`<br/>`jdbc:mariadb://192.168.0.1:3306/testMariadb`<br/>`jdbc:mysql://192.168.0.1:3306/testMysql?serverTimezone=UTC`<br/>`jdbc:postgresql://192.168.0.1:5432/testPostgresql`<br/>`jdbc:timescaledb://192.168.0.1:5432/testPostgresql`<br/>`jdbc:sqlite:./testSqlite.db`.<br/><br/>If no database is available it will be created; for example the url `jdbc:h2:./testH2` creates a new H2 database in openHAB folder. Example to create your own MySQL database directly:<br/><br/>`CREATE DATABASE 'yourDB' CHARACTER SET utf8 COLLATE utf8_general_ci;` |
|
||||||
| user | | if needed | database user name |
|
| user | | if needed | database user name |
|
||||||
| password | | if needed | database user password |
|
| password | | if needed | database user password |
|
||||||
| errReconnectThreshold | 0 | No | when the service is deactivated (0 means ignore) |
|
| errReconnectThreshold | 0 | No | when the service is deactivated (0 means ignore) |
|
||||||
| sqltype.CALL | `VARCHAR(200)` | No | All `sqlType` options allow you to change the SQL data type used to store values for different openHAB item states. See the following links for further information: [mybatis](https://mybatis.github.io/mybatis-3/apidocs/reference/org/apache/ibatis/type/JdbcType.html) [H2](https://www.h2database.com/html/datatypes.html) [PostgresSQL](https://www.postgresql.org/docs/9.3/static/datatype.html) |
|
| sqltype.CALL | `VARCHAR(200)` | No | All `sqlType` options allow you to change the SQL data type used to store values for different openHAB item states. See the following links for further information: [mybatis](https://mybatis.github.io/mybatis-3/apidocs/reference/org/apache/ibatis/type/JdbcType.html) [H2](https://www.h2database.com/html/datatypes.html) [PostgresSQL](https://www.postgresql.org/docs/9.3/static/datatype.html) |
|
||||||
| sqltype.COLOR | `VARCHAR(70)` | No | see above |
|
| sqltype.COLOR | `VARCHAR(70)` | No | see above |
|
||||||
| sqltype.CONTACT | `VARCHAR(6)` | No | see above |
|
| sqltype.CONTACT | `VARCHAR(6)` | No | see above |
|
||||||
| sqltype.DATETIME | `DATETIME` | No | see above |
|
| sqltype.DATETIME | `DATETIME` | No | see above |
|
||||||
| sqltype.DIMMER | `TINYINT` | No | see above |
|
| sqltype.DIMMER | `TINYINT` | No | see above |
|
||||||
| sqltype.IMAGE | `VARCHAR(65500)` | No | see above |
|
| sqltype.IMAGE | `VARCHAR(65500)` | No | see above |
|
||||||
| sqltype.LOCATION | `VARCHAR(50)` | No | see above |
|
| sqltype.LOCATION | `VARCHAR(50)` | No | see above |
|
||||||
| sqltype.NUMBER | `DOUBLE` | No | see above |
|
| sqltype.NUMBER | `DOUBLE` | No | see above |
|
||||||
| sqltype.PLAYER | `VARCHAR(20)` | No | see above |
|
| sqltype.PLAYER | `VARCHAR(20)` | No | see above |
|
||||||
| sqltype.ROLLERSHUTTER | `TINYINT` | No | see above |
|
| sqltype.ROLLERSHUTTER | `TINYINT` | No | see above |
|
||||||
| sqltype.STRING | `VARCHAR(65500)` | No | see above |
|
| sqltype.STRING | `VARCHAR(65500)` | No | see above |
|
||||||
| sqltype.SWITCH | `VARCHAR(6)` | No | see above |
|
| sqltype.SWITCH | `VARCHAR(6)` | No | see above |
|
||||||
| sqltype.tablePrimaryKey | `TIMESTAMP` | No | type of `time` column for newly created item tables |
|
| sqltype.tablePrimaryKey | `TIMESTAMP` | No | type of `time` column for newly created item tables |
|
||||||
| sqltype.tablePrimaryValue | `NOW()` | No | value of `time` column for newly inserted rows |
|
| sqltype.tablePrimaryValue | `NOW()` | No | value of `time` column for newly inserted rows |
|
||||||
| numberDecimalcount | 3 | No | for Itemtype "Number" default decimal digit count |
|
| numberDecimalcount | 3 | No | for Itemtype "Number" default decimal digit count |
|
||||||
| tableNamePrefix | `item` | No | table name prefix. For Migration from MySQL Persistence, set to `Item`. |
|
| tableNamePrefix | `item` | No | table name prefix. For Migration from MySQL Persistence, set to `Item`. |
|
||||||
| tableUseRealItemNames | `false` | No | table name prefix generation. When set to `true`, real item names are used for table names and `tableNamePrefix` is ignored. When set to `false`, the `tableNamePrefix` is used to generate table names with sequential numbers. |
|
| tableUseRealItemNames | `false` | No | table name prefix generation. When set to `true`, real item names are used for table names and `tableNamePrefix` is ignored. When set to `false`, the `tableNamePrefix` is used to generate table names with sequential numbers. |
|
||||||
| tableIdDigitCount | 4 | No | when `tableUseRealItemNames` is `false` and thus table names are generated sequentially, this controls how many zero-padded digits are used in the table name. With the default of 4, the first table name will end with `0001`. For migration from the MySQL persistence service, set this to 0. |
|
| tableCaseSensitiveItemNames | `false` | No | table name case when `tableUseRealItemNames` is `true`. When set to `true`, item name case is preserved in table names and no suffix is used. When set to `false`, table names are lower cased and a numeric suffix is added. Please read [this](#case-sensitive-item-names) before enabling. |
|
||||||
| rebuildTableNames | false | No | rename existing tables using `tableUseRealItemNames` and `tableIdDigitCount`. USE WITH CARE! Deactivate after Renaming is done! |
|
| tableIdDigitCount | 4 | No | when `tableUseRealItemNames` is `false` and thus table names are generated sequentially, this controls how many zero-padded digits are used in the table name. With the default of 4, the first table name will end with `0001`. For migration from the MySQL persistence service, set this to 0. |
|
||||||
| jdbc.maximumPoolSize | configured per database in package `org.openhab.persistence.jdbc.db.*` | No | Some embedded databases can handle only one connection. See [this link](https://github.com/brettwooldridge/HikariCP/issues/256) for more information |
|
| rebuildTableNames | false | No | rename existing tables using `tableUseRealItemNames` and `tableIdDigitCount`. USE WITH CARE! Deactivate after Renaming is done! |
|
||||||
| jdbc.minimumIdle | see above | No | see above |
|
| jdbc.maximumPoolSize | configured per database in package `org.openhab.persistence.jdbc.db.*` | No | Some embedded databases can handle only one connection. See [this link](https://github.com/brettwooldridge/HikariCP/issues/256) for more information |
|
||||||
| enableLogTime | `false` | No | timekeeping |
|
| jdbc.minimumIdle | see above | No | see above |
|
||||||
|
| enableLogTime | `false` | No | timekeeping |
|
||||||
|
|
||||||
All item- and event-related configuration is done in the file `persistence/jdbc.persist`.
|
All item- and event-related configuration is done in the file `persistence/jdbc.persist`.
|
||||||
|
|
||||||
To configure this service as the default persistence service for openHAB 2, add or change the line
|
To configure this service as the default persistence service for openHAB, add or change the line
|
||||||
|
|
||||||
```
|
```
|
||||||
org.openhab.core.persistence:default=jdbc
|
org.openhab.core.persistence:default=jdbc
|
||||||
|
@ -85,6 +86,13 @@ services/jdbc.cfg
|
||||||
url=jdbc:postgresql://192.168.0.1:5432/testPostgresql
|
url=jdbc:postgresql://192.168.0.1:5432/testPostgresql
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Case Sensitive Item Names
|
||||||
|
|
||||||
|
To avoid numbered suffixes entirely, `tableUseRealItemNames` and `tableCaseSensitiveItemNames` must both be enabled.
|
||||||
|
With this configuration, tables are named exactly like their corresponding items.
|
||||||
|
In order for this to work correctly, the underlying operating system, database server and configuration must support case sensitive table names.
|
||||||
|
For MySQL, see [MySQL: Identifier Case Sensitivity](https://dev.mysql.com/doc/refman/8.0/en/identifier-case-sensitivity.html) for more information.
|
||||||
|
|
||||||
### Migration from MySQL to JDBC Persistence Services
|
### Migration from MySQL to JDBC Persistence Services
|
||||||
|
|
||||||
The JDBC Persistence service can act as a replacement for the MySQL Persistence service.
|
The JDBC Persistence service can act as a replacement for the MySQL Persistence service.
|
||||||
|
|
|
@ -84,8 +84,9 @@ public class JdbcBaseDAO {
|
||||||
protected String sqlIfTableExists = "SHOW TABLES LIKE '#searchTable#'";
|
protected String sqlIfTableExists = "SHOW TABLES LIKE '#searchTable#'";
|
||||||
protected String sqlCreateNewEntryInItemsTable = "INSERT INTO #itemsManageTable# (ItemName) VALUES ('#itemname#')";
|
protected String sqlCreateNewEntryInItemsTable = "INSERT INTO #itemsManageTable# (ItemName) VALUES ('#itemname#')";
|
||||||
protected String sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (ItemId INT NOT NULL AUTO_INCREMENT,#colname# #coltype# NOT NULL,PRIMARY KEY (ItemId))";
|
protected String sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (ItemId INT NOT NULL AUTO_INCREMENT,#colname# #coltype# NOT NULL,PRIMARY KEY (ItemId))";
|
||||||
|
protected String sqlDropItemsTableIfExists = "DROP TABLE IF EXISTS #itemsManageTable#";
|
||||||
protected String sqlDeleteItemsEntry = "DELETE FROM items WHERE ItemName=#itemname#";
|
protected String sqlDeleteItemsEntry = "DELETE FROM items WHERE ItemName=#itemname#";
|
||||||
protected String sqlGetItemIDTableNames = "SELECT itemid, itemname FROM #itemsManageTable#";
|
protected String sqlGetItemIDTableNames = "SELECT ItemId, ItemName FROM #itemsManageTable#";
|
||||||
protected String sqlGetItemTables = "SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema='#jdbcUriDatabaseName#' AND NOT table_name='#itemsManageTable#'";
|
protected String sqlGetItemTables = "SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema='#jdbcUriDatabaseName#' AND NOT table_name='#itemsManageTable#'";
|
||||||
protected String sqlCreateItemTable = "CREATE TABLE IF NOT EXISTS #tableName# (time #tablePrimaryKey# NOT NULL, value #dbType#, PRIMARY KEY(time))";
|
protected String sqlCreateItemTable = "CREATE TABLE IF NOT EXISTS #tableName# (time #tablePrimaryKey# NOT NULL, value #dbType#, PRIMARY KEY(time))";
|
||||||
protected String sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, ? ) ON DUPLICATE KEY UPDATE VALUE= ?";
|
protected String sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, ? ) ON DUPLICATE KEY UPDATE VALUE= ?";
|
||||||
|
@ -266,7 +267,7 @@ public class JdbcBaseDAO {
|
||||||
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
||||||
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
||||||
new String[] { "#itemsManageTable#", "#itemname#" },
|
new String[] { "#itemsManageTable#", "#itemname#" },
|
||||||
new String[] { vo.getItemsManageTable(), vo.getItemname() });
|
new String[] { vo.getItemsManageTable(), vo.getItemName() });
|
||||||
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
||||||
return Yank.insert(sql, null);
|
return Yank.insert(sql, null);
|
||||||
}
|
}
|
||||||
|
@ -280,9 +281,17 @@ public class JdbcBaseDAO {
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ItemsVO doDropItemsTableIfExists(ItemsVO vo) {
|
||||||
|
String sql = StringUtilsExt.replaceArrayMerge(sqlDropItemsTableIfExists, new String[] { "#itemsManageTable#" },
|
||||||
|
new String[] { vo.getItemsManageTable() });
|
||||||
|
logger.debug("JDBC::doDropItemsTableIfExists sql={}", sql);
|
||||||
|
Yank.execute(sql, null);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
public void doDeleteItemsEntry(ItemsVO vo) {
|
public void doDeleteItemsEntry(ItemsVO vo) {
|
||||||
String sql = StringUtilsExt.replaceArrayMerge(sqlDeleteItemsEntry, new String[] { "#itemname#" },
|
String sql = StringUtilsExt.replaceArrayMerge(sqlDeleteItemsEntry, new String[] { "#itemname#" },
|
||||||
new String[] { vo.getItemname() });
|
new String[] { vo.getItemName() });
|
||||||
logger.debug("JDBC::doDeleteItemsEntry sql={}", sql);
|
logger.debug("JDBC::doDeleteItemsEntry sql={}", sql);
|
||||||
Yank.execute(sql, null);
|
Yank.execute(sql, null);
|
||||||
}
|
}
|
||||||
|
@ -306,8 +315,9 @@ public class JdbcBaseDAO {
|
||||||
* ITEM DAOs *
|
* ITEM DAOs *
|
||||||
*************/
|
*************/
|
||||||
public void doUpdateItemTableNames(List<ItemVO> vol) {
|
public void doUpdateItemTableNames(List<ItemVO> vol) {
|
||||||
if (!vol.isEmpty()) {
|
logger.debug("JDBC::doUpdateItemTableNames vol.size = {}", vol.size());
|
||||||
String sql = updateItemTableNamesProvider(vol);
|
for (ItemVO itemTable : vol) {
|
||||||
|
String sql = updateItemTableNamesProvider(itemTable);
|
||||||
Yank.execute(sql, null);
|
Yank.execute(sql, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,13 +426,8 @@ public class JdbcBaseDAO {
|
||||||
return filterString;
|
return filterString;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String updateItemTableNamesProvider(List<ItemVO> namesList) {
|
private String updateItemTableNamesProvider(ItemVO itemTable) {
|
||||||
logger.debug("JDBC::updateItemTableNamesProvider namesList.size = {}", namesList.size());
|
String queryString = "ALTER TABLE " + itemTable.getTableName() + " RENAME TO " + itemTable.getNewTableName();
|
||||||
String queryString = "";
|
|
||||||
for (int i = 0; i < namesList.size(); i++) {
|
|
||||||
ItemVO it = namesList.get(i);
|
|
||||||
queryString += "ALTER TABLE " + it.getTableName() + " RENAME TO " + it.getNewTableName() + ";";
|
|
||||||
}
|
|
||||||
logger.debug("JDBC::query queryString = {}", queryString);
|
logger.debug("JDBC::query queryString = {}", queryString);
|
||||||
return queryString;
|
return queryString;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ public class JdbcDerbyDAO extends JdbcBaseDAO {
|
||||||
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
||||||
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
||||||
new String[] { "#itemsManageTable#", "#itemname#" },
|
new String[] { "#itemsManageTable#", "#itemname#" },
|
||||||
new String[] { vo.getItemsManageTable().toUpperCase(), vo.getItemname() });
|
new String[] { vo.getItemsManageTable().toUpperCase(), vo.getItemName() });
|
||||||
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
||||||
return Yank.insert(sql, null);
|
return Yank.insert(sql, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ public class JdbcHsqldbDAO extends JdbcBaseDAO {
|
||||||
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
||||||
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
||||||
new String[] { "#itemsManageTable#", "#itemname#" },
|
new String[] { "#itemsManageTable#", "#itemname#" },
|
||||||
new String[] { vo.getItemsManageTable(), vo.getItemname() });
|
new String[] { vo.getItemsManageTable(), vo.getItemName() });
|
||||||
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
||||||
return Yank.insert(sql, null);
|
return Yank.insert(sql, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO {
|
||||||
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
|
||||||
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
|
||||||
new String[] { "#itemsManageTable#", "#itemname#" },
|
new String[] { "#itemsManageTable#", "#itemname#" },
|
||||||
new String[] { vo.getItemsManageTable(), vo.getItemname() });
|
new String[] { vo.getItemsManageTable(), vo.getItemName() });
|
||||||
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
|
||||||
return Yank.insert(sql, null);
|
return Yank.insert(sql, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ public class ItemsVO implements Serializable {
|
||||||
private String coltype = "VARCHAR(500)";
|
private String coltype = "VARCHAR(500)";
|
||||||
private String colname = "itemname";
|
private String colname = "itemname";
|
||||||
private String itemsManageTable = "items";
|
private String itemsManageTable = "items";
|
||||||
private int itemid;
|
private int itemId;
|
||||||
private String itemname;
|
private String itemName;
|
||||||
private String table_name;
|
private String tableName;
|
||||||
private String jdbcUriDatabaseName;
|
private String jdbcUriDatabaseName;
|
||||||
|
|
||||||
public String getColtype() {
|
public String getColtype() {
|
||||||
|
@ -57,28 +57,28 @@ public class ItemsVO implements Serializable {
|
||||||
this.itemsManageTable = itemsManageTable.replaceAll(STR_FILTER, "");
|
this.itemsManageTable = itemsManageTable.replaceAll(STR_FILTER, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getItemid() {
|
public int getItemId() {
|
||||||
return itemid;
|
return itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setItemid(int itemid) {
|
public void setItemId(int itemId) {
|
||||||
this.itemid = itemid;
|
this.itemId = itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getItemname() {
|
public String getItemName() {
|
||||||
return itemname;
|
return itemName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setItemname(String itemname) {
|
public void setItemName(String itemName) {
|
||||||
this.itemname = itemname;
|
this.itemName = itemName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTable_name() {
|
public String getTableName() {
|
||||||
return table_name;
|
return tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTable_name(String table_name) {
|
public void setTableName(String tableName) {
|
||||||
this.table_name = table_name;
|
this.tableName = tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getJdbcUriDatabaseName() {
|
public String getJdbcUriDatabaseName() {
|
||||||
|
@ -98,8 +98,8 @@ public class ItemsVO implements Serializable {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + ((itemname == null) ? 0 : itemname.hashCode());
|
result = prime * result + ((itemName == null) ? 0 : itemName.hashCode());
|
||||||
result = prime * result + (itemid ^ (itemid >>> 32));
|
result = prime * result + (itemId ^ (itemId >>> 32));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,14 +120,14 @@ public class ItemsVO implements Serializable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ItemsVO other = (ItemsVO) obj;
|
ItemsVO other = (ItemsVO) obj;
|
||||||
if (itemname == null) {
|
if (itemName == null) {
|
||||||
if (other.itemname != null) {
|
if (other.itemName != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!itemname.equals(other.itemname)) {
|
} else if (!itemName.equals(other.itemName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return itemid == other.itemid;
|
return itemId == other.itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -140,11 +140,11 @@ public class ItemsVO implements Serializable {
|
||||||
builder.append(", itemsManageTable=");
|
builder.append(", itemsManageTable=");
|
||||||
builder.append(itemsManageTable);
|
builder.append(itemsManageTable);
|
||||||
builder.append(", itemid=");
|
builder.append(", itemid=");
|
||||||
builder.append(itemid);
|
builder.append(itemId);
|
||||||
builder.append(", itemname=");
|
builder.append(", itemname=");
|
||||||
builder.append(itemname);
|
builder.append(itemName);
|
||||||
builder.append(", table_name=");
|
builder.append(", table_name=");
|
||||||
builder.append(table_name);
|
builder.append(tableName);
|
||||||
builder.append("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ public class JdbcConfiguration {
|
||||||
// private String password;
|
// private String password;
|
||||||
private int numberDecimalcount = 3;
|
private int numberDecimalcount = 3;
|
||||||
private boolean tableUseRealItemNames = false;
|
private boolean tableUseRealItemNames = false;
|
||||||
|
private boolean tableCaseSensitiveItemNames = false;
|
||||||
private String tableNamePrefix = "item";
|
private String tableNamePrefix = "item";
|
||||||
private int tableIdDigitCount = 4;
|
private int tableIdDigitCount = 4;
|
||||||
private boolean rebuildTableNames = false;
|
private boolean rebuildTableNames = false;
|
||||||
|
@ -163,6 +164,12 @@ public class JdbcConfiguration {
|
||||||
logger.debug("JDBC::updateConfig: tableUseRealItemNames={}", tableUseRealItemNames);
|
logger.debug("JDBC::updateConfig: tableUseRealItemNames={}", tableUseRealItemNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String lc = (String) configuration.get("tableCaseSensitiveItemNames");
|
||||||
|
if (lc != null && !lc.isBlank()) {
|
||||||
|
tableCaseSensitiveItemNames = Boolean.parseBoolean(lc);
|
||||||
|
logger.debug("JDBC::updateConfig: tableCaseSensitiveItemNames={}", tableCaseSensitiveItemNames);
|
||||||
|
}
|
||||||
|
|
||||||
String td = (String) configuration.get("tableIdDigitCount");
|
String td = (String) configuration.get("tableIdDigitCount");
|
||||||
if (td != null && !td.isBlank() && isNumericPattern.matcher(td).matches()) {
|
if (td != null && !td.isBlank() && isNumericPattern.matcher(td).matches()) {
|
||||||
tableIdDigitCount = Integer.parseInt(td);
|
tableIdDigitCount = Integer.parseInt(td);
|
||||||
|
@ -363,6 +370,19 @@ public class JdbcConfiguration {
|
||||||
return tableUseRealItemNames;
|
return tableUseRealItemNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getTableCaseSensitiveItemNames() {
|
||||||
|
return tableCaseSensitiveItemNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if real item names (without number suffix) is enabled.
|
||||||
|
*
|
||||||
|
* @return true if both tableUseRealItemNames and tableCaseSensitiveItemNames are enabled.
|
||||||
|
*/
|
||||||
|
public boolean getTableUseRealCaseSensitiveItemNames() {
|
||||||
|
return tableUseRealItemNames && tableCaseSensitiveItemNames;
|
||||||
|
}
|
||||||
|
|
||||||
public int getTableIdDigitCount() {
|
public int getTableIdDigitCount() {
|
||||||
return tableIdDigitCount;
|
return tableIdDigitCount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -53,10 +54,10 @@ public class JdbcMapper {
|
||||||
protected int errCnt;
|
protected int errCnt;
|
||||||
protected boolean initialized = false;
|
protected boolean initialized = false;
|
||||||
protected @NonNullByDefault({}) JdbcConfiguration conf;
|
protected @NonNullByDefault({}) JdbcConfiguration conf;
|
||||||
protected final Map<String, String> sqlTables = new HashMap<>();
|
protected final Map<String, String> itemNameToTableNameMap = new HashMap<>();
|
||||||
|
protected @NonNullByDefault({}) NamingStrategy namingStrategy;
|
||||||
private long afterAccessMin = 10000;
|
private long afterAccessMin = 10000;
|
||||||
private long afterAccessMax = 0;
|
private long afterAccessMax = 0;
|
||||||
private static final String ITEM_NAME_PATTERN = "[^a-zA-Z_0-9\\-]";
|
|
||||||
|
|
||||||
public JdbcMapper(TimeZoneProvider timeZoneProvider) {
|
public JdbcMapper(TimeZoneProvider timeZoneProvider) {
|
||||||
this.timeZoneProvider = timeZoneProvider;
|
this.timeZoneProvider = timeZoneProvider;
|
||||||
|
@ -97,11 +98,19 @@ public class JdbcMapper {
|
||||||
return res != null ? res : "";
|
return res != null ? res : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean ifItemsTableExists() {
|
||||||
|
logger.debug("JDBC::ifItemsTableExists");
|
||||||
|
long timerStart = System.currentTimeMillis();
|
||||||
|
boolean res = conf.getDBDAO().doIfTableExists(new ItemsVO());
|
||||||
|
logTime("doIfTableExists", timerStart, System.currentTimeMillis());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
public ItemsVO createNewEntryInItemsTable(ItemsVO vo) {
|
public ItemsVO createNewEntryInItemsTable(ItemsVO vo) {
|
||||||
logger.debug("JDBC::createNewEntryInItemsTable");
|
logger.debug("JDBC::createNewEntryInItemsTable");
|
||||||
long timerStart = System.currentTimeMillis();
|
long timerStart = System.currentTimeMillis();
|
||||||
Long i = conf.getDBDAO().doCreateNewEntryInItemsTable(vo);
|
Long i = conf.getDBDAO().doCreateNewEntryInItemsTable(vo);
|
||||||
vo.setItemid(i.intValue());
|
vo.setItemId(i.intValue());
|
||||||
logTime("doCreateNewEntryInItemsTable", timerStart, System.currentTimeMillis());
|
logTime("doCreateNewEntryInItemsTable", timerStart, System.currentTimeMillis());
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +123,14 @@ public class JdbcMapper {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean dropItemsTableIfExists(ItemsVO vo) {
|
||||||
|
logger.debug("JDBC::dropItemsTableIfExists");
|
||||||
|
long timerStart = System.currentTimeMillis();
|
||||||
|
conf.getDBDAO().doDropItemsTableIfExists(vo);
|
||||||
|
logTime("doDropItemsTableIfExists", timerStart, System.currentTimeMillis());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public ItemsVO deleteItemsEntry(ItemsVO vo) {
|
public ItemsVO deleteItemsEntry(ItemsVO vo) {
|
||||||
logger.debug("JDBC::deleteItemsEntry");
|
logger.debug("JDBC::deleteItemsEntry");
|
||||||
long timerStart = System.currentTimeMillis();
|
long timerStart = System.currentTimeMillis();
|
||||||
|
@ -252,47 +269,66 @@ public class JdbcMapper {
|
||||||
* DATABASE TABLEHANDLING *
|
* DATABASE TABLEHANDLING *
|
||||||
**************************/
|
**************************/
|
||||||
protected void checkDBSchema() {
|
protected void checkDBSchema() {
|
||||||
// Create Items Table if does not exist
|
if (!conf.getTableUseRealCaseSensitiveItemNames()) {
|
||||||
createItemsTableIfNot(new ItemsVO());
|
createItemsTableIfNot(new ItemsVO());
|
||||||
|
}
|
||||||
if (conf.getRebuildTableNames()) {
|
if (conf.getRebuildTableNames()) {
|
||||||
formatTableNames();
|
formatTableNames();
|
||||||
|
|
||||||
|
if (conf.getTableUseRealCaseSensitiveItemNames()) {
|
||||||
|
dropItemsTableIfExists(new ItemsVO());
|
||||||
|
}
|
||||||
logger.info(
|
logger.info(
|
||||||
"JDBC::checkDBSchema: Rebuild complete, configure the 'rebuildTableNames' setting to 'false' to stop rebuilds on startup");
|
"JDBC::checkDBSchema: Rebuild complete, configure the 'rebuildTableNames' setting to 'false' to stop rebuilds on startup");
|
||||||
} else {
|
|
||||||
// Reset the error counter
|
// Reset the error counter
|
||||||
errCnt = 0;
|
errCnt = 0;
|
||||||
|
}
|
||||||
|
populateItemNameToTableNameMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateItemNameToTableNameMap() {
|
||||||
|
itemNameToTableNameMap.clear();
|
||||||
|
if (conf.getTableUseRealCaseSensitiveItemNames()) {
|
||||||
|
for (String itemName : getItemTables().stream().map(t -> t.getTableName()).collect(Collectors.toList())) {
|
||||||
|
itemNameToTableNameMap.put(itemName, itemName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (ItemsVO vo : getItemIDTableNames()) {
|
for (ItemsVO vo : getItemIDTableNames()) {
|
||||||
sqlTables.put(vo.getItemname(), getTableName(vo.getItemid(), vo.getItemname()));
|
itemNameToTableNameMap.put(vo.getItemName(),
|
||||||
|
namingStrategy.getTableName(vo.getItemId(), vo.getItemName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getTable(Item item) {
|
protected String getTable(Item item) {
|
||||||
int rowId = 0;
|
int itemId = 0;
|
||||||
ItemsVO isvo;
|
ItemsVO isvo;
|
||||||
ItemVO ivo;
|
ItemVO ivo;
|
||||||
|
|
||||||
String itemName = item.getName();
|
String itemName = item.getName();
|
||||||
String tableName = sqlTables.get(itemName);
|
String tableName = itemNameToTableNameMap.get(itemName);
|
||||||
|
|
||||||
// Table already exists - return the name
|
// Table already exists - return the name
|
||||||
if (tableName != null) {
|
if (!Objects.isNull(tableName)) {
|
||||||
return tableName;
|
return tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("JDBC::getTable: no table found for item '{}' in sqlTables", itemName);
|
logger.debug("JDBC::getTable: no table found for item '{}' in sqlTables", itemName);
|
||||||
|
|
||||||
// Create a new entry in items table
|
if (!conf.getTableUseRealCaseSensitiveItemNames()) {
|
||||||
isvo = new ItemsVO();
|
// Create a new entry in items table
|
||||||
isvo.setItemname(itemName);
|
isvo = new ItemsVO();
|
||||||
isvo = createNewEntryInItemsTable(isvo);
|
isvo.setItemName(itemName);
|
||||||
rowId = isvo.getItemid();
|
isvo = createNewEntryInItemsTable(isvo);
|
||||||
if (rowId == 0) {
|
itemId = isvo.getItemId();
|
||||||
logger.error("JDBC::getTable: Creating table for item '{}' failed.", itemName);
|
if (itemId == 0) {
|
||||||
|
logger.error("JDBC::getTable: Creating items entry for item '{}' failed.", itemName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the table name
|
// Create the table name
|
||||||
logger.debug("JDBC::getTable: getTableName with rowId={} itemName={}", rowId, itemName);
|
logger.debug("JDBC::getTable: getTableName with rowId={} itemName={}", itemId, itemName);
|
||||||
tableName = getTableName(rowId, itemName);
|
tableName = namingStrategy.getTableName(itemId, itemName);
|
||||||
|
|
||||||
// Create table for item
|
// Create table for item
|
||||||
String dataType = conf.getDBDAO().getDataType(item);
|
String dataType = conf.getDBDAO().getDataType(item);
|
||||||
|
@ -301,18 +337,8 @@ public class JdbcMapper {
|
||||||
ivo = createItemTable(ivo);
|
ivo = createItemTable(ivo);
|
||||||
logger.debug("JDBC::getTable: Table created for item '{}' with dataType {} in SQL database.", itemName,
|
logger.debug("JDBC::getTable: Table created for item '{}' with dataType {} in SQL database.", itemName,
|
||||||
dataType);
|
dataType);
|
||||||
sqlTables.put(itemName, tableName);
|
|
||||||
|
|
||||||
// Check if the new entry is in the table list
|
itemNameToTableNameMap.put(itemName, tableName);
|
||||||
// If it's not in the list, then there was an error and we need to do
|
|
||||||
// some tidying up
|
|
||||||
// The item needs to be removed from the index table to avoid duplicates
|
|
||||||
if (sqlTables.get(itemName) == null) {
|
|
||||||
logger.error("JDBC::getTable: Item '{}' was not added to the table - removing index", itemName);
|
|
||||||
isvo = new ItemsVO();
|
|
||||||
isvo.setItemname(itemName);
|
|
||||||
deleteItemsEntry(isvo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tableName;
|
return tableName;
|
||||||
}
|
}
|
||||||
|
@ -323,93 +349,57 @@ public class JdbcMapper {
|
||||||
initialized = false;
|
initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Integer, String> tableIds = new HashMap<>();
|
List<ItemsVO> itemIdTableNames = ifItemsTableExists() ? getItemIDTableNames() : new ArrayList<ItemsVO>();
|
||||||
|
List<String> itemTables = getItemTables().stream().map(t -> t.getTableName()).collect(Collectors.toList());
|
||||||
|
List<ItemVO> oldNewTableNames;
|
||||||
|
|
||||||
//
|
if (itemIdTableNames.isEmpty()) {
|
||||||
for (ItemsVO vo : getItemIDTableNames()) {
|
// Without mappings we can only migrate from direct item name to numeric mapping.
|
||||||
String t = getTableName(vo.getItemid(), vo.getItemname());
|
if (conf.getTableUseRealCaseSensitiveItemNames()) {
|
||||||
sqlTables.put(vo.getItemname(), t);
|
logger.info("JDBC::formatTableNames: Nothing to migrate.");
|
||||||
tableIds.put(vo.getItemid(), t);
|
initialized = tmpinit;
|
||||||
}
|
return;
|
||||||
|
|
||||||
//
|
|
||||||
List<ItemsVO> al = getItemTables();
|
|
||||||
|
|
||||||
String oldName = "";
|
|
||||||
String newName = "";
|
|
||||||
List<ItemVO> oldNewTablenames = new ArrayList<>();
|
|
||||||
for (int i = 0; i < al.size(); i++) {
|
|
||||||
int id = -1;
|
|
||||||
oldName = al.get(i).getTable_name();
|
|
||||||
logger.info("JDBC::formatTableNames: found Table Name= {}", oldName);
|
|
||||||
|
|
||||||
if (oldName.startsWith(conf.getTableNamePrefix()) && !oldName.contains("_")) {
|
|
||||||
id = Integer.parseInt(oldName.substring(conf.getTableNamePrefix().length()));
|
|
||||||
logger.info("JDBC::formatTableNames: found Table with Prefix '{}' Name= {} id= {}",
|
|
||||||
conf.getTableNamePrefix(), oldName, (id));
|
|
||||||
} else if (oldName.contains("_")) {
|
|
||||||
id = Integer.parseInt(oldName.substring(oldName.lastIndexOf("_") + 1));
|
|
||||||
logger.info("JDBC::formatTableNames: found Table Name= {} id= {}", oldName, (id));
|
|
||||||
}
|
}
|
||||||
logger.info("JDBC::formatTableNames: found Table id= {}", id);
|
oldNewTableNames = new ArrayList<>();
|
||||||
|
for (String itemName : itemTables) {
|
||||||
newName = tableIds.get(id);
|
ItemsVO isvo = new ItemsVO();
|
||||||
logger.info("JDBC::formatTableNames: found Table newName= {}", newName);
|
isvo.setItemName(itemName);
|
||||||
|
isvo = createNewEntryInItemsTable(isvo);
|
||||||
if (newName != null) {
|
int itemId = isvo.getItemId();
|
||||||
if (!oldName.equalsIgnoreCase(newName)) {
|
if (itemId == 0) {
|
||||||
oldNewTablenames.add(new ItemVO(oldName, newName));
|
logger.error("JDBC::formatTableNames: Creating items entry for item '{}' failed.", itemName);
|
||||||
logger.info("JDBC::formatTableNames: Table '{}' will be renamed to '{}'", oldName, newName);
|
|
||||||
} else {
|
} else {
|
||||||
logger.info("JDBC::formatTableNames: Table oldName='{}' newName='{}' nothing to rename", oldName,
|
String newTableName = namingStrategy.getTableName(itemId, itemName);
|
||||||
newName);
|
oldNewTableNames.add(new ItemVO(itemName, newTableName));
|
||||||
|
logger.info("JDBC::formatTableNames: Table '{}' will be renamed to '{}'", itemName, newTableName);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.error("JDBC::formatTableNames: Table '{}' could NOT be renamed to '{}'", oldName, newName);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
String itemsManageTable = new ItemsVO().getItemsManageTable();
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (ItemsVO vo : itemIdTableNames) {
|
||||||
|
int itemId = vo.getItemId();
|
||||||
|
String itemName = vo.getItemName();
|
||||||
|
itemIdToItemNameMap.put(itemId, itemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldNewTableNames = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, itemsManageTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateItemTableNames(oldNewTablenames);
|
updateItemTableNames(oldNewTableNames);
|
||||||
logger.info("JDBC::formatTableNames: Finished updating {} item table names", oldNewTablenames.size());
|
logger.info("JDBC::formatTableNames: Finished updating {} item table names", oldNewTableNames.size());
|
||||||
|
|
||||||
initialized = tmpinit;
|
initialized = tmpinit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTableName(int rowId, String itemName) {
|
|
||||||
return getTableNamePrefix(itemName) + formatRight(rowId, conf.getTableIdDigitCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getTableNamePrefix(String itemName) {
|
|
||||||
String name = conf.getTableNamePrefix();
|
|
||||||
if (conf.getTableUseRealItemNames()) {
|
|
||||||
// Create the table name with real Item Names
|
|
||||||
name = (itemName.replaceAll(ITEM_NAME_PATTERN, "") + "_").toLowerCase();
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<PersistenceItemInfo> getItems() {
|
public Set<PersistenceItemInfo> getItems() {
|
||||||
// TODO: in general it would be possible to query the count, earliest and latest values for each item too but it
|
// TODO: in general it would be possible to query the count, earliest and latest values for each item too but it
|
||||||
// would be a very costly operation
|
// would be a very costly operation
|
||||||
return sqlTables.keySet().stream().map(itemName -> new JdbcPersistenceItemInfo(itemName))
|
return itemNameToTableNameMap.keySet().stream().map(itemName -> new JdbcPersistenceItemInfo(itemName))
|
||||||
.collect(Collectors.<PersistenceItemInfo> toSet());
|
.collect(Collectors.<PersistenceItemInfo> toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String formatRight(final Object value, final int len) {
|
|
||||||
final String valueAsString = String.valueOf(value);
|
|
||||||
if (valueAsString.length() < len) {
|
|
||||||
final StringBuffer result = new StringBuffer(len);
|
|
||||||
for (int i = len - valueAsString.length(); i > 0; i--) {
|
|
||||||
result.append('0');
|
|
||||||
}
|
|
||||||
result.append(valueAsString);
|
|
||||||
return result.toString();
|
|
||||||
} else {
|
|
||||||
return valueAsString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************
|
/*****************
|
||||||
* H E L P E R S *
|
* H E L P E R S *
|
||||||
*****************/
|
*****************/
|
||||||
|
|
|
@ -206,7 +206,7 @@ public class JdbcPersistenceService extends JdbcMapper implements ModifiablePers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String table = sqlTables.get(itemName);
|
String table = itemNameToTableNameMap.get(itemName);
|
||||||
if (table == null) {
|
if (table == null) {
|
||||||
logger.debug("JDBC::query: unable to find table for item with name: '{}', no data in database.", itemName);
|
logger.debug("JDBC::query: unable to find table for item with name: '{}', no data in database.", itemName);
|
||||||
return List.of();
|
return List.of();
|
||||||
|
@ -229,6 +229,7 @@ public class JdbcPersistenceService extends JdbcMapper implements ModifiablePers
|
||||||
|
|
||||||
conf = new JdbcConfiguration(configuration);
|
conf = new JdbcConfiguration(configuration);
|
||||||
if (conf.valid && checkDBAccessability()) {
|
if (conf.valid && checkDBAccessability()) {
|
||||||
|
namingStrategy = new NamingStrategy(conf);
|
||||||
checkDBSchema();
|
checkDBSchema();
|
||||||
// connection has been established ... initialization completed!
|
// connection has been established ... initialization completed!
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
@ -259,7 +260,7 @@ public class JdbcPersistenceService extends JdbcMapper implements ModifiablePers
|
||||||
throw new IllegalArgumentException("Item name must not be null");
|
throw new IllegalArgumentException("Item name must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
String table = sqlTables.get(itemName);
|
String table = itemNameToTableNameMap.get(itemName);
|
||||||
if (table == null) {
|
if (table == null) {
|
||||||
logger.debug("JDBC::remove: unable to find table for item with name: '{}', no data in database.", itemName);
|
logger.debug("JDBC::remove: unable to find table for item with name: '{}', no data in database.", itemName);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.persistence.jdbc.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.items.ItemUtil;
|
||||||
|
import org.openhab.persistence.jdbc.dto.ItemVO;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class manages strategy for table names.
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class NamingStrategy {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(NamingStrategy.class);
|
||||||
|
|
||||||
|
private JdbcConfiguration configuration;
|
||||||
|
|
||||||
|
public NamingStrategy(JdbcConfiguration configuration) {
|
||||||
|
this.configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTableName(int itemId, String itemName) {
|
||||||
|
if (!ItemUtil.isValidItemName(itemName)) {
|
||||||
|
throw new IllegalArgumentException(itemName + " is not a valid item name");
|
||||||
|
}
|
||||||
|
if (configuration.getTableUseRealItemNames()) {
|
||||||
|
return formatTableName(itemName, itemId);
|
||||||
|
} else {
|
||||||
|
return configuration.getTableNamePrefix() + getSuffix(itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatTableName(String itemName, int itemId) {
|
||||||
|
if (configuration.getTableCaseSensitiveItemNames()) {
|
||||||
|
return itemName;
|
||||||
|
} else {
|
||||||
|
return itemName.toLowerCase() + "_" + getSuffix(itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSuffix(int itemId) {
|
||||||
|
int digits = configuration.getTableIdDigitCount();
|
||||||
|
if (digits > 0) {
|
||||||
|
return String.format("%0" + configuration.getTableIdDigitCount() + "d", itemId);
|
||||||
|
} else {
|
||||||
|
return String.valueOf(itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ItemVO> prepareMigration(List<String> itemTables, Map<Integer, String> itemIdToItemNameMap,
|
||||||
|
String itemsManageTable) {
|
||||||
|
List<ItemVO> oldNewTableNames = new ArrayList<>();
|
||||||
|
Map<String, Integer> tableNameToItemIdMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (Entry<Integer, String> entry : itemIdToItemNameMap.entrySet()) {
|
||||||
|
String itemName = entry.getValue();
|
||||||
|
tableNameToItemIdMap.put(itemName, entry.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String oldName : itemTables) {
|
||||||
|
Integer itemIdBoxed = tableNameToItemIdMap.get(oldName);
|
||||||
|
int itemId = -1;
|
||||||
|
|
||||||
|
if (Objects.nonNull(itemIdBoxed)) {
|
||||||
|
itemId = itemIdBoxed;
|
||||||
|
logger.info("JDBC::formatTableNames: found by name; table name= {} id= {}", oldName, itemId);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
itemId = Integer.parseInt(oldName.replaceFirst("^.*\\D", ""));
|
||||||
|
logger.info("JDBC::formatTableNames: found by id; table name= {} id= {}", oldName, itemId);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Fall through.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String itemName = itemIdToItemNameMap.get(itemId);
|
||||||
|
|
||||||
|
if (!Objects.isNull(itemName)) {
|
||||||
|
String newName = getTableName(itemId, itemName);
|
||||||
|
if (newName.equalsIgnoreCase(itemsManageTable)) {
|
||||||
|
logger.error(
|
||||||
|
"JDBC::formatTableNames: Table '{}' could NOT be renamed to '{}' since it conflicts with manage table",
|
||||||
|
oldName, newName);
|
||||||
|
} else if (!oldName.equals(newName)) {
|
||||||
|
oldNewTableNames.add(new ItemVO(oldName, newName));
|
||||||
|
logger.info("JDBC::formatTableNames: Table '{}' will be renamed to '{}'", oldName, newName);
|
||||||
|
} else {
|
||||||
|
logger.info("JDBC::formatTableNames: Table oldName='{}' newName='{}' nothing to rename", oldName,
|
||||||
|
newName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error("JDBC::formatTableNames: Table '{}' could NOT be renamed for id '{}'", oldName, itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldNewTableNames;
|
||||||
|
}
|
||||||
|
}
|
|
@ -143,6 +143,11 @@
|
||||||
#tableUseRealItemNames=
|
#tableUseRealItemNames=
|
||||||
tableUseRealItemNames=true
|
tableUseRealItemNames=true
|
||||||
|
|
||||||
|
# Tablename Prefix generation, using case sensitive item names (optional, default: disabled -> table names are lower cased
|
||||||
|
# with numeric suffix appended).
|
||||||
|
# If true, no suffix is used.
|
||||||
|
#tableCaseSensitiveItemNames=true
|
||||||
|
|
||||||
# Tablename Suffix length (optional, default: 4 -> 0001-9999)
|
# Tablename Suffix length (optional, default: 4 -> 0001-9999)
|
||||||
# for Migration from MYSQL-Bundle set to 0.
|
# for Migration from MYSQL-Bundle set to 0.
|
||||||
#tableIdDigitCount=
|
#tableIdDigitCount=
|
||||||
|
@ -165,6 +170,15 @@
|
||||||
<option value="false">Disable</option>
|
<option value="false">Disable</option>
|
||||||
</options>
|
</options>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
<parameter name="tableCaseSensitiveItemNames" type="text">
|
||||||
|
<label>Tablename Case Sensitive</label>
|
||||||
|
<description><![CDATA[Enables Tablename generation with case sensitive item names case when "Tablename Realname Generation" is enabled <br>
|
||||||
|
If true, no suffix is used. (optional, default: disabled -> table names are lower cased with numeric suffix appended).]]></description>
|
||||||
|
<options>
|
||||||
|
<option value="true">Enable</option>
|
||||||
|
<option value="false">Disable</option>
|
||||||
|
</options>
|
||||||
|
</parameter>
|
||||||
<parameter name="tableIdDigitCount" type="text">
|
<parameter name="tableIdDigitCount" type="text">
|
||||||
<label>Tablename Suffix ID Count</label>
|
<label>Tablename Suffix ID Count</label>
|
||||||
<description><![CDATA[Tablename Suffix ID Count <br>(optional, default: 4 -> 0001-9999). <br>
|
<description><![CDATA[Tablename Suffix ID Count <br>(optional, default: 4 -> 0001-9999). <br>
|
||||||
|
@ -172,7 +186,8 @@
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="rebuildTableNames" type="text">
|
<parameter name="rebuildTableNames" type="text">
|
||||||
<label>Tablename Rebuild</label>
|
<label>Tablename Rebuild</label>
|
||||||
<description><![CDATA[Rename existing tables using 'Tablename Realname Generation' and 'Tablename Suffix ID Count', (optional, default: disabled). <br>
|
<description><![CDATA[Rename existing tables using 'Tablename Prefix String', 'Tablename Realname Generation', 'Tablename Case Sensitive' and
|
||||||
|
'Tablename Suffix ID Count'. (optional, default: disabled). <br>
|
||||||
USE WITH CARE! Deactivate after renaming is done!]]></description>
|
USE WITH CARE! Deactivate after renaming is done!]]></description>
|
||||||
<options>
|
<options>
|
||||||
<option value="true">Enable</option>
|
<option value="true">Enable</option>
|
||||||
|
|
|
@ -9,7 +9,7 @@ persistence.config.jdbc.minimumIdle.description = Overrides min idle database co
|
||||||
persistence.config.jdbc.password.label = Database Password
|
persistence.config.jdbc.password.label = Database Password
|
||||||
persistence.config.jdbc.password.description = Defines the database password.
|
persistence.config.jdbc.password.description = Defines the database password.
|
||||||
persistence.config.jdbc.rebuildTableNames.label = Tablename Rebuild
|
persistence.config.jdbc.rebuildTableNames.label = Tablename Rebuild
|
||||||
persistence.config.jdbc.rebuildTableNames.description = Rename existing tables using 'Tablename Realname Generation' and 'Tablename Suffix ID Count', (optional, default: disabled). <br> USE WITH CARE! Deactivate after renaming is done!
|
persistence.config.jdbc.rebuildTableNames.description = Rename existing tables using 'Tablename Prefix String', 'Tablename Realname Generation', 'Tablename Case Sensitive' and 'Tablename Suffix ID Count'. (optional, default: disabled). <br> USE WITH CARE! Deactivate after renaming is done!
|
||||||
persistence.config.jdbc.rebuildTableNames.option.true = Enable
|
persistence.config.jdbc.rebuildTableNames.option.true = Enable
|
||||||
persistence.config.jdbc.rebuildTableNames.option.false = Disable
|
persistence.config.jdbc.rebuildTableNames.option.false = Disable
|
||||||
persistence.config.jdbc.sqltype.CALL.label = SqlType CALL
|
persistence.config.jdbc.sqltype.CALL.label = SqlType CALL
|
||||||
|
@ -36,6 +36,10 @@ persistence.config.jdbc.sqltype.STRING.label = SqlType STRING
|
||||||
persistence.config.jdbc.sqltype.STRING.description = Overrides used JDBC/SQL datatype for STRING <br>(optional, default: "VARCHAR(65500)").
|
persistence.config.jdbc.sqltype.STRING.description = Overrides used JDBC/SQL datatype for STRING <br>(optional, default: "VARCHAR(65500)").
|
||||||
persistence.config.jdbc.sqltype.SWITCH.label = SqlType SWITCH
|
persistence.config.jdbc.sqltype.SWITCH.label = SqlType SWITCH
|
||||||
persistence.config.jdbc.sqltype.SWITCH.description = Overrides used JDBC/SQL datatype for SWITCH <br>(optional, default: "VARCHAR(6)").
|
persistence.config.jdbc.sqltype.SWITCH.description = Overrides used JDBC/SQL datatype for SWITCH <br>(optional, default: "VARCHAR(6)").
|
||||||
|
persistence.config.jdbc.tableCaseSensitiveItemNames.label = Tablename Case Sensitive
|
||||||
|
persistence.config.jdbc.tableCaseSensitiveItemNames.description = Enables Tablename generation with case sensitive item names case when "Tablename Realname Generation" is enabled <br> If true, no suffix is used. (optional, default: disabled -> table names are lower cased with numeric suffix appended).
|
||||||
|
persistence.config.jdbc.tableCaseSensitiveItemNames.option.true = Enable
|
||||||
|
persistence.config.jdbc.tableCaseSensitiveItemNames.option.false = Disable
|
||||||
persistence.config.jdbc.tableIdDigitCount.label = Tablename Suffix ID Count
|
persistence.config.jdbc.tableIdDigitCount.label = Tablename Suffix ID Count
|
||||||
persistence.config.jdbc.tableIdDigitCount.description = Tablename Suffix ID Count <br>(optional, default: 4 -> 0001-9999). <br> For migration from MYSQL-Bundle set to 0.
|
persistence.config.jdbc.tableIdDigitCount.description = Tablename Suffix ID Count <br>(optional, default: 4 -> 0001-9999). <br> For migration from MYSQL-Bundle set to 0.
|
||||||
persistence.config.jdbc.tableNamePrefix.label = Tablename Prefix String
|
persistence.config.jdbc.tableNamePrefix.label = Tablename Prefix String
|
||||||
|
|
|
@ -0,0 +1,444 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.persistence.jdbc.internal;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.core.Is.is;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.mockito.junit.jupiter.MockitoSettings;
|
||||||
|
import org.mockito.quality.Strictness;
|
||||||
|
import org.openhab.persistence.jdbc.dto.ItemVO;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link NamingStrategy} class.
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
|
@NonNullByDefault
|
||||||
|
public class NamingStrategyTest {
|
||||||
|
private static final String ITEMS_MANAGE_TABLE_NAME = "items";
|
||||||
|
|
||||||
|
private @Mock @NonNullByDefault({}) JdbcConfiguration configurationMock;
|
||||||
|
private NamingStrategy namingStrategy = new NamingStrategy(configurationMock);
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void initialize() {
|
||||||
|
final Logger logger = (Logger) LoggerFactory.getLogger(NamingStrategy.class);
|
||||||
|
logger.setLevel(Level.OFF);
|
||||||
|
namingStrategy = new NamingStrategy(configurationMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTableNameWhenInvalidItemNameThrows() {
|
||||||
|
Assertions.assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
namingStrategy.getTableName(1, "4Two");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTableNameWhenUseRealItemNamesNameIsLowerCaseAndNumbered() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
assertThat(namingStrategy.getTableName(1, "Test"), is("test_1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTableNameWhenUseRealCaseSensitiveItemNamesNameIsSameCase() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
assertThat(namingStrategy.getTableName(1, "Camel"), is("Camel"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTableNameWhenUseRealCaseSensitiveItemNamesNameIsSameCaseLower() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
assertThat(namingStrategy.getTableName(1, "lower"), is("lower"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTableNameWhenNotUseRealItemNamesAndCount4NameHasLeavingZeros() {
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(4).when(configurationMock).getTableIdDigitCount();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
assertThat(namingStrategy.getTableName(2, "Test"), is("Item0002"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTableNameWhenNotUseRealItemNamesAndCount0() {
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(0).when(configurationMock).getTableIdDigitCount();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
assertThat(namingStrategy.getTableName(12345, "Test"), is("Item12345"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromNumberedToRealNames() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "Item1";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationRealItemNames(itemId, itemName, tableName);
|
||||||
|
|
||||||
|
assertTableName(actual, "Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationWithChangedPrefix() {
|
||||||
|
Mockito.doReturn(0).when(configurationMock).getTableIdDigitCount();
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "Item1";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigration(itemId, itemName, tableName, "item");
|
||||||
|
|
||||||
|
assertTableName(actual, "item1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationShouldNotStopWhenEncounteringUnknownItem() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(2);
|
||||||
|
itemIdToItemNameMap.put(1, "First");
|
||||||
|
itemIdToItemNameMap.put(3, "Third");
|
||||||
|
|
||||||
|
List<String> itemTables = new ArrayList<String>(3);
|
||||||
|
itemTables.add("Item1");
|
||||||
|
itemTables.add("Item2");
|
||||||
|
itemTables.add("Item3");
|
||||||
|
|
||||||
|
List<ItemVO> actual = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(2));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is("First"));
|
||||||
|
assertThat(actual.get(1).getNewTableName(), is("Third"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromMixedNumberedToNumberedRealNames() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(3);
|
||||||
|
itemIdToItemNameMap.put(1, "First");
|
||||||
|
itemIdToItemNameMap.put(2, "Second");
|
||||||
|
itemIdToItemNameMap.put(3, "Third");
|
||||||
|
|
||||||
|
List<String> itemTables = new ArrayList<String>(3);
|
||||||
|
itemTables.add("Item1");
|
||||||
|
itemTables.add("Item002");
|
||||||
|
itemTables.add("third_0003");
|
||||||
|
|
||||||
|
List<ItemVO> actual = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(3));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is("first_1"));
|
||||||
|
assertThat(actual.get(1).getNewTableName(), is("second_2"));
|
||||||
|
assertThat(actual.get(2).getNewTableName(), is("third_3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromMixedNumberedToCaseSensitiveRealNames() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(3);
|
||||||
|
itemIdToItemNameMap.put(1, "First");
|
||||||
|
itemIdToItemNameMap.put(2, "Second");
|
||||||
|
itemIdToItemNameMap.put(3, "Third");
|
||||||
|
|
||||||
|
List<String> itemTables = new ArrayList<String>(3);
|
||||||
|
itemTables.add("Item1");
|
||||||
|
itemTables.add("Item002");
|
||||||
|
itemTables.add("third_0003");
|
||||||
|
|
||||||
|
List<ItemVO> actual = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(3));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is("First"));
|
||||||
|
assertThat(actual.get(1).getNewTableName(), is("Second"));
|
||||||
|
assertThat(actual.get(2).getNewTableName(), is("Third"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromNumberedRealNamesToCaseSensitiveRealNames() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "test_0001";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationRealItemNames(itemId, itemName, tableName, true);
|
||||||
|
|
||||||
|
assertTableName(actual, "Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromCaseSensitiveRealNamesToNumberedRealNames() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "Test";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationRealItemNames(itemId, itemName, tableName, false);
|
||||||
|
|
||||||
|
assertTableName(actual, "test_0001");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationRealNamesWithTwoItemsWithDifferentCaseToNumbered() {
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
Mockito.doReturn(1).when(configurationMock).getTableIdDigitCount();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(2);
|
||||||
|
itemIdToItemNameMap.put(1, "MyItem");
|
||||||
|
itemIdToItemNameMap.put(2, "myItem");
|
||||||
|
|
||||||
|
List<String> itemTables = new ArrayList<String>(2);
|
||||||
|
itemTables.add("MyItem");
|
||||||
|
itemTables.add("myItem");
|
||||||
|
|
||||||
|
List<ItemVO> actual = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(2));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is("Item1"));
|
||||||
|
assertThat(actual.get(1).getNewTableName(), is("Item2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationNumberedWithTwoItemsWithDifferentCaseToNumberedRealNames() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(2);
|
||||||
|
itemIdToItemNameMap.put(1, "MyItem");
|
||||||
|
itemIdToItemNameMap.put(2, "myItem");
|
||||||
|
|
||||||
|
List<String> itemTables = new ArrayList<String>(2);
|
||||||
|
itemTables.add("Item1");
|
||||||
|
itemTables.add("Item2");
|
||||||
|
|
||||||
|
List<ItemVO> actual = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(2));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is("myitem_1"));
|
||||||
|
assertThat(actual.get(1).getNewTableName(), is("myitem_2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationNumberedWithTwoItemsWithDifferentCaseToCaseSensitiveRealNames() {
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(2);
|
||||||
|
itemIdToItemNameMap.put(1, "MyItem");
|
||||||
|
itemIdToItemNameMap.put(2, "myItem");
|
||||||
|
|
||||||
|
List<String> itemTables = new ArrayList<String>(2);
|
||||||
|
itemTables.add("Item1");
|
||||||
|
itemTables.add("Item2");
|
||||||
|
|
||||||
|
List<ItemVO> actual = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(2));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is("MyItem"));
|
||||||
|
assertThat(actual.get(1).getNewTableName(), is("myItem"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromNumberedRealNamesToCaseSensitiveRealNamesWhenUnknownItemIdThenSkip() {
|
||||||
|
final int itemId = 2;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "test_0001";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationRealItemNames(itemId, itemName, tableName);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromNumberedRealNamesToNumbered() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "test_0001";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationNumbered(itemId, itemName, tableName);
|
||||||
|
|
||||||
|
assertTableName(actual, "Item0001");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromNumberedToNumberedWithCorrectPadding() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "Item1";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationNumbered(itemId, itemName, tableName, 2);
|
||||||
|
|
||||||
|
assertTableName(actual, "Item01");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromNumberedToNumberedExceedingPadding() {
|
||||||
|
final int itemId = 101;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "Item0101";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationNumbered(itemId, itemName, tableName, 2);
|
||||||
|
|
||||||
|
assertTableName(actual, "Item101");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromCaseSensitiveRealNamesToNumbered() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "Test";
|
||||||
|
final String tableName = "Test";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationNumbered(itemId, itemName, tableName);
|
||||||
|
|
||||||
|
assertTableName(actual, "Item0001");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromCaseSensitiveRealNamesToNumberedHavingUnderscore() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "My_Test";
|
||||||
|
final String tableName = "My_Test";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationNumbered(itemId, itemName, tableName);
|
||||||
|
|
||||||
|
assertTableName(actual, "Item0001");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromCaseSensitiveRealNamesHavingUnderscoreAndNumberToNumbered() {
|
||||||
|
final int itemId = 2;
|
||||||
|
final String itemName = "My_Test_1";
|
||||||
|
final String tableName = "My_Test_1";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationNumbered(itemId, itemName, tableName);
|
||||||
|
|
||||||
|
assertTableName(actual, "Item0002");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationFromCaseSensitiveRealNamesToNumberedShouldSwap() {
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn("Item").when(configurationMock).getTableNamePrefix();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(2);
|
||||||
|
itemIdToItemNameMap.put(1, "Item2");
|
||||||
|
itemIdToItemNameMap.put(2, "Item1");
|
||||||
|
|
||||||
|
List<String> itemTables = new ArrayList<String>(2);
|
||||||
|
itemTables.add("Item2");
|
||||||
|
itemTables.add("Item1");
|
||||||
|
|
||||||
|
List<ItemVO> actual = namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(2));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is("Item1"));
|
||||||
|
assertThat(actual.get(1).getNewTableName(), is("Item2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prepareMigrationWhenConflictWithItemsManageTableThenSkip() {
|
||||||
|
final int itemId = 1;
|
||||||
|
final String itemName = "items";
|
||||||
|
final String tableName = "Item1";
|
||||||
|
|
||||||
|
List<ItemVO> actual = prepareMigrationRealItemNames(itemId, itemName, tableName);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ItemVO> prepareMigrationNumbered(int itemId, String itemName, String tableName) {
|
||||||
|
return prepareMigrationNumbered(itemId, itemName, tableName, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ItemVO> prepareMigrationNumbered(int itemId, String itemName, String tableName,
|
||||||
|
int tableIdDigitCount) {
|
||||||
|
Mockito.doReturn(tableIdDigitCount).when(configurationMock).getTableIdDigitCount();
|
||||||
|
Mockito.doReturn(false).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
return prepareMigration(itemId, itemName, tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ItemVO> prepareMigrationRealItemNames(int itemId, String itemName, String tableName) {
|
||||||
|
return prepareMigrationRealItemNames(itemId, itemName, tableName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ItemVO> prepareMigrationRealItemNames(int itemId, String itemName, String tableName,
|
||||||
|
boolean caseSensitive) {
|
||||||
|
Mockito.doReturn(4).when(configurationMock).getTableIdDigitCount();
|
||||||
|
Mockito.doReturn(true).when(configurationMock).getTableUseRealItemNames();
|
||||||
|
Mockito.doReturn(caseSensitive).when(configurationMock).getTableCaseSensitiveItemNames();
|
||||||
|
return prepareMigration(itemId, itemName, tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ItemVO> prepareMigration(int itemId, String itemName, String tableName) {
|
||||||
|
return prepareMigration(itemId, itemName, tableName, "Item");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ItemVO> prepareMigration(int itemId, String itemName, String tableName, String prefix) {
|
||||||
|
Mockito.doReturn(prefix).when(configurationMock).getTableNamePrefix();
|
||||||
|
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = getItemIdToItemNameMap(itemId, itemName);
|
||||||
|
List<String> itemTables = getItemTables(tableName);
|
||||||
|
|
||||||
|
return namingStrategy.prepareMigration(itemTables, itemIdToItemNameMap, ITEMS_MANAGE_TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Integer, String> getItemIdToItemNameMap(int itemId, String itemName) {
|
||||||
|
Map<Integer, String> itemIdToItemNameMap = new HashMap<>(1);
|
||||||
|
itemIdToItemNameMap.put(itemId, itemName);
|
||||||
|
return itemIdToItemNameMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getItemTables(String tableName) {
|
||||||
|
List<String> itemTables = new ArrayList<String>(1);
|
||||||
|
itemTables.add(tableName);
|
||||||
|
return itemTables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTableName(List<ItemVO> actual, String expected) {
|
||||||
|
assertThat(actual.size(), is(1));
|
||||||
|
assertThat(actual.get(0).getNewTableName(), is(expected));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue