[jdbc] Implement 'ModifiablePersistenceService' interface (#11922)

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
Christoph Weitkamp 2022-01-15 14:29:06 +01:00 committed by GitHub
parent 344a4ceae3
commit bb2e4c7c65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 382 additions and 98 deletions

View File

@ -13,7 +13,6 @@
package org.openhab.persistence.jdbc.db;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@ -327,8 +326,8 @@ public class JdbcBaseDAO {
Yank.execute(sql, null);
}
public void doStoreItemValue(Item item, ItemVO vo) {
ItemVO storedVO = storeItemValueProvider(item, vo);
public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
new String[] { "#tableName#", "#tablePrimaryValue#" },
new String[] { storedVO.getTableName(), sqlTypes.get("tablePrimaryValue") });
@ -337,6 +336,16 @@ public class JdbcBaseDAO {
Yank.execute(sql, params);
}
public void doStoreItemValue(Item item, State itemState, ItemVO vo, ZonedDateTime date) {
ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
new String[] { "#tableName#", "#tablePrimaryValue#" }, new String[] { storedVO.getTableName(), "?" });
java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli());
Object[] params = new Object[] { storedVO.getValue(), timestamp, storedVO.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue());
Yank.execute(sql, params);
}
public List<HistoricItem> doGetHistItemFilterQuery(Item item, FilterCriteria filter, int numberDecimalcount,
String table, String name, ZoneId timeZone) {
String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone);
@ -353,6 +362,12 @@ public class JdbcBaseDAO {
.collect(Collectors.<HistoricItem> toList());
}
public void doDeleteItemValues(Item item, FilterCriteria filter, String table, ZoneId timeZone) {
String sql = histItemFilterDeleteProvider(filter, table, timeZone);
logger.debug("JDBC::doDeleteItemValues sql={}", sql);
Yank.execute(sql, null);
}
/*************
* Providers *
*************/
@ -364,19 +379,9 @@ public class JdbcBaseDAO {
"JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
filter, numberDecimalcount, table, simpleName);
String filterString = "";
if (filter.getBeginDate() != null) {
filterString += filterString.isEmpty() ? " WHERE" : " AND";
filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
+ "'";
}
if (filter.getEndDate() != null) {
filterString += filterString.isEmpty() ? " WHERE" : " AND";
filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
+ "'";
}
filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC ";
if (filter.getPageSize() != 0x7fffffff) {
String filterString = resolveTimeFilter(filter, timeZone);
filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC";
if (filter.getPageSize() != Integer.MAX_VALUE) {
filterString += " LIMIT " + filter.getPageNumber() * filter.getPageSize() + "," + filter.getPageSize();
}
// SELECT time, ROUND(value,3) FROM number_item_0114 ORDER BY time DESC LIMIT 0,1
@ -391,6 +396,33 @@ public class JdbcBaseDAO {
return queryString;
}
protected String histItemFilterDeleteProvider(FilterCriteria filter, String table, ZoneId timeZone) {
logger.debug("JDBC::histItemFilterDeleteProvider filter = {}, table = {}", filter, table);
String filterString = resolveTimeFilter(filter, timeZone);
String deleteString = "DELETE FROM " + table;
if (!filterString.isEmpty()) {
deleteString += filterString;
}
logger.debug("JDBC::delete deleteString = {}", deleteString);
return deleteString;
}
protected String resolveTimeFilter(FilterCriteria filter, ZoneId timeZone) {
String filterString = "";
if (filter.getBeginDate() != null) {
filterString += filterString.isEmpty() ? " WHERE" : " AND";
filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
+ "'";
}
if (filter.getEndDate() != null) {
filterString += filterString.isEmpty() ? " WHERE" : " AND";
filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
+ "'";
}
return filterString;
}
private String updateItemTableNamesProvider(List<ItemVO> namesList) {
logger.debug("JDBC::updateItemTableNamesProvider namesList.size = {}", namesList.size());
String queryString = "";
@ -402,14 +434,14 @@ public class JdbcBaseDAO {
return queryString;
}
protected ItemVO storeItemValueProvider(Item item, ItemVO vo) {
protected ItemVO storeItemValueProvider(Item item, State itemState, ItemVO vo) {
String itemType = getItemType(item);
logger.debug("JDBC::storeItemValueProvider: item '{}' as Type '{}' in '{}' with state '{}'", item.getName(),
itemType, vo.getTableName(), item.getState());
itemType, vo.getTableName(), itemState);
// insertItemValue
logger.debug("JDBC::storeItemValueProvider: getState: '{}'", item.getState());
logger.debug("JDBC::storeItemValueProvider: itemState: '{}'", itemState);
/*
* !!ATTENTION!!
*
@ -427,20 +459,19 @@ public class JdbcBaseDAO {
switch (itemType) {
case "COLORITEM":
vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
vo.setValue(item.getState().toString());
vo.setValue(itemState.toString());
break;
case "NUMBERITEM":
State state = item.getState();
State convertedState = state;
if (item instanceof NumberItem && state instanceof QuantityType) {
State convertedState = itemState;
if (item instanceof NumberItem && itemState instanceof QuantityType) {
Unit<? extends Quantity<?>> unit = ((NumberItem) item).getUnit();
if (unit != null && !Units.ONE.equals(unit)) {
convertedState = ((QuantityType<?>) state).toUnit(unit);
convertedState = ((QuantityType<?>) itemState).toUnit(unit);
if (convertedState == null) {
logger.warn(
"JDBC::storeItemValueProvider: Failed to convert state '{}' to unit '{}'. Please check your item definition for correctness.",
state, unit);
convertedState = state;
itemState, unit);
convertedState = itemState;
}
}
}
@ -462,21 +493,21 @@ public class JdbcBaseDAO {
vo.setValue(value);
} else {// fall back to String
vo.setValueTypes(it, java.lang.String.class);
logger.warn("JDBC::storeItemValueProvider: item.getState().toString(): '{}'", convertedState);
logger.warn("JDBC::storeItemValueProvider: itemState: '{}'", convertedState);
vo.setValue(convertedState.toString());
}
break;
case "ROLLERSHUTTERITEM":
case "DIMMERITEM":
vo.setValueTypes(getSqlTypes().get(itemType), java.lang.Integer.class);
int value = ((DecimalType) item.getState()).intValue();
int value = ((DecimalType) itemState).intValue();
logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", value);
vo.setValue(value);
break;
case "DATETIMEITEM":
vo.setValueTypes(getSqlTypes().get(itemType), java.sql.Timestamp.class);
java.sql.Timestamp d = new java.sql.Timestamp(
((DateTimeType) item.getState()).getZonedDateTime().toInstant().toEpochMilli());
((DateTimeType) itemState).getZonedDateTime().toInstant().toEpochMilli());
logger.debug("JDBC::storeItemValueProvider: DateTimeItem: '{}'", d);
vo.setValue(d);
break;
@ -489,8 +520,8 @@ public class JdbcBaseDAO {
default:
// All other items should return the best format by default
vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
logger.debug("JDBC::storeItemValueProvider: other: item.getState().toString(): '{}'", item.getState());
vo.setValue(item.getState().toString());
logger.debug("JDBC::storeItemValueProvider: other: itemState: '{}'", itemState);
vo.setValue(itemState.toString());
break;
}
return vo;
@ -533,9 +564,10 @@ public class JdbcBaseDAO {
protected ZonedDateTime objectAsDate(Object v) {
if (v instanceof java.lang.String) {
return ZonedDateTime.ofInstant(Timestamp.valueOf(v.toString()).toInstant(), ZoneId.systemDefault());
return ZonedDateTime.ofInstant(java.sql.Timestamp.valueOf(v.toString()).toInstant(),
ZoneId.systemDefault());
}
return ZonedDateTime.ofInstant(((Timestamp) v).toInstant(), ZoneId.systemDefault());
return ZonedDateTime.ofInstant(((java.sql.Timestamp) v).toInstant(), ZoneId.systemDefault());
}
protected Long objectAsLong(Object v) {

View File

@ -25,6 +25,7 @@ import org.openhab.core.library.items.NumberItem;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.FilterCriteria.Ordering;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.types.State;
import org.openhab.persistence.jdbc.dto.ItemVO;
import org.openhab.persistence.jdbc.dto.ItemsVO;
import org.openhab.persistence.jdbc.dto.JdbcHistoricItem;
@ -148,13 +149,14 @@ public class JdbcDerbyDAO extends JdbcBaseDAO {
}
@Override
public void doStoreItemValue(Item item, ItemVO vo) {
vo = storeItemValueProvider(item, vo);
public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
new String[] { vo.getTableName().toUpperCase(), vo.getDbType(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { vo.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, vo.getValue());
new String[] { storedVO.getTableName().toUpperCase(), storedVO.getDbType(),
sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { storedVO.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
Yank.execute(sql, params);
}

View File

@ -14,6 +14,7 @@ package org.openhab.persistence.jdbc.db;
import org.knowm.yank.Yank;
import org.openhab.core.items.Item;
import org.openhab.core.types.State;
import org.openhab.persistence.jdbc.dto.ItemVO;
import org.openhab.persistence.jdbc.utils.StringUtilsExt;
import org.slf4j.Logger;
@ -71,13 +72,13 @@ public class JdbcH2DAO extends JdbcBaseDAO {
* ITEM DAOs *
*************/
@Override
public void doStoreItemValue(Item item, ItemVO vo) {
vo = storeItemValueProvider(item, vo);
public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
new String[] { vo.getTableName(), vo.getDbType(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { vo.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, vo.getValue());
new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { storedVO.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
Yank.execute(sql, params);
}

View File

@ -14,6 +14,7 @@ package org.openhab.persistence.jdbc.db;
import org.knowm.yank.Yank;
import org.openhab.core.items.Item;
import org.openhab.core.types.State;
import org.openhab.persistence.jdbc.dto.ItemVO;
import org.openhab.persistence.jdbc.dto.ItemsVO;
import org.openhab.persistence.jdbc.utils.StringUtilsExt;
@ -101,13 +102,14 @@ public class JdbcHsqldbDAO extends JdbcBaseDAO {
* ITEM DAOs *
*************/
@Override
public void doStoreItemValue(Item item, ItemVO vo) {
vo = storeItemValueProvider(item, vo);
public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
new String[] { "#tableName#", "#dbType#", "#tableName#", "#tablePrimaryValue#" }, new String[] {
vo.getTableName(), vo.getDbType(), vo.getTableName(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { vo.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, vo.getValue());
new String[] { "#tableName#", "#dbType#", "#tableName#", "#tablePrimaryValue#" },
new String[] { storedVO.getTableName(), storedVO.getDbType(), storedVO.getTableName(),
sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { storedVO.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
Yank.execute(sql, params);
}

View File

@ -19,6 +19,7 @@ import org.knowm.yank.Yank;
import org.openhab.core.items.Item;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.FilterCriteria.Ordering;
import org.openhab.core.types.State;
import org.openhab.persistence.jdbc.dto.ItemVO;
import org.openhab.persistence.jdbc.dto.ItemsVO;
import org.openhab.persistence.jdbc.utils.StringUtilsExt;
@ -133,13 +134,13 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO {
* ITEM DAOs *
*************/
@Override
public void doStoreItemValue(Item item, ItemVO vo) {
vo = storeItemValueProvider(item, vo);
public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
new String[] { vo.getTableName(), vo.getDbType(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { vo.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, vo.getValue());
new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { storedVO.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
Yank.execute(sql, params);
}

View File

@ -14,6 +14,7 @@ package org.openhab.persistence.jdbc.db;
import org.knowm.yank.Yank;
import org.openhab.core.items.Item;
import org.openhab.core.types.State;
import org.openhab.persistence.jdbc.dto.ItemVO;
import org.openhab.persistence.jdbc.dto.ItemsVO;
import org.openhab.persistence.jdbc.utils.StringUtilsExt;
@ -90,13 +91,13 @@ public class JdbcSqliteDAO extends JdbcBaseDAO {
* ITEM DAOs *
*************/
@Override
public void doStoreItemValue(Item item, ItemVO vo) {
vo = storeItemValueProvider(item, vo);
public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
new String[] { vo.getTableName(), vo.getDbType(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { vo.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, vo.getValue());
new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
Object[] params = new Object[] { storedVO.getValue() };
logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
Yank.execute(sql, params);
}

View File

@ -16,6 +16,7 @@ import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -30,7 +31,7 @@ public class ItemVO implements Serializable {
private static final long serialVersionUID = 1871441039821454890L;
private String tableName;
private String newTableName;
private @Nullable String newTableName;
private String dbType;
private String jdbcType;
private String itemType;
@ -38,7 +39,7 @@ public class ItemVO implements Serializable {
private Date time;
private Object value;
public ItemVO(String tableName, String newTableName) {
public ItemVO(String tableName, @Nullable String newTableName) {
logger.debug("JDBC:ItemVO tableName={}; newTableName={}; ", tableName, newTableName);
this.tableName = tableName;
this.newTableName = newTableName;
@ -61,7 +62,7 @@ public class ItemVO implements Serializable {
this.tableName = tableName;
}
public String getNewTableName() {
public @Nullable String getNewTableName() {
return newTableName;
}
@ -117,11 +118,6 @@ public class ItemVO implements Serializable {
this.value = value;
}
/**
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
@ -146,20 +142,8 @@ public class ItemVO implements Serializable {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ItemVO [tableName=");
builder.append(tableName);
builder.append(", newTableName=");
builder.append(newTableName);
builder.append(", dbType=");
builder.append(dbType);
builder.append(", javaType=");
builder.append(javaType);
builder.append(", time=");
builder.append(time);
builder.append(", value=");
builder.append(value);
builder.append("]");
return builder.toString();
return new StringBuilder("ItemVO [tableName=").append(tableName).append(", newTableName=").append(newTableName)
.append(", dbType=").append(dbType).append(", javaType=").append(javaType).append(", time=")
.append(time).append(", value=").append(value).append("]").toString();
}
}

View File

@ -12,6 +12,7 @@
*/
package org.openhab.persistence.jdbc.internal;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -19,12 +20,14 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.Nullable;
import org.knowm.yank.Yank;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.items.Item;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.types.State;
import org.openhab.persistence.jdbc.dto.ItemVO;
import org.openhab.persistence.jdbc.dto.ItemsVO;
import org.openhab.persistence.jdbc.dto.JdbcPersistenceItemInfo;
@ -145,15 +148,19 @@ public class JdbcMapper {
return vo;
}
public Item storeItemValue(Item item) {
logger.debug("JDBC::storeItemValue: item={}", item);
public Item storeItemValue(Item item, State itemState, @Nullable ZonedDateTime date) {
logger.debug("JDBC::storeItemValue: item={} state={} date={}", item, itemState, date);
String tableName = getTable(item);
if (tableName == null) {
logger.error("JDBC::store: Unable to store item '{}'.", item.getName());
return item;
}
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doStoreItemValue(item, new ItemVO(tableName, null));
if (date == null) {
conf.getDBDAO().doStoreItemValue(item, itemState, new ItemVO(tableName, null));
} else {
conf.getDBDAO().doStoreItemValue(item, itemState, new ItemVO(tableName, null), date);
}
logTime("storeItemValue", timerStart, System.currentTimeMillis());
errCnt = 0;
return item;
@ -177,6 +184,21 @@ public class JdbcMapper {
return null;
}
public boolean deleteItemValues(FilterCriteria filter, String table, Item item) {
logger.debug("JDBC::deleteItemValues filter='{}' table='{}' item='{}' itemName='{}'", (filter != null), table,
item, item.getName());
if (table != null) {
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doDeleteItemValues(item, filter, table, timeZoneProvider.getTimeZone());
logTime("deleteItemValues", timerStart, System.currentTimeMillis());
errCnt = 0;
return true;
} else {
logger.error("JDBC::deleteItemValues: TABLE is NULL; cannot delete data from non-existent table.");
return false;
}
}
/***********************
* DATABASE CONNECTION *
***********************/

View File

@ -12,6 +12,8 @@
*/
package org.openhab.persistence.jdbc.internal;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -27,10 +29,12 @@ import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.ModifiablePersistenceService;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.persistence.PersistenceService;
import org.openhab.core.persistence.QueryablePersistenceService;
import org.openhab.core.persistence.strategy.PersistenceStrategy;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@ -52,8 +56,10 @@ import org.slf4j.LoggerFactory;
QueryablePersistenceService.class }, configurationPid = "org.openhab.jdbc", //
property = Constants.SERVICE_PID + "=org.openhab.jdbc")
@ConfigurableService(category = "persistence", label = "JDBC Persistence Service", description_uri = JdbcPersistenceService.CONFIG_URI)
public class JdbcPersistenceService extends JdbcMapper implements QueryablePersistenceService {
public class JdbcPersistenceService extends JdbcMapper implements ModifiablePersistenceService {
private static final String SERVICE_ID = "jdbc";
private static final String SERVICE_LABEL = "JDBC";
protected static final String CONFIG_URI = "persistence:jdbc";
private final Logger logger = LoggerFactory.getLogger(JdbcPersistenceService.class);
@ -110,39 +116,48 @@ public class JdbcPersistenceService extends JdbcMapper implements QueryablePersi
@Override
public String getId() {
logger.debug("JDBC::getName: returning name 'jdbc' for queryable persistence service.");
return "jdbc";
return SERVICE_ID;
}
@Override
public String getLabel(@Nullable Locale locale) {
return "JDBC";
return SERVICE_LABEL;
}
@Override
public void store(Item item) {
store(item, null);
internalStore(item, null, item.getState());
}
/**
* @{inheritDoc
*/
@Override
public void store(Item item, @Nullable String alias) {
// alias is not supported
internalStore(item, null, item.getState());
}
@Override
public void store(Item item, ZonedDateTime date, State state) {
internalStore(item, date, state);
}
private void internalStore(Item item, @Nullable ZonedDateTime date, State state) {
// Do not store undefined/uninitialized data
if (item.getState() instanceof UnDefType) {
if (state instanceof UnDefType) {
logger.debug("JDBC::store: ignore Item '{}' because it is UnDefType", item.getName());
return;
}
if (!checkDBAccessability()) {
logger.warn(
"JDBC::store: No connection to database. Cannot persist item '{}'! Will retry connecting to database when error count:{} equals errReconnectThreshold:{}",
item, errCnt, conf.getErrReconnectThreshold());
"JDBC::store: No connection to database. Cannot persist state '{}' for item '{}'! Will retry connecting to database when error count:{} equals errReconnectThreshold:{}",
state, item, errCnt, conf.getErrReconnectThreshold());
return;
}
long timerStart = System.currentTimeMillis();
storeItemValue(item);
logger.debug("JDBC: Stored item '{}' as '{}' in SQL database at {} in {} ms.", item.getName(), item.getState(),
new java.util.Date(), System.currentTimeMillis() - timerStart);
storeItemValue(item, state, date);
if (logger.isDebugEnabled()) {
logger.debug("JDBC: Stored item '{}' as '{}' in SQL database at {} in {} ms.", item.getName(), state,
new Date(), System.currentTimeMillis() - timerStart);
}
}
@Override
@ -228,4 +243,42 @@ public class JdbcPersistenceService extends JdbcMapper implements QueryablePersi
public List<PersistenceStrategy> getDefaultStrategies() {
return List.of(PersistenceStrategy.Globals.CHANGE);
}
@Override
public boolean remove(FilterCriteria filter) throws IllegalArgumentException {
if (!checkDBAccessability()) {
logger.warn("JDBC::remove: database not connected, remove aborted for item '{}'", filter.getItemName());
return false;
}
// Get the item name from the filter
// Also get the Item object so we can determine the type
Item item = null;
String itemName = filter.getItemName();
logger.debug("JDBC::remove: item is {}", itemName);
if (itemName == null) {
throw new IllegalArgumentException("Item name must not be null");
}
try {
item = itemRegistry.getItem(itemName);
} catch (ItemNotFoundException e) {
logger.error("JDBC::remove: unable to get item for itemName: '{}'. Ignore and give up!", itemName);
return false;
}
String table = sqlTables.get(itemName);
if (table == null) {
logger.debug("JDBC::remove: unable to find table for item with name: '{}', no data in database.", itemName);
return false;
}
long timerStart = System.currentTimeMillis();
boolean result = deleteItemValues(filter, table, item);
if (logger.isDebugEnabled()) {
logger.debug("JDBC: Deleted values for item '{}' in SQL database at {} in {} ms.", item.getName(),
new Date(), System.currentTimeMillis() - timerStart);
}
return result;
}
}

View File

@ -0,0 +1,135 @@
/**
* 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.db;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.FilterCriteria.Ordering;
/**
* Tests the {@link JdbcBaseDAO}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class JdbcBaseDAOTest {
private static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
private static final DateTimeFormatter DATE_PARSER = DateTimeFormatter.ofPattern(DATE_PATTERN);
private static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC");
private static final String DB_TABLE_NAME = "testitem";
private final JdbcBaseDAO jdbcBaseDAO = new JdbcBaseDAO();
private @NonNullByDefault({}) FilterCriteria filter;
@BeforeEach
public void setup() {
filter = new FilterCriteria();
}
@Test
public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseDescendingOrder() {
String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time DESC"));
}
@Test
public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseAscendingOrder() {
filter.setOrdering(Ordering.ASCENDING);
String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time ASC"));
}
@Test
public void testHistItemFilterQueryProviderWithStartAndEndDateReturnsDeleteQueryWithWhereClauseDescendingOrder() {
filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " WHERE TIME>'" //
+ JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
+ " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "' ORDER BY time DESC"));
}
@Test
public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseDescendingOrderAndLimit() {
filter.setPageSize(1);
String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time DESC LIMIT 0,1"));
}
@Test
public void testHistItemFilterDeleteProviderReturnsDeleteQueryWithoutWhereClause() {
String sql = jdbcBaseDAO.histItemFilterDeleteProvider(filter, DB_TABLE_NAME, UTC_ZONE_ID);
assertThat(sql, is("DELETE FROM " + DB_TABLE_NAME));
}
@Test
public void testHistItemFilterDeleteProviderWithStartAndEndDateReturnsDeleteQueryWithWhereClause() {
filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
String sql = jdbcBaseDAO.histItemFilterDeleteProvider(filter, DB_TABLE_NAME, UTC_ZONE_ID);
assertThat(sql, is("DELETE FROM " + DB_TABLE_NAME + " WHERE TIME>'" //
+ JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
+ " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
}
@Test
public void testResolveTimeFilterWithNoDatesReturnsEmptyString() {
String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
assertThat(sql, is(""));
}
@Test
public void testResolveTimeFilterWithStartDateOnlyReturnsWhereClause() {
filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
assertThat(sql, is(" WHERE TIME>'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'"));
}
@Test
public void testResolveTimeFilterWithEndDateOnlyReturnsWhereClause() {
filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
assertThat(sql, is(" WHERE TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
}
@Test
public void testResolveTimeFilterWithStartAndEndDateReturnsWhereClauseWithTwoConditions() {
filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
assertThat(sql, is(" WHERE TIME>'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
+ " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
}
private ZonedDateTime parseDateTimeString(String dts) {
return ZonedDateTime.of(LocalDateTime.parse(dts, DATE_PARSER), UTC_ZONE_ID);
}
}

View File

@ -0,0 +1,51 @@
/**
* 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.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.persistence.FilterCriteria;
/**
* Tests the {@link JdbcPersistenceService}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class JdbcPersistenceServiceTest {
private final JdbcPersistenceService jdbcPersistenceService = new JdbcPersistenceService(mock(ItemRegistry.class),
mock(TimeZoneProvider.class)) {
@Override
protected boolean checkDBAccessability() {
return true;
}
};
private @NonNullByDefault({}) FilterCriteria filter;
@BeforeEach
public void setup() {
filter = new FilterCriteria();
}
@Test
void removeThrowsIllegalArgumentExceptionIfItemNameOfFilterIsNull() {
assertThrows(IllegalArgumentException.class, () -> jdbcPersistenceService.remove(filter));
}
}