mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[jpa] Add dynamic import, upgrade dependencies, add UI config (#13516)
* Adds a dynamic package import so JDBC drivers on the classpath can be used * Upgrades OpenJPA from 2.4.0 to 3.2.2 * Upgrades Derby JDBC driver from 10.11.1.1 to 10.16.1.1 * Adds config.xml and ConfigurableService annotation so add-on can be configured using the UI * Adds null annotations on all classes * Prevent NPEs and some code cleanup See also: * https://openjpa.apache.org/builds/3.2.2/apache-openjpa/RELEASE-NOTES.html * https://community.openhab.org/t/jpa-with-mysql-or-mariadb/138679 Fixes #13375 Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
parent
d18322f860
commit
0306f4508f
@ -6,7 +6,7 @@ The service uses an abstraction layer that theoretically allows it to support ma
|
|||||||
It will create one table named `historic_item` where all item states are stored.
|
It will create one table named `historic_item` where all item states are stored.
|
||||||
The item state is stored in a string representation.
|
The item state is stored in a string representation.
|
||||||
|
|
||||||
The service currently supports MySQL, Apache Derby and PostgreSQL databases.
|
The service currently supports Apache Derby, MariaDB, MySQL and PostgreSQL databases.
|
||||||
Only the embedded Apache Derby database driver is included.
|
Only the embedded Apache Derby database driver is included.
|
||||||
Other drivers must be installed manually.
|
Other drivers must be installed manually.
|
||||||
(See below for more information on that.)
|
(See below for more information on that.)
|
||||||
@ -15,12 +15,13 @@ Other drivers must be installed manually.
|
|||||||
|
|
||||||
This service can be configured in the file `services/jpa.cfg`.
|
This service can be configured in the file `services/jpa.cfg`.
|
||||||
|
|
||||||
| Property | Default | Required | Description |
|
| Property | Default | Required | Description |
|
||||||
| -------- | ------- | :-------: | ------------------------------------------------------------ |
|
| ------------ | ------- | :-------: | ------------------------------------------------------------ |
|
||||||
| url | | Yes | JDBC connection URL. Examples:<br/><br/>`jdbc:postgresql://hab.local:5432/openhab`<br/>`jdbc:derby://hab.local:1527/openhab;create=true`<br/>`jdbc:mysql://localhost:3306/openhab` |
|
| url | | Yes | JDBC connection URL. Examples:<br/><br/>`jdbc:derby://hab.local:1527/openhab;create=true`<br/>`jdbc:mariadb://localhost:3306/openhab`<br/>`jdbc:mysql://localhost:3306/openhab`<br/>`jdbc:postgresql://hab.local:5432/openhab` |
|
||||||
| driver | | Yes | database driver. Examples:<br/><br/>`org.postgresql.Driver`<br/>`org.apache.derby.jdbc.ClientDriver`<br/>`com.mysql.jdbc.Driver`<br/></br>Only the Apache Derby driver is included with the service. Drivers for other databases must be installed manually. This is a trivial process. Normally JDBC database drivers are packaged as OSGi bundles and can just be dropped into the `addons` folder. This has the advantage that users can update their drivers as needed. The following database drivers are known to work:<br/><br/>`postgresql-9.4-1203-jdbc41.jar`<br/>`postgresql-9.4-1206-jdbc41.jar` |
|
| driver | | Yes | database driver. Examples:<br/><br/>`com.mysql.jdbc.Driver`<br/>`org.apache.derby.jdbc.ClientDriver``org.mariadb.jdbc.Driver`<br/><br/>`org.postgresql.Driver`<br/></br>Only the Apache Derby driver is included with the service. Drivers for other databases must be installed manually. This is a trivial process. Normally JDBC database drivers are packaged as OSGi bundles and can just be dropped into the `addons` folder. This has the advantage that users can update their drivers as needed. The following database drivers are known to work:<br/><br/>`postgresql-9.4-1203-jdbc41.jar`<br/>`postgresql-9.4-1206-jdbc41.jar` |
|
||||||
| user | | if needed | database user name for connection |
|
| user | | if needed | database user name for connection |
|
||||||
| password | | if needed | database user password for connection |
|
| password | | if needed | database user password for connection |
|
||||||
|
| syncmappings | | if needed | The OpenJPA synchronize mappings configuration |
|
||||||
|
|
||||||
## Adding support for other JPA supported databases
|
## Adding support for other JPA supported databases
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
<name>openHAB Add-ons :: Bundles :: Persistence Service :: JPA</name>
|
<name>openHAB Add-ons :: Bundles :: Persistence Service :: JPA</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<bnd.importpackage>!com.ibm.*,!com.sun.*,!oracle.*,!org.apache.bval.*,!org.apache.geronimo.*,!org.apache.avalon.*,!org.apache.log,!org.apache.tools.*,!org.apache.xerces.*,!org.jboss.*,!org.postgresql.*,!org.slf4j.impl,!weblogic.*,!javax.rmi</bnd.importpackage>
|
<bnd.importpackage>!com.ibm.*,!com.sun.*,!oracle.*,!javax.interceptor.*,!javax.enterprise.*,!javax.rmi,!org.apache.bval.*,!net.sf.cglib.*,!org.apache.commons.beanutils.*,!org.apache.geronimo.*,!org.apache.avalon.*,!org.apache.log,!org.apache.tools.*,!org.apache.xerces.*,!org.jboss.*,!org.postgresql.*,!org.slf4j.impl,!weblogic.*</bnd.importpackage>
|
||||||
|
<openjpa.version>3.2.2</openjpa.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -23,13 +24,13 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.openjpa</groupId>
|
<groupId>org.apache.openjpa</groupId>
|
||||||
<artifactId>openjpa-all</artifactId>
|
<artifactId>openjpa-all</artifactId>
|
||||||
<version>2.4.0</version>
|
<version>${openjpa.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.apache.derby/derby -->
|
<!-- https://mvnrepository.com/artifact/org.apache.derby/derby -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.derby</groupId>
|
<groupId>org.apache.derby</groupId>
|
||||||
<artifactId>derby</artifactId>
|
<artifactId>derby</artifactId>
|
||||||
<version>10.11.1.1</version>
|
<version>10.16.1.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@ -39,7 +40,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.openjpa</groupId>
|
<groupId>org.apache.openjpa</groupId>
|
||||||
<artifactId>openjpa-maven-plugin</artifactId>
|
<artifactId>openjpa-maven-plugin</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>${openjpa.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<excludes>org/apache/bval/**</excludes>
|
<excludes>org/apache/bval/**</excludes>
|
||||||
<includes>**/model/*.class</includes>
|
<includes>**/model/*.class</includes>
|
||||||
@ -51,7 +52,7 @@
|
|||||||
<groupId>org.apache.openjpa</groupId>
|
<groupId>org.apache.openjpa</groupId>
|
||||||
<artifactId>openjpa</artifactId>
|
<artifactId>openjpa</artifactId>
|
||||||
<!-- set the version to be the same as the level in your runtime -->
|
<!-- set the version to be the same as the level in your runtime -->
|
||||||
<version>3.1.0</version>
|
<version>${openjpa.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<executions>
|
<executions>
|
||||||
|
@ -14,6 +14,8 @@ package org.openhab.persistence.jpa.internal;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
* @author Kai Kreuzer - migrated to 3.x
|
* @author Kai Kreuzer - migrated to 3.x
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class JpaConfiguration {
|
public class JpaConfiguration {
|
||||||
private final Logger logger = LoggerFactory.getLogger(JpaConfiguration.class);
|
private final Logger logger = LoggerFactory.getLogger(JpaConfiguration.class);
|
||||||
|
|
||||||
@ -33,51 +36,51 @@ public class JpaConfiguration {
|
|||||||
private static final String CFG_PASSWORD = "password";
|
private static final String CFG_PASSWORD = "password";
|
||||||
private static final String CFG_SYNCMAPPING = "syncmappings";
|
private static final String CFG_SYNCMAPPING = "syncmappings";
|
||||||
|
|
||||||
public static boolean isInitialized = false;
|
|
||||||
|
|
||||||
public final String dbConnectionUrl;
|
public final String dbConnectionUrl;
|
||||||
public final String dbDriverClass;
|
public final String dbDriverClass;
|
||||||
public final String dbUserName;
|
public final String dbUserName;
|
||||||
public final String dbPassword;
|
public final String dbPassword;
|
||||||
public final String dbSyncMapping;
|
public final String dbSyncMapping;
|
||||||
|
|
||||||
public JpaConfiguration(final Map<String, Object> properties) {
|
public JpaConfiguration(final Map<String, @Nullable Object> properties) throws IllegalArgumentException {
|
||||||
logger.debug("Update config...");
|
logger.debug("Creating JPA config...");
|
||||||
|
|
||||||
String param = (String) properties.get(CFG_CONNECTION_URL);
|
String param = (String) properties.get(CFG_CONNECTION_URL);
|
||||||
logger.debug("url: {}", param);
|
logger.debug("url: {}", param);
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
logger.warn("Connection url is required in jpa.cfg!");
|
throw new IllegalArgumentException("Connection URL is required in JPA configuration!");
|
||||||
} else if (param.isBlank()) {
|
} else if (param.isBlank()) {
|
||||||
logger.warn("Empty connection url in jpa.cfg!");
|
throw new IllegalArgumentException("Empty connection URL in JPA configuration!");
|
||||||
}
|
}
|
||||||
dbConnectionUrl = param;
|
dbConnectionUrl = param;
|
||||||
|
|
||||||
param = (String) properties.get(CFG_DRIVER_CLASS);
|
param = (String) properties.get(CFG_DRIVER_CLASS);
|
||||||
logger.debug("driver: {}", param);
|
logger.debug("driver: {}", param);
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
logger.warn("Driver class is required in jpa.cfg!");
|
throw new IllegalArgumentException("Driver class is required in JPA configuration!");
|
||||||
} else if (param.isBlank()) {
|
} else if (param.isBlank()) {
|
||||||
logger.warn("Empty driver class in jpa.cfg!");
|
throw new IllegalArgumentException("Empty driver class in JPA configuration!");
|
||||||
}
|
}
|
||||||
dbDriverClass = param;
|
dbDriverClass = param;
|
||||||
|
|
||||||
if (properties.get(CFG_USERNAME) == null) {
|
param = (String) properties.get(CFG_USERNAME);
|
||||||
logger.info("{} was not specified!", CFG_USERNAME);
|
if (param == null) {
|
||||||
|
logger.info("{} was not specified in JPA configuration!", CFG_USERNAME);
|
||||||
}
|
}
|
||||||
dbUserName = (String) properties.get(CFG_USERNAME);
|
dbUserName = param == null ? "" : param;
|
||||||
|
|
||||||
if (properties.get(CFG_PASSWORD) == null) {
|
param = (String) properties.get(CFG_PASSWORD);
|
||||||
logger.info("{} was not specified!", CFG_PASSWORD);
|
if (param == null) {
|
||||||
|
logger.info("{} was not specified in JPA configuration!", CFG_PASSWORD);
|
||||||
}
|
}
|
||||||
dbPassword = (String) properties.get(CFG_PASSWORD);
|
dbPassword = param == null ? "" : param;
|
||||||
|
|
||||||
if (properties.get(CFG_SYNCMAPPING) == null) {
|
param = (String) properties.get(CFG_SYNCMAPPING);
|
||||||
logger.debug("{} was not specified!", CFG_SYNCMAPPING);
|
if (param == null) {
|
||||||
|
logger.debug("{} was not specified in JPA configuration!", CFG_SYNCMAPPING);
|
||||||
}
|
}
|
||||||
dbSyncMapping = (String) properties.get(CFG_SYNCMAPPING);
|
dbSyncMapping = param == null ? "" : param;
|
||||||
|
|
||||||
isInitialized = true;
|
logger.debug("Creating JPA config... done");
|
||||||
logger.debug("Update config... done");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,10 @@ import java.text.DateFormat;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.core.items.Item;
|
import org.openhab.core.items.Item;
|
||||||
import org.openhab.core.library.items.ContactItem;
|
import org.openhab.core.library.items.ContactItem;
|
||||||
import org.openhab.core.library.items.DateTimeItem;
|
import org.openhab.core.library.items.DateTimeItem;
|
||||||
@ -37,6 +38,7 @@ import org.openhab.core.library.types.StringListType;
|
|||||||
import org.openhab.core.library.types.StringType;
|
import org.openhab.core.library.types.StringType;
|
||||||
import org.openhab.core.persistence.HistoricItem;
|
import org.openhab.core.persistence.HistoricItem;
|
||||||
import org.openhab.core.types.State;
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
|
import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,6 +47,7 @@ import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
|
|||||||
* @author Manfred Bergmann - Initial contribution
|
* @author Manfred Bergmann - Initial contribution
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class JpaHistoricItem implements HistoricItem {
|
public class JpaHistoricItem implements HistoricItem {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
@ -78,25 +81,20 @@ public class JpaHistoricItem implements HistoricItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method maps a jpa result item to this historic item.
|
* This method maps {@link JpaPersistentItem}s to {@link HistoricItem}s.
|
||||||
*
|
*
|
||||||
* @param jpaQueryResult the result which jpa items
|
* @param jpaQueryResult the result with jpa items
|
||||||
* @param item used for query information, like the state (State)
|
* @param item used for query information, like the state (State)
|
||||||
* @return list of historic items
|
* @return list of historic items
|
||||||
*/
|
*/
|
||||||
public static List<HistoricItem> fromResultList(List<JpaPersistentItem> jpaQueryResult, Item item) {
|
public static List<HistoricItem> fromResultList(List<JpaPersistentItem> jpaQueryResult, Item item) {
|
||||||
List<HistoricItem> ret = new ArrayList<>();
|
return jpaQueryResult.stream().map(pItem -> fromPersistedItem(pItem, item)).collect(Collectors.toList());
|
||||||
for (JpaPersistentItem i : jpaQueryResult) {
|
|
||||||
HistoricItem hi = fromPersistedItem(i, item);
|
|
||||||
ret.add(hi);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the string value of the persisted item to the state of a HistoricItem.
|
* Converts the string value of the persisted item to the state of a {@link HistoricItem}.
|
||||||
*
|
*
|
||||||
* @param pItem the persisted JpaPersistentItem
|
* @param pItem the persisted {@link JpaPersistentItem}
|
||||||
* @param item the source reference Item
|
* @param item the source reference Item
|
||||||
* @return historic item
|
* @return historic item
|
||||||
*/
|
*/
|
||||||
@ -105,7 +103,7 @@ public class JpaHistoricItem implements HistoricItem {
|
|||||||
if (item instanceof NumberItem) {
|
if (item instanceof NumberItem) {
|
||||||
state = new DecimalType(Double.valueOf(pItem.getValue()));
|
state = new DecimalType(Double.valueOf(pItem.getValue()));
|
||||||
} else if (item instanceof DimmerItem) {
|
} else if (item instanceof DimmerItem) {
|
||||||
state = new PercentType(Integer.valueOf(pItem.getValue()));
|
state = new PercentType(Integer.parseInt(pItem.getValue()));
|
||||||
} else if (item instanceof SwitchItem) {
|
} else if (item instanceof SwitchItem) {
|
||||||
state = OnOffType.valueOf(pItem.getValue());
|
state = OnOffType.valueOf(pItem.getValue());
|
||||||
} else if (item instanceof ContactItem) {
|
} else if (item instanceof ContactItem) {
|
||||||
@ -113,7 +111,7 @@ public class JpaHistoricItem implements HistoricItem {
|
|||||||
} else if (item instanceof RollershutterItem) {
|
} else if (item instanceof RollershutterItem) {
|
||||||
state = PercentType.valueOf(pItem.getValue());
|
state = PercentType.valueOf(pItem.getValue());
|
||||||
} else if (item instanceof DateTimeItem) {
|
} else if (item instanceof DateTimeItem) {
|
||||||
state = new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.valueOf(pItem.getValue())),
|
state = new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(pItem.getValue())),
|
||||||
ZoneId.systemDefault()));
|
ZoneId.systemDefault()));
|
||||||
} else if (item instanceof LocationItem) {
|
} else if (item instanceof LocationItem) {
|
||||||
PointType pType = null;
|
PointType pType = null;
|
||||||
@ -125,7 +123,7 @@ public class JpaHistoricItem implements HistoricItem {
|
|||||||
pType.setAltitude(new DecimalType(comps[2]));
|
pType.setAltitude(new DecimalType(comps[2]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state = pType;
|
state = pType == null ? UnDefType.UNDEF : pType;
|
||||||
} else if (item instanceof StringListType) {
|
} else if (item instanceof StringListType) {
|
||||||
state = new StringListType(pItem.getValue());
|
state = new StringListType(pItem.getValue());
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.persistence.jpa.internal;
|
package org.openhab.persistence.jpa.internal;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -27,6 +26,7 @@ import javax.persistence.Query;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.config.core.ConfigurableService;
|
||||||
import org.openhab.core.items.Item;
|
import org.openhab.core.items.Item;
|
||||||
import org.openhab.core.items.ItemNotFoundException;
|
import org.openhab.core.items.ItemNotFoundException;
|
||||||
import org.openhab.core.items.ItemRegistry;
|
import org.openhab.core.items.ItemRegistry;
|
||||||
@ -40,9 +40,9 @@ import org.openhab.core.persistence.strategy.PersistenceStrategy;
|
|||||||
import org.openhab.core.types.UnDefType;
|
import org.openhab.core.types.UnDefType;
|
||||||
import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
|
import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
|
||||||
import org.osgi.framework.BundleContext;
|
import org.osgi.framework.BundleContext;
|
||||||
|
import org.osgi.framework.Constants;
|
||||||
import org.osgi.service.component.annotations.Activate;
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.ConfigurationPolicy;
|
|
||||||
import org.osgi.service.component.annotations.Deactivate;
|
import org.osgi.service.component.annotations.Deactivate;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -55,19 +55,36 @@ import org.slf4j.LoggerFactory;
|
|||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
@Component(service = { PersistenceService.class,
|
@Component(service = { PersistenceService.class,
|
||||||
QueryablePersistenceService.class }, configurationPid = "org.openhab.jpa", configurationPolicy = ConfigurationPolicy.REQUIRE)
|
QueryablePersistenceService.class }, configurationPid = "org.openhab.jpa", //
|
||||||
|
property = Constants.SERVICE_PID + "=org.openhab.jpa")
|
||||||
|
@ConfigurableService(category = "persistence", label = "JPA Persistence Service", description_uri = JpaPersistenceService.CONFIG_URI)
|
||||||
public class JpaPersistenceService implements QueryablePersistenceService {
|
public class JpaPersistenceService implements QueryablePersistenceService {
|
||||||
|
|
||||||
|
private static final String SERVICE_ID = "jpa";
|
||||||
|
private static final String SERVICE_LABEL = "JPA";
|
||||||
|
protected static final String CONFIG_URI = "persistence:jpa";
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(JpaPersistenceService.class);
|
private final Logger logger = LoggerFactory.getLogger(JpaPersistenceService.class);
|
||||||
|
|
||||||
private final ItemRegistry itemRegistry;
|
private final ItemRegistry itemRegistry;
|
||||||
|
|
||||||
private @Nullable EntityManagerFactory emf = null;
|
private @Nullable EntityManagerFactory emf;
|
||||||
|
|
||||||
private @NonNullByDefault({}) JpaConfiguration config;
|
private @NonNullByDefault({}) JpaConfiguration config;
|
||||||
|
|
||||||
|
private boolean initialized;
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
public JpaPersistenceService(final @Reference ItemRegistry itemRegistry) {
|
public JpaPersistenceService(BundleContext context, Map<String, @Nullable Object> properties,
|
||||||
|
final @Reference ItemRegistry itemRegistry) {
|
||||||
this.itemRegistry = itemRegistry;
|
this.itemRegistry = itemRegistry;
|
||||||
|
logger.debug("Activating JPA persistence service");
|
||||||
|
try {
|
||||||
|
config = new JpaConfiguration(properties);
|
||||||
|
initialized = true;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.warn("{}", e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,36 +92,32 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
*
|
*
|
||||||
* @return EntityManagerFactory
|
* @return EntityManagerFactory
|
||||||
*/
|
*/
|
||||||
protected @Nullable EntityManagerFactory getEntityManagerFactory() {
|
protected EntityManagerFactory getEntityManagerFactory() {
|
||||||
|
EntityManagerFactory emf = this.emf;
|
||||||
if (emf == null) {
|
if (emf == null) {
|
||||||
emf = newEntityManagerFactory();
|
emf = newEntityManagerFactory();
|
||||||
|
this.emf = emf;
|
||||||
}
|
}
|
||||||
return emf;
|
return emf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Activate
|
|
||||||
public void activate(BundleContext context, Map<String, Object> properties) {
|
|
||||||
logger.debug("Activating jpa persistence service");
|
|
||||||
config = new JpaConfiguration(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the EntityPersistenceFactory
|
* Closes the EntityPersistenceFactory
|
||||||
*/
|
*/
|
||||||
@Deactivate
|
@Deactivate
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
logger.debug("Deactivating jpa persistence service");
|
logger.debug("Deactivating JPA persistence service");
|
||||||
closeEntityManagerFactory();
|
closeEntityManagerFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "jpa";
|
return SERVICE_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLabel(@Nullable Locale locale) {
|
public String getLabel(@Nullable Locale locale) {
|
||||||
return "JPA";
|
return SERVICE_LABEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -121,8 +134,8 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!JpaConfiguration.isInitialized) {
|
if (!initialized) {
|
||||||
logger.debug("Trying to create EntityManagerFactory but we don't have configuration yet!");
|
logger.debug("Cannot create EntityManagerFactory without a valid configuration!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +148,7 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
pItem.setValue(newValue);
|
pItem.setValue(newValue);
|
||||||
logger.debug("Stored new value: {}", newValue);
|
logger.debug("Stored new value: {}", newValue);
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
logger.error("Error on converting state value to string: {}", e1.getMessage());
|
logger.error("Error while converting state value to string: {}", e1.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pItem.setName(name);
|
pItem.setName(name);
|
||||||
@ -151,7 +164,7 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
logger.debug("Persisting item...done");
|
logger.debug("Persisting item...done");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error on persisting item! Rolling back!", e);
|
logger.error("Error while persisting item! Rolling back!", e);
|
||||||
em.getTransaction().rollback();
|
em.getTransaction().rollback();
|
||||||
} finally {
|
} finally {
|
||||||
em.close();
|
em.close();
|
||||||
@ -162,20 +175,24 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<PersistenceItemInfo> getItemInfo() {
|
public Set<PersistenceItemInfo> getItemInfo() {
|
||||||
return Collections.emptySet();
|
return Set.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<HistoricItem> query(FilterCriteria filter) {
|
public Iterable<HistoricItem> query(FilterCriteria filter) {
|
||||||
logger.debug("Querying for historic item: {}", filter.getItemName());
|
logger.debug("Querying for historic item: {}", filter.getItemName());
|
||||||
|
|
||||||
if (!JpaConfiguration.isInitialized) {
|
if (!initialized) {
|
||||||
logger.warn("Trying to create EntityManagerFactory but we don't have configuration yet!");
|
logger.warn("Cannot create EntityManagerFactory without a valid configuration!");
|
||||||
return Collections.emptyList();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
String itemName = filter.getItemName();
|
String itemName = filter.getItemName();
|
||||||
Item item = getItemFromRegistry(itemName);
|
Item item = getItemFromRegistry(itemName);
|
||||||
|
if (item == null) {
|
||||||
|
logger.debug("Item '{}' does not exist in the item registry", itemName);
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
String sortOrder;
|
String sortOrder;
|
||||||
if (filter.getOrdering() == Ordering.ASCENDING) {
|
if (filter.getOrdering() == Ordering.ASCENDING) {
|
||||||
@ -225,19 +242,19 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
logger.debug("Retrieving result list...done");
|
logger.debug("Retrieving result list...done");
|
||||||
|
|
||||||
List<HistoricItem> historicList = JpaHistoricItem.fromResultList(result, item);
|
List<HistoricItem> historicList = JpaHistoricItem.fromResultList(result, item);
|
||||||
logger.debug("{}", String.format("Convert to HistoricItem: %d", historicList.size()));
|
logger.debug("Convert to HistoricItem: {}", historicList.size());
|
||||||
|
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
|
|
||||||
return historicList;
|
return historicList;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error on querying database!", e);
|
logger.error("Error while querying database!", e);
|
||||||
em.getTransaction().rollback();
|
em.getTransaction().rollback();
|
||||||
} finally {
|
} finally {
|
||||||
em.close();
|
em.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.emptyList();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -251,24 +268,24 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
Map<String, String> properties = new HashMap<>();
|
Map<String, String> properties = new HashMap<>();
|
||||||
properties.put("javax.persistence.jdbc.url", config.dbConnectionUrl);
|
properties.put("javax.persistence.jdbc.url", config.dbConnectionUrl);
|
||||||
properties.put("javax.persistence.jdbc.driver", config.dbDriverClass);
|
properties.put("javax.persistence.jdbc.driver", config.dbDriverClass);
|
||||||
if (config.dbUserName != null) {
|
if (!config.dbUserName.isBlank()) {
|
||||||
properties.put("javax.persistence.jdbc.user", config.dbUserName);
|
properties.put("javax.persistence.jdbc.user", config.dbUserName);
|
||||||
}
|
}
|
||||||
if (config.dbPassword != null) {
|
if (!config.dbPassword.isBlank()) {
|
||||||
properties.put("javax.persistence.jdbc.password", config.dbPassword);
|
properties.put("javax.persistence.jdbc.password", config.dbPassword);
|
||||||
}
|
}
|
||||||
if (config.dbUserName != null && config.dbPassword == null) {
|
if (config.dbUserName.isBlank() && config.dbPassword.isBlank()) {
|
||||||
logger.warn("JPA persistence - it is recommended to use a password to protect data store");
|
logger.info("It is recommended to use a password to protect the JPA persistence data store");
|
||||||
}
|
}
|
||||||
if (config.dbSyncMapping != null && !config.dbSyncMapping.isBlank()) {
|
if (!config.dbSyncMapping.isBlank()) {
|
||||||
logger.warn("You are settings openjpa.jdbc.SynchronizeMappings, I hope you know what you're doing!");
|
logger.info("You are setting openjpa.jdbc.SynchronizeMappings, I hope you know what you're doing!");
|
||||||
properties.put("openjpa.jdbc.SynchronizeMappings", config.dbSyncMapping);
|
properties.put("openjpa.jdbc.SynchronizeMappings", config.dbSyncMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityManagerFactory fac = Persistence.createEntityManagerFactory(getPersistenceUnitName(), properties);
|
EntityManagerFactory factory = Persistence.createEntityManagerFactory(getPersistenceUnitName(), properties);
|
||||||
logger.debug("Creating EntityManagerFactory...done");
|
logger.debug("Creating EntityManagerFactory...done");
|
||||||
|
|
||||||
return fac;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -317,6 +334,6 @@ public class JpaPersistenceService implements QueryablePersistenceService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PersistenceStrategy> getDefaultStrategies() {
|
public List<PersistenceStrategy> getDefaultStrategies() {
|
||||||
return Collections.emptyList();
|
return List.of();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ package org.openhab.persistence.jpa.internal;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.core.library.types.DateTimeType;
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.PointType;
|
import org.openhab.core.library.types.PointType;
|
||||||
@ -25,16 +26,16 @@ import org.openhab.core.types.State;
|
|||||||
* @author Manfred Bergmann - Initial contribution
|
* @author Manfred Bergmann - Initial contribution
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class StateHelper {
|
public class StateHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given State to a string that can be persisted in db
|
* Converts the given State to a string that can be persisted in the database.
|
||||||
*
|
*
|
||||||
* @param state the state of the item to be persisted
|
* @param state the state of the item to be persisted
|
||||||
* @return state converted as string
|
* @return state converted as string
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static String toString(State state) throws Exception {
|
public static String toString(State state) {
|
||||||
if (state instanceof DateTimeType) {
|
if (state instanceof DateTimeType) {
|
||||||
return String.valueOf(((DateTimeType) state).getZonedDateTime().toInstant().toEpochMilli());
|
return String.valueOf(((DateTimeType) state).getZonedDateTime().toInstant().toEpochMilli());
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import javax.persistence.Table;
|
|||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.core.persistence.HistoricItem;
|
import org.openhab.core.persistence.HistoricItem;
|
||||||
import org.openhab.core.types.State;
|
import org.openhab.core.types.State;
|
||||||
import org.openhab.core.types.UnDefType;
|
import org.openhab.core.types.UnDefType;
|
||||||
@ -39,11 +40,12 @@ import org.openhab.core.types.UnDefType;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HISTORIC_ITEM")
|
@Table(name = "HISTORIC_ITEM")
|
||||||
|
@NonNullByDefault
|
||||||
public class JpaPersistentItem implements HistoricItem {
|
public class JpaPersistentItem implements HistoricItem {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
private Long id;
|
private @NonNullByDefault({}) Long id;
|
||||||
|
|
||||||
private String name = "";
|
private String name = "";
|
||||||
private String realName = "";
|
private String realName = "";
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
@org.osgi.annotation.bundle.Header(name = org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE, value = "*")
|
||||||
|
package org.openhab.persistence.jpa.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This dynamic import is required for loading the JDBC driver class.
|
||||||
|
*
|
||||||
|
* @author Wouter Born - Initial contribution
|
||||||
|
*/
|
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<config-description:config-descriptions
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||||
|
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<config-description uri="persistence:jpa">
|
||||||
|
|
||||||
|
<parameter name="url" type="text" required="true">
|
||||||
|
<label>Database URL</label>
|
||||||
|
<description><![CDATA[JDBC connection URL.<br>Examples:<br>jdbc:derby://hab.local:1527/openhab;create=true<br>jdbc:mariadb://localhost:3306/openhab<br>jdbc:mysql://localhost:3306/openhab<br>jdbc:postgresql://hab.local:5432/openhab]]></description>
|
||||||
|
</parameter>
|
||||||
|
|
||||||
|
<parameter name="driver" type="text" required="true">
|
||||||
|
<label>Database Driver</label>
|
||||||
|
<description><![CDATA[The JDBC driver class name for the connection.<br>Examples:<br>com.mysql.jdbc.Driver<br>org.apache.derby.jdbc.ClientDriver<br>org.mariadb.jdbc.Driver<br>org.postgresql.Driver]]></description>
|
||||||
|
</parameter>
|
||||||
|
|
||||||
|
<parameter name="user" type="text">
|
||||||
|
<label>Database User</label>
|
||||||
|
<description>The database user name for the connection.</description>
|
||||||
|
</parameter>
|
||||||
|
|
||||||
|
<parameter name="password" type="text">
|
||||||
|
<context>password</context>
|
||||||
|
<label>Database Password</label>
|
||||||
|
<description>The database user password for the connection.</description>
|
||||||
|
</parameter>
|
||||||
|
|
||||||
|
<parameter name="syncmappings" type="text">
|
||||||
|
<label>Synchronize Mappings</label>
|
||||||
|
<description>The OpenJPA synchronize mappings configuration.</description>
|
||||||
|
</parameter>
|
||||||
|
|
||||||
|
</config-description>
|
||||||
|
|
||||||
|
</config-description:config-descriptions>
|
@ -0,0 +1,14 @@
|
|||||||
|
persistence.config.jpa.driver.label = Database Driver
|
||||||
|
persistence.config.jpa.driver.description = The JDBC driver class name for the connection.<br>Examples:<br>com.mysql.jdbc.Driver<br>org.apache.derby.jdbc.ClientDriver<br>org.mariadb.jdbc.Driver<br>org.postgresql.Driver
|
||||||
|
persistence.config.jpa.password.label = Database Password
|
||||||
|
persistence.config.jpa.password.description = The database user password for the connection.
|
||||||
|
persistence.config.jpa.syncmappings.label = Synchronize Mappings
|
||||||
|
persistence.config.jpa.syncmappings.description = The OpenJPA synchronize mappings configuration.
|
||||||
|
persistence.config.jpa.url.label = Database URL
|
||||||
|
persistence.config.jpa.url.description = JDBC connection URL.<br>Examples:<br>jdbc:derby://hab.local:1527/openhab;create=true<br>jdbc:mariadb://localhost:3306/openhab<br>jdbc:mysql://localhost:3306/openhab<br>jdbc:postgresql://hab.local:5432/openhab
|
||||||
|
persistence.config.jpa.user.label = Database User
|
||||||
|
persistence.config.jpa.user.description = The database user name for the connection.
|
||||||
|
|
||||||
|
# service
|
||||||
|
|
||||||
|
service.persistence.jpa.label = JPA Persistence Service
|
Loading…
Reference in New Issue
Block a user