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.jpa.internal;
15 import java.util.Collections;
16 import java.util.Date;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Locale;
23 import javax.persistence.EntityManager;
24 import javax.persistence.EntityManagerFactory;
25 import javax.persistence.Persistence;
26 import javax.persistence.Query;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.core.items.Item;
31 import org.openhab.core.items.ItemNotFoundException;
32 import org.openhab.core.items.ItemRegistry;
33 import org.openhab.core.persistence.FilterCriteria;
34 import org.openhab.core.persistence.FilterCriteria.Ordering;
35 import org.openhab.core.persistence.HistoricItem;
36 import org.openhab.core.persistence.PersistenceItemInfo;
37 import org.openhab.core.persistence.PersistenceService;
38 import org.openhab.core.persistence.QueryablePersistenceService;
39 import org.openhab.core.persistence.strategy.PersistenceStrategy;
40 import org.openhab.core.types.UnDefType;
41 import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
42 import org.osgi.framework.BundleContext;
43 import org.osgi.service.component.annotations.Activate;
44 import org.osgi.service.component.annotations.Component;
45 import org.osgi.service.component.annotations.ConfigurationPolicy;
46 import org.osgi.service.component.annotations.Deactivate;
47 import org.osgi.service.component.annotations.Reference;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * JPA based implementation of QueryablePersistenceService.
54 * @author Manfred Bergmann - Initial contribution
57 @Component(service = { PersistenceService.class,
58 QueryablePersistenceService.class }, configurationPid = "org.openhab.jpa", configurationPolicy = ConfigurationPolicy.REQUIRE)
59 public class JpaPersistenceService implements QueryablePersistenceService {
60 private final Logger logger = LoggerFactory.getLogger(JpaPersistenceService.class);
62 private final ItemRegistry itemRegistry;
64 private @Nullable EntityManagerFactory emf = null;
66 private @NonNullByDefault({}) JpaConfiguration config;
69 public JpaPersistenceService(final @Reference ItemRegistry itemRegistry) {
70 this.itemRegistry = itemRegistry;
74 * lazy loading because update() is called after activate()
76 * @return EntityManagerFactory
78 protected @Nullable EntityManagerFactory getEntityManagerFactory() {
80 emf = newEntityManagerFactory();
86 public void activate(BundleContext context, Map<String, Object> properties) {
87 logger.debug("Activating jpa persistence service");
88 config = new JpaConfiguration(properties);
92 * Closes the EntityPersistenceFactory
95 public void deactivate() {
96 logger.debug("Deactivating jpa persistence service");
97 closeEntityManagerFactory();
101 public String getId() {
106 public String getLabel(@Nullable Locale locale) {
111 public void store(Item item) {
116 public void store(Item item, @Nullable String alias) {
117 logger.debug("Storing item: {}", item.getName());
119 if (item.getState() instanceof UnDefType) {
120 logger.debug("This item is of undefined type. Cannot persist it!");
124 if (!JpaConfiguration.isInitialized) {
125 logger.debug("Trying to create EntityManagerFactory but we don't have configuration yet!");
129 // determine item name to be stored
130 String name = (alias != null) ? alias : item.getName();
132 JpaPersistentItem pItem = new JpaPersistentItem();
134 String newValue = StateHelper.toString(item.getState());
135 pItem.setValue(newValue);
136 logger.debug("Stored new value: {}", newValue);
137 } catch (Exception e1) {
138 logger.error("Error on converting state value to string: {}", e1.getMessage());
142 pItem.setRealName(item.getName());
143 pItem.setTimestamp(new Date());
145 EntityManager em = getEntityManagerFactory().createEntityManager();
147 logger.debug("Persisting item...");
148 // In RESOURCE_LOCAL calls to EntityManager require a begin/commit
149 em.getTransaction().begin();
151 em.getTransaction().commit();
152 logger.debug("Persisting item...done");
153 } catch (Exception e) {
154 logger.error("Error on persisting item! Rolling back!", e);
155 em.getTransaction().rollback();
160 logger.debug("Storing item...done");
164 public Set<PersistenceItemInfo> getItemInfo() {
165 return Collections.emptySet();
169 public Iterable<HistoricItem> query(FilterCriteria filter) {
170 logger.debug("Querying for historic item: {}", filter.getItemName());
172 if (!JpaConfiguration.isInitialized) {
173 logger.warn("Trying to create EntityManagerFactory but we don't have configuration yet!");
174 return Collections.emptyList();
177 String itemName = filter.getItemName();
178 Item item = getItemFromRegistry(itemName);
181 if (filter.getOrdering() == Ordering.ASCENDING) {
187 boolean hasBeginDate = false;
188 boolean hasEndDate = false;
189 String queryString = "SELECT n FROM " + JpaPersistentItem.class.getSimpleName()
190 + " n WHERE n.realName = :itemName";
191 if (filter.getBeginDate() != null) {
192 queryString += " AND n.timestamp >= :beginDate";
195 if (filter.getEndDate() != null) {
196 queryString += " AND n.timestamp <= :endDate";
199 queryString += " ORDER BY n.timestamp " + sortOrder;
201 logger.debug("The query: {}", queryString);
203 EntityManager em = getEntityManagerFactory().createEntityManager();
205 // In RESOURCE_LOCAL calls to EntityManager require a begin/commit
206 em.getTransaction().begin();
208 logger.debug("Creating query...");
209 Query query = em.createQuery(queryString);
210 query.setParameter("itemName", item.getName());
212 query.setParameter("beginDate", Date.from(filter.getBeginDate().toInstant()));
215 query.setParameter("endDate", Date.from(filter.getEndDate().toInstant()));
218 query.setFirstResult(filter.getPageNumber() * filter.getPageSize());
219 query.setMaxResults(filter.getPageSize());
220 logger.debug("Creating query...done");
222 logger.debug("Retrieving result list...");
223 @SuppressWarnings("unchecked")
224 List<JpaPersistentItem> result = query.getResultList();
225 logger.debug("Retrieving result list...done");
227 List<HistoricItem> historicList = JpaHistoricItem.fromResultList(result, item);
228 logger.debug("{}", String.format("Convert to HistoricItem: %d", historicList.size()));
230 em.getTransaction().commit();
233 } catch (Exception e) {
234 logger.error("Error on querying database!", e);
235 em.getTransaction().rollback();
240 return Collections.emptyList();
244 * Creates a new EntityManagerFactory with properties read from openhab.cfg via JpaConfiguration.
246 * @return initialized EntityManagerFactory
248 protected EntityManagerFactory newEntityManagerFactory() {
249 logger.trace("Creating EntityManagerFactory...");
251 Map<String, String> properties = new HashMap<>();
252 properties.put("javax.persistence.jdbc.url", config.dbConnectionUrl);
253 properties.put("javax.persistence.jdbc.driver", config.dbDriverClass);
254 if (config.dbUserName != null) {
255 properties.put("javax.persistence.jdbc.user", config.dbUserName);
257 if (config.dbPassword != null) {
258 properties.put("javax.persistence.jdbc.password", config.dbPassword);
260 if (config.dbUserName != null && config.dbPassword == null) {
261 logger.warn("JPA persistence - it is recommended to use a password to protect data store");
263 if (config.dbSyncMapping != null && !config.dbSyncMapping.isBlank()) {
264 logger.warn("You are settings openjpa.jdbc.SynchronizeMappings, I hope you know what you're doing!");
265 properties.put("openjpa.jdbc.SynchronizeMappings", config.dbSyncMapping);
268 EntityManagerFactory fac = Persistence.createEntityManagerFactory(getPersistenceUnitName(), properties);
269 logger.debug("Creating EntityManagerFactory...done");
275 * Closes EntityManagerFactory
277 protected void closeEntityManagerFactory() {
282 logger.debug("Closing down entity objects...done");
286 * Checks if EntityManagerFactory is open
288 * @return true when open, false otherwise
290 protected boolean isEntityManagerFactoryOpen() {
291 return emf != null && emf.isOpen();
295 * Return the persistence unit as in persistence.xml file.
297 * @return the persistence unit name
299 protected String getPersistenceUnitName() {
304 * Retrieves the item for the given name from the item registry
309 private @Nullable Item getItemFromRegistry(String itemName) {
311 return itemRegistry.getItem(itemName);
312 } catch (ItemNotFoundException e1) {
313 logger.error("Unable to get item type for {}", itemName);
319 public List<PersistenceStrategy> getDefaultStrategies() {
320 return Collections.emptyList();