2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.persistence.jdbc.internal;
15 import java.time.ZonedDateTime;
16 import java.util.Date;
17 import java.util.List;
18 import java.util.Locale;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.core.config.core.ConfigurableService;
25 import org.openhab.core.i18n.TimeZoneProvider;
26 import org.openhab.core.items.GroupItem;
27 import org.openhab.core.items.Item;
28 import org.openhab.core.items.ItemNotFoundException;
29 import org.openhab.core.items.ItemRegistry;
30 import org.openhab.core.persistence.FilterCriteria;
31 import org.openhab.core.persistence.HistoricItem;
32 import org.openhab.core.persistence.ModifiablePersistenceService;
33 import org.openhab.core.persistence.PersistenceItemInfo;
34 import org.openhab.core.persistence.PersistenceService;
35 import org.openhab.core.persistence.QueryablePersistenceService;
36 import org.openhab.core.persistence.strategy.PersistenceStrategy;
37 import org.openhab.core.types.State;
38 import org.openhab.core.types.UnDefType;
39 import org.osgi.framework.BundleContext;
40 import org.osgi.framework.Constants;
41 import org.osgi.service.component.annotations.Activate;
42 import org.osgi.service.component.annotations.Component;
43 import org.osgi.service.component.annotations.Deactivate;
44 import org.osgi.service.component.annotations.Reference;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * This is the implementation of the JDBC {@link PersistenceService}.
51 * @author Helmut Lehmeyer - Initial contribution
52 * @author Kai Kreuzer - Migration to 3.x
55 @Component(service = { PersistenceService.class,
56 QueryablePersistenceService.class }, configurationPid = "org.openhab.jdbc", //
57 property = Constants.SERVICE_PID + "=org.openhab.jdbc")
58 @ConfigurableService(category = "persistence", label = "JDBC Persistence Service", description_uri = JdbcPersistenceService.CONFIG_URI)
59 public class JdbcPersistenceService extends JdbcMapper implements ModifiablePersistenceService {
61 private static final String SERVICE_ID = "jdbc";
62 private static final String SERVICE_LABEL = "JDBC";
63 protected static final String CONFIG_URI = "persistence:jdbc";
65 private final Logger logger = LoggerFactory.getLogger(JdbcPersistenceService.class);
67 private final ItemRegistry itemRegistry;
70 public JdbcPersistenceService(final @Reference ItemRegistry itemRegistry,
71 final @Reference TimeZoneProvider timeZoneProvider) {
72 super(timeZoneProvider);
73 this.itemRegistry = itemRegistry;
77 * Called by the SCR to activate the component with its configuration read
80 * @param bundleContext
81 * BundleContext of the Bundle that defines this component
82 * @param configuration
83 * Configuration properties for this component obtained from the
87 public void activate(BundleContext bundleContext, Map<Object, Object> configuration) {
88 logger.debug("JDBC::activate: persistence service activated");
89 updateConfig(configuration);
93 * Called by the SCR to deactivate the component when either the
94 * configuration is removed or mandatory references are no longer satisfied
95 * or the component has simply been stopped.
98 * Reason code for the deactivation:<br>
100 * <li>0 – Unspecified
101 * <li>1 – The component was disabled
102 * <li>2 – A reference became unsatisfied
103 * <li>3 – A configuration was changed
104 * <li>4 – A configuration was deleted
105 * <li>5 – The component was disposed
106 * <li>6 – The bundle was stopped
110 public void deactivate(final int reason) {
111 logger.debug("JDBC::deactivate: persistence bundle stopping. Disconnecting from database. reason={}", reason);
112 // closeConnection();
117 public String getId() {
118 logger.debug("JDBC::getName: returning name 'jdbc' for queryable persistence service.");
123 public String getLabel(@Nullable Locale locale) {
124 return SERVICE_LABEL;
128 public void store(Item item) {
129 internalStore(item, null, item.getState());
133 public void store(Item item, @Nullable String alias) {
134 // alias is not supported
135 internalStore(item, null, item.getState());
139 public void store(Item item, ZonedDateTime date, State state) {
140 internalStore(item, date, state);
143 private void internalStore(Item item, @Nullable ZonedDateTime date, State state) {
144 // Do not store undefined/uninitialized data
145 if (state instanceof UnDefType) {
146 logger.debug("JDBC::store: ignore Item '{}' because it is UnDefType", item.getName());
149 if (!checkDBAccessability()) {
151 "JDBC::store: No connection to database. Cannot persist state '{}' for item '{}'! Will retry connecting to database when error count:{} equals errReconnectThreshold:{}",
152 state, item, errCnt, conf.getErrReconnectThreshold());
155 long timerStart = System.currentTimeMillis();
156 storeItemValue(item, state, date);
157 if (logger.isDebugEnabled()) {
158 logger.debug("JDBC: Stored item '{}' as '{}' in SQL database at {} in {} ms.", item.getName(), state,
159 new Date(), System.currentTimeMillis() - timerStart);
164 public Set<PersistenceItemInfo> getItemInfo() {
169 * Queries the {@link PersistenceService} for data with a given filter
173 * the filter to apply to the query
174 * @return a time series of items
177 public Iterable<HistoricItem> query(FilterCriteria filter) {
178 if (!checkDBAccessability()) {
179 logger.warn("JDBC::query: database not connected, query aborted for item '{}'", filter.getItemName());
183 // Get the item name from the filter
184 // Also get the Item object so we can determine the type
186 String itemName = filter.getItemName();
187 logger.debug("JDBC::query: item is {}", itemName);
189 item = itemRegistry.getItem(itemName);
190 } catch (ItemNotFoundException e1) {
191 logger.error("JDBC::query: unable to get item for itemName: '{}'. Ignore and give up!", itemName);
195 if (item instanceof GroupItem) {
196 // For Group Item is BaseItem needed to get correct Type of Value.
197 item = GroupItem.class.cast(item).getBaseItem();
198 logger.debug("JDBC::query: item is instanceof GroupItem '{}'", itemName);
200 logger.debug("JDBC::query: BaseItem of GroupItem is null. Ignore and give up!");
203 if (item instanceof GroupItem) {
204 logger.debug("JDBC::query: BaseItem of GroupItem is a GroupItem too. Ignore and give up!");
209 String table = sqlTables.get(itemName);
211 logger.debug("JDBC::query: unable to find table for item with name: '{}', no data in database.", itemName);
215 long timerStart = System.currentTimeMillis();
216 List<HistoricItem> items = getHistItemFilterQuery(filter, conf.getNumberDecimalcount(), table, item);
217 if (logger.isDebugEnabled()) {
218 logger.debug("JDBC: Query for item '{}' returned {} rows in {} ms", itemName, items.size(),
219 System.currentTimeMillis() - timerStart);
227 public void updateConfig(Map<Object, Object> configuration) {
228 logger.debug("JDBC::updateConfig");
230 conf = new JdbcConfiguration(configuration);
231 if (conf.valid && checkDBAccessability()) {
233 // connection has been established ... initialization completed!
239 logger.debug("JDBC::updateConfig: configuration complete for service={}.", getId());
243 public List<PersistenceStrategy> getDefaultStrategies() {
244 return List.of(PersistenceStrategy.Globals.CHANGE);
248 public boolean remove(FilterCriteria filter) throws IllegalArgumentException {
249 if (!checkDBAccessability()) {
250 logger.warn("JDBC::remove: database not connected, remove aborted for item '{}'", filter.getItemName());
254 // Get the item name from the filter
255 // Also get the Item object so we can determine the type
257 String itemName = filter.getItemName();
258 logger.debug("JDBC::remove: item is {}", itemName);
259 if (itemName == null) {
260 throw new IllegalArgumentException("Item name must not be null");
263 item = itemRegistry.getItem(itemName);
264 } catch (ItemNotFoundException e) {
265 logger.error("JDBC::remove: unable to get item for itemName: '{}'. Ignore and give up!", itemName);
269 String table = sqlTables.get(itemName);
271 logger.debug("JDBC::remove: unable to find table for item with name: '{}', no data in database.", itemName);
275 long timerStart = System.currentTimeMillis();
276 boolean result = deleteItemValues(filter, table, item);
277 if (logger.isDebugEnabled()) {
278 logger.debug("JDBC: Deleted values for item '{}' in SQL database at {} in {} ms.", item.getName(),
279 new Date(), System.currentTimeMillis() - timerStart);