import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.stream.Collectors;
+import javax.measure.Quantity;
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.Nullable;
import org.knowm.yank.Yank;
import org.openhab.core.items.GroupItem;
import org.openhab.core.items.Item;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.Units;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.FilterCriteria.Ordering;
import org.openhab.core.persistence.HistoricItem;
String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone);
logger.debug("JDBC::doGetHistItemFilterQuery sql={}", sql);
List<Object[]> m = Yank.queryObjectArrays(sql, null);
-
- List<HistoricItem> items = new ArrayList<>();
- for (int i = 0; i < m.size(); i++) {
- items.add(new JdbcHistoricItem(item.getName(), getState(item, m.get(i)[1]), objectAsDate(m.get(i)[0])));
- }
- return items;
+ // we already retrieve the unit here once as it is a very costly operation
+ String itemName = item.getName();
+ Unit<? extends Quantity<?>> unit = item instanceof NumberItem ? ((NumberItem) item).getUnit() : null;
+ return m.stream().map(o -> new JdbcHistoricItem(itemName, getState(item, unit, o[1]), objectAsDate(o[0])))
+ .collect(Collectors.<HistoricItem> toList());
}
/*************
*************/
static final DateTimeFormatter JDBC_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
- private String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
+ protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
String simpleName, ZoneId timeZone) {
logger.debug(
"JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
- filter.toString(), numberDecimalcount, table, simpleName);
+ filter, numberDecimalcount, table, simpleName);
String filterString = "";
if (filter.getBeginDate() != null) {
// insertItemValue
logger.debug("JDBC::storeItemValueProvider: getState: '{}'", item.getState());
- if ("COLORITEM".equals(itemType)) {
- vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
- vo.setValue(item.getState().toString());
- } else if ("NUMBERITEM".equals(itemType)) {
- String it = getSqlTypes().get(itemType);
- if (it.toUpperCase().contains("DOUBLE")) {
- vo.setValueTypes(it, java.lang.Double.class);
- Number newVal = (Number) item.getState();
- logger.debug("JDBC::storeItemValueProvider: newVal.doubleValue: '{}'", newVal.doubleValue());
- vo.setValue(newVal.doubleValue());
- } else if (it.toUpperCase().contains("DECIMAL") || it.toUpperCase().contains("NUMERIC")) {
- vo.setValueTypes(it, java.math.BigDecimal.class);
- BigDecimal newVal = BigDecimal.valueOf(((Number) item.getState()).doubleValue());
- logger.debug("JDBC::storeItemValueProvider: newVal.toBigDecimal: '{}'", newVal);
- vo.setValue(newVal);
- } else if (it.toUpperCase().contains("INT")) {
- vo.setValueTypes(it, java.lang.Integer.class);
- Number newVal = (Number) item.getState();
- logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", newVal.intValue());
- vo.setValue(newVal.intValue());
- } else {// fall back to String
- vo.setValueTypes(it, java.lang.String.class);
- logger.warn("JDBC::storeItemValueProvider: item.getState().toString(): '{}'", item.getState());
+ /*
+ * !!ATTENTION!!
+ *
+ * 1. DimmerItem.getStateAs(PercentType.class).toString() always
+ * returns 0
+ * RollershutterItem.getStateAs(PercentType.class).toString() works
+ * as expected
+ *
+ * 2. (item instanceof ColorItem) == (item instanceof DimmerItem) =
+ * true Therefore for instance tests ColorItem always has to be
+ * tested before DimmerItem
+ *
+ * !!ATTENTION!!
+ */
+ switch (itemType) {
+ case "COLORITEM":
+ vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
vo.setValue(item.getState().toString());
- }
- } else if ("ROLLERSHUTTERITEM".equals(itemType) || "DIMMERITEM".equals(itemType)) {
- vo.setValueTypes(getSqlTypes().get(itemType), java.lang.Integer.class);
- Number newVal = (DecimalType) item.getState();
- logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", newVal.intValue());
- vo.setValue(newVal.intValue());
- } else if ("DATETIMEITEM".equals(itemType)) {
- vo.setValueTypes(getSqlTypes().get(itemType), java.sql.Timestamp.class);
- java.sql.Timestamp d = new java.sql.Timestamp(
- ((DateTimeType) item.getState()).getZonedDateTime().toInstant().toEpochMilli());
- logger.debug("JDBC::storeItemValueProvider: DateTimeItem: '{}'", d);
- vo.setValue(d);
- } else {
- /*
- * !!ATTENTION!!
- *
- * 1. DimmerItem.getStateAs(PercentType.class).toString() always
- * returns 0
- * RollershutterItem.getStateAs(PercentType.class).toString() works
- * as expected
- *
- * 2. (item instanceof ColorItem) == (item instanceof DimmerItem) =
- * true Therefore for instance tests ColorItem always has to be
- * tested before DimmerItem
- *
- * !!ATTENTION!!
- */
- // 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());
+ break;
+ case "NUMBERITEM":
+ State state = item.getState();
+ State convertedState = state;
+ if (item instanceof NumberItem && state instanceof QuantityType) {
+ Unit<? extends Quantity<?>> unit = ((NumberItem) item).getUnit();
+ if (unit != null && !Units.ONE.equals(unit)) {
+ convertedState = ((QuantityType<?>) state).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;
+ }
+ }
+ }
+ String it = getSqlTypes().get(itemType);
+ if (it.toUpperCase().contains("DOUBLE")) {
+ vo.setValueTypes(it, java.lang.Double.class);
+ double value = ((Number) convertedState).doubleValue();
+ logger.debug("JDBC::storeItemValueProvider: newVal.doubleValue: '{}'", value);
+ vo.setValue(value);
+ } else if (it.toUpperCase().contains("DECIMAL") || it.toUpperCase().contains("NUMERIC")) {
+ vo.setValueTypes(it, java.math.BigDecimal.class);
+ BigDecimal value = BigDecimal.valueOf(((Number) convertedState).doubleValue());
+ logger.debug("JDBC::storeItemValueProvider: newVal.toBigDecimal: '{}'", value);
+ vo.setValue(value);
+ } else if (it.toUpperCase().contains("INT")) {
+ vo.setValueTypes(it, java.lang.Integer.class);
+ int value = ((Number) convertedState).intValue();
+ logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", value);
+ vo.setValue(value);
+ } else {// fall back to String
+ vo.setValueTypes(it, java.lang.String.class);
+ logger.warn("JDBC::storeItemValueProvider: item.getState().toString(): '{}'", 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();
+ 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());
+ logger.debug("JDBC::storeItemValueProvider: DateTimeItem: '{}'", d);
+ vo.setValue(d);
+ break;
+ 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());
+ break;
}
return vo;
}
/*****************
* H E L P E R S *
*****************/
- protected State getState(Item item, Object v) {
- String clazz = v.getClass().getSimpleName();
- logger.debug("JDBC::ItemResultHandler::handleResult getState value = '{}', getClass = '{}', clazz = '{}'",
- v.toString(), v.getClass(), clazz);
+ protected State getState(Item item, @Nullable Unit<? extends Quantity<?>> unit, Object v) {
+ logger.debug(
+ "JDBC::ItemResultHandler::handleResult getState value = '{}', unit = '{}', getClass = '{}', clazz = '{}'",
+ v, unit, v.getClass(), v.getClass().getSimpleName());
if (item instanceof NumberItem) {
String it = getSqlTypes().get("NUMBERITEM");
if (it.toUpperCase().contains("DOUBLE")) {
- return new DecimalType(((Number) v).doubleValue());
+ return unit == null ? new DecimalType(((Number) v).doubleValue())
+ : QuantityType.valueOf(((Number) v).doubleValue(), unit);
} else if (it.toUpperCase().contains("DECIMAL") || it.toUpperCase().contains("NUMERIC")) {
- return new DecimalType((BigDecimal) v);
+ return unit == null ? new DecimalType((BigDecimal) v)
+ : QuantityType.valueOf(((BigDecimal) v).doubleValue(), unit);
} else if (it.toUpperCase().contains("INT")) {
- return new DecimalType(((Integer) v).intValue());
+ return unit == null ? new DecimalType(((Integer) v).intValue())
+ : QuantityType.valueOf(((Integer) v).doubleValue(), unit);
}
- return DecimalType.valueOf(((String) v).toString());
+ return unit == null ? DecimalType.valueOf(((String) v).toString())
+ : QuantityType.valueOf(((String) v).toString());
} else if (item instanceof ColorItem) {
return HSBType.valueOf(((String) v).toString());
} else if (item instanceof DimmerItem) {
if (i instanceof GroupItem) {
item = ((GroupItem) i).getBaseItem();
if (item == null) {
- // if GroupItem:<ItemType> is not defined in
- // *.items using StringType
- // logger.debug("JDBC: BaseItem GroupItem:<ItemType> is not
- // defined in *.items searching for first Member and try to use
- // as ItemType");
+ // if GroupItem:<ItemType> is not defined in *.items using StringType
logger.debug(
"JDBC::getItemType: Cannot detect ItemType for {} because the GroupItems' base type isn't set in *.items File.",
i.getName());
package org.openhab.persistence.jdbc.db;
import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.measure.Quantity;
+import javax.measure.Unit;
import org.knowm.yank.Yank;
import org.openhab.core.items.Item;
+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;
String table, String name, ZoneId timeZone) {
String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone);
List<Object[]> m = Yank.queryObjectArrays(sql, null);
-
logger.debug("JDBC::doGetHistItemFilterQuery got Array length={}", m.size());
-
- List<HistoricItem> items = new ArrayList<>();
- for (int i = 0; i < m.size(); i++) {
- logger.debug("JDBC::doGetHistItemFilterQuery 0='{}' 1='{}'", m.get(i)[0], m.get(i)[1]);
- items.add(new JdbcHistoricItem(item.getName(), getState(item, m.get(i)[1]), objectAsDate(m.get(i)[0])));
- }
- return items;
+ // we already retrieve the unit here once as it is a very costly operation
+ String itemName = item.getName();
+ Unit<? extends Quantity<?>> unit = item instanceof NumberItem ? ((NumberItem) item).getUnit() : null;
+ return m.stream().map(o -> {
+ logger.debug("JDBC::doGetHistItemFilterQuery 0='{}' 1='{}'", o[0], o[1]);
+ return new JdbcHistoricItem(itemName, getState(item, unit, o[1]), objectAsDate(o[0]));
+ }).collect(Collectors.<HistoricItem> toList());
}
/****************************
* SQL generation Providers *
****************************/
- static final DateTimeFormatter JDBC_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
- private String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
+ @Override
+ protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
String simpleName, ZoneId timeZone) {
logger.debug(
"JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
}
public Item storeItemValue(Item item) {
- logger.debug("JDBC::storeItemValue: item={}", item.toString());
+ logger.debug("JDBC::storeItemValue: item={}", item);
String tableName = getTable(item);
if (tableName == null) {
logger.error("JDBC::store: Unable to store item '{}'.", item.getName());
(filter != null), numberDecimalcount, table, item, item.getName());
if (table != null) {
long timerStart = System.currentTimeMillis();
- List<HistoricItem> r = conf.getDBDAO().doGetHistItemFilterQuery(item, filter, numberDecimalcount, table,
- item.getName(), timeZoneProvider.getTimeZone());
- logTime("insertItemValue", timerStart, System.currentTimeMillis());
- return r;
+ List<HistoricItem> result = conf.getDBDAO().doGetHistItemFilterQuery(item, filter, numberDecimalcount,
+ table, item.getName(), timeZoneProvider.getTimeZone());
+ logTime("getHistItemFilterQuery", timerStart, System.currentTimeMillis());
+ errCnt = 0;
+ return result;
} else {
logger.error("JDBC::getHistItemFilterQuery: TABLE is NULL; cannot get data from non-existent table.");
}
logger.info(
"JDBC::checkDBSchema: Rebuild complete, configure the 'rebuildTableNames' setting to 'false' to stop rebuilds on startup");
} else {
- List<ItemsVO> al;
// Reset the error counter
errCnt = 0;
- al = getItemIDTableNames();
- for (int i = 0; i < al.size(); i++) {
- String t = getTableName(al.get(i).getItemid(), al.get(i).getItemname());
- sqlTables.put(al.get(i).getItemname(), t);
+ for (ItemsVO vo : getItemIDTableNames()) {
+ sqlTables.put(vo.getItemname(), getTableName(vo.getItemid(), vo.getItemname()));
}
}
}
initialized = false;
}
- List<ItemsVO> al;
Map<Integer, String> tableIds = new HashMap<>();
//
- al = getItemIDTableNames();
- for (int i = 0; i < al.size(); i++) {
- String t = getTableName(al.get(i).getItemid(), al.get(i).getItemname());
- sqlTables.put(al.get(i).getItemname(), t);
- tableIds.put(al.get(i).getItemid(), t);
+ for (ItemsVO vo : getItemIDTableNames()) {
+ String t = getTableName(vo.getItemid(), vo.getItemname());
+ sqlTables.put(vo.getItemname(), t);
+ tableIds.put(vo.getItemid(), t);
}
//
- al = getItemTables();
+ List<ItemsVO> al = getItemTables();
String oldName = "";
String newName = "";
// 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
return sqlTables.keySet().stream().map(itemName -> new JdbcPersistenceItemInfo(itemName))
- .collect(Collectors.<PersistenceItemInfo> toUnmodifiableSet());
+ .collect(Collectors.<PersistenceItemInfo> toSet());
}
private static String formatRight(final Object value, final int len) {