You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/12/03 12:18:53 UTC
[syncope] branch SYNCOPE-1400_SYNCOPE-1401 created (now 25dfcc0)
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a change to branch SYNCOPE-1400_SYNCOPE-1401
in repository https://gitbox.apache.org/repos/asf/syncope.git.
at 25dfcc0 [SYNCOPE-1400] Adding support for MySQL 8 via OpenJPA 3.0.1 + [SYNCOPE-1401] Adding support for MySQL with JSON type
This branch includes the following new commits:
new 25dfcc0 [SYNCOPE-1400] Adding support for MySQL 8 via OpenJPA 3.0.1 + [SYNCOPE-1401] Adding support for MySQL with JSON type
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[syncope] 01/01: [SYNCOPE-1400] Adding support for MySQL 8 via
OpenJPA 3.0.1 + [SYNCOPE-1401] Adding support for MySQL with JSON type
Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch SYNCOPE-1400_SYNCOPE-1401
in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 25dfcc05994b11fe4b33cb8458b2bd57cd93e4d9
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Mon Dec 3 13:18:36 2018 +0100
[SYNCOPE-1400] Adding support for MySQL 8 via OpenJPA 3.0.1 + [SYNCOPE-1401] Adding support for MySQL with JSON type
---
core/persistence-jpa-json/pom.xml | 129 ++++++---
...AJSONAnyDAO.java => AbstractJPAJSONAnyDAO.java} | 81 ++----
.../jpa/dao/AbstractJPAJSONAnySearchDAO.java | 125 +++++++++
.../core/persistence/jpa/dao/MyJPAJSONAnyDAO.java | 120 ++++++++
...nySearchDAO.java => MyJPAJSONAnySearchDAO.java} | 131 ++-------
.../core/persistence/jpa/dao/PGJPAJSONAnyDAO.java | 305 ++-------------------
.../persistence/jpa/dao/PGJPAJSONAnySearchDAO.java | 110 +-------
.../jpa/entity/JPAJSONEntityFactory.java | 3 +-
.../jpa/entity/MyJPAJSONEntityFactory.java | 37 +++
.../main/resources/META-INF/spring-orm-myjson.xml | 137 +++++++++
.../resources/myjson}/domains/Master.properties | 10 +-
.../src/main/resources/myjson/indexes.xml | 58 ++++
.../main/resources/myjson/persistence.properties | 23 +-
.../src/main/resources/myjson/views.xml | 181 ++++++++++++
.../{pgjsonb => }/domains/MasterContent.xml | 0
.../core/persistence/jpa/dao/JPAAnySearchDAO.java | 4 +
.../main/resources/audit/audit_mysql_innodb.sql | 2 +-
.../core/persistence/jpa/inner/AnyTypeTest.java | 2 +
fit/core-reference/pom.xml | 168 +++++++++++-
.../{mysql => myjson}/domains/Master.properties | 10 +-
.../{mysql => myjson}/provisioning.properties | 2 +-
.../main/resources/mysql/domains/Master.properties | 8 +-
.../main/resources/mysql/provisioning.properties | 2 +-
pom.xml | 5 +-
24 files changed, 1035 insertions(+), 618 deletions(-)
diff --git a/core/persistence-jpa-json/pom.xml b/core/persistence-jpa-json/pom.xml
index dfd7bb2..0331a27 100644
--- a/core/persistence-jpa-json/pom.xml
+++ b/core/persistence-jpa-json/pom.xml
@@ -227,10 +227,6 @@ under the License.
<filtering>true</filtering>
</testResource>
<testResource>
- <directory>src/test/resources/pgjsonb</directory>
- <filtering>true</filtering>
- </testResource>
- <testResource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</testResource>
@@ -243,62 +239,119 @@ under the License.
</profile>
<profile>
- <id>sqlgen</id>
-
- <properties>
- <skipTests>true</skipTests>
- </properties>
+ <id>mysql</id>
+ <dependencies>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ <version>${jdbc.mysql.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
<build>
<defaultGoal>clean verify</defaultGoal>
<plugins>
<plugin>
- <groupId>org.apache.openjpa</groupId>
- <artifactId>openjpa-maven-plugin</artifactId>
- <inherited>true</inherited>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
- <id>sqlgen</id>
- <phase>process-classes</phase>
+ <id>add-test-source</id>
+ <phase>generate-test-sources</phase>
<goals>
- <goal>sql</goal>
+ <goal>add-test-source</goal>
</goals>
+ <configuration>
+ <sources>
+ <source>${basedir}/../persistence-jpa/src/test/java</source>
+ </sources>
+ </configuration>
</execution>
</executions>
- </plugin>
- </plugins>
- </build>
- </profile>
-
- <profile>
- <id>schemagen</id>
-
- <properties>
- <skipTests>true</skipTests>
- </properties>
+ </plugin>
- <build>
- <defaultGoal>clean verify</defaultGoal>
-
- <plugins>
<plugin>
- <groupId>org.apache.openjpa</groupId>
- <artifactId>openjpa-maven-plugin</artifactId>
- <inherited>true</inherited>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>**/*Test.java</include>
+ </includes>
+ <excludedGroups>multitenancy,plainAttrTable</excludedGroups>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <configuration>
+ <images>
+ <image>
+ <name>mysql/mysql-server:${docker.mysql.version}</name>
+ <run>
+ <cmd>--skip-log-bin --server-id=1</cmd>
+ <env>
+ <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD>
+ <MYSQL_DATABASE>syncope</MYSQL_DATABASE>
+ <MYSQL_USER>syncope</MYSQL_USER>
+ <MYSQL_PASSWORD>syncope</MYSQL_PASSWORD>
+ </env>
+ <ports>
+ <port>3306:3306</port>
+ </ports>
+ <wait>
+ <log>MySQL init process done. Ready for start up.</log>
+ <time>30000</time>
+ </wait>
+ </run>
+ </image>
+ </images>
+ </configuration>
<executions>
<execution>
- <id>schemagen</id>
- <phase>process-classes</phase>
+ <id>start-mysql</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>stop-mysql</id>
+ <phase>post-integration-test</phase>
<goals>
- <goal>schema</goal>
+ <goal>stop</goal>
+ <goal>remove</goal>
</goals>
</execution>
</executions>
- </plugin>
+ </plugin>
</plugins>
+
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ <testResource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ <testResource>
+ <directory>src/main/resources/myjson</directory>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
</build>
</profile>
</profiles>
-
</project>
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java
similarity index 81%
copy from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java
copy to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java
index 94bb72a..f0bca9a 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java
@@ -20,40 +20,37 @@ package org.apache.syncope.core.persistence.jpa.dao;
import java.io.StringReader;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
-import java.util.StringJoiner;
import java.util.regex.Pattern;
import javax.persistence.Query;
import org.apache.commons.jexl3.parser.Parser;
import org.apache.commons.jexl3.parser.ParserConstants;
import org.apache.commons.jexl3.parser.Token;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
import org.apache.syncope.core.persistence.api.dao.DuplicateException;
+import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO;
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
import org.apache.syncope.core.persistence.api.entity.DerSchema;
-import org.apache.syncope.core.persistence.api.entity.PlainAttr;
-import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
+import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
-import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
-import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO;
-@Repository
-public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJSONAnyDAO {
+abstract class AbstractJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJSONAnyDAO {
@Autowired
private PlainSchemaDAO plainSchemaDAO;
@@ -61,22 +58,19 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ
@Autowired
private DerSchemaDAO derSchemaDAO;
- private String queryBegin(final String table) {
- return "SELECT DISTINCT id FROM " + table + " u,"
- + "jsonb_array_elements(u.plainAttrs) attrs,"
- + "jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues ";
- }
+ protected abstract String queryBegin(String table);
- private String attrValueMatch(
- final AnyUtils anyUtils,
- final PlainSchema schema,
- final PlainAttrValue attrValue,
- final boolean ignoreCaseMatch) {
+ protected abstract String attrValueMatch(
+ AnyUtils anyUtils,
+ PlainSchema schema,
+ PlainAttrValue attrValue,
+ boolean ignoreCaseMatch);
+ protected Pair<String, Boolean> schemaInfo(final AttrSchemaType schemaType, final boolean ignoreCaseMatch) {
String key;
boolean lower = false;
- switch (schema.getType()) {
+ switch (schemaType) {
case Boolean:
key = "booleanValue";
break;
@@ -102,29 +96,10 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ
key = "stringValue";
}
- if (lower) {
- return "attrs ->> 'schema' = ? "
- + "AND "
- + (lower ? "LOWER(" : "")
- + (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues") + " ->> '" + key
- + "'" + (lower ? ")" : "")
- + " = "
- + (lower ? "LOWER(" : "")
- + "?"
- + (lower ? ")" : "");
- } else {
- PlainAttr<?> container = anyUtils.newPlainAttr();
- container.setSchema(schema);
- if (attrValue instanceof PlainAttrUniqueValue) {
- container.setUniqueValue((PlainAttrUniqueValue) attrValue);
- } else {
- ((JSONPlainAttr) container).add(attrValue);
- }
- return "plainAttrs @> '" + POJOHelper.serialize(Arrays.asList(container)) + "'::jsonb";
- }
+ return Pair.of(key, lower);
}
- private <A extends Any<?>> List<A> buildResult(final AnyUtils anyUtils, final List<Object> queryResult) {
+ protected <A extends Any<?>> List<A> buildResult(final AnyUtils anyUtils, final List<Object> queryResult) {
List<A> result = new ArrayList<>();
queryResult.forEach(anyKey -> {
A any = anyUtils.<A>dao().find(anyKey.toString());
@@ -210,6 +185,8 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ
return attrValues;
}
+ protected abstract List<Object> findByDerAttrValue(String table, Map<String, List<Object>> clauses);
+
@SuppressWarnings("unchecked")
@Transactional(readOnly = true)
@Override
@@ -272,7 +249,7 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ
return Collections.emptyList();
}
- StringJoiner clauses = new StringJoiner(" AND id IN ");
+ Map<String, List<Object>> clauses = new LinkedHashMap<>();
// builder to build the clauses
StringBuilder bld = new StringBuilder();
@@ -280,8 +257,6 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ
// Contains used identifiers in order to avoid replications
Set<String> used = new HashSet<>();
- List<Object> queryParams = new ArrayList<>();
-
// Create several clauses: one for eanch identifiers
for (int i = 0; i < identifiers.size(); i++) {
if (!used.contains(identifiers.get(i))) {
@@ -306,25 +281,21 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ
append("WHERE ").
append(attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch)).
append(')');
- queryParams.add(schema.getKey());
- queryParams.add(attrValues.get(i));
used.add(identifiers.get(i));
- clauses.add(bld.toString());
+ List<Object> queryParams = new ArrayList<>();
+ queryParams.add(schema.getKey());
+ queryParams.add(attrValues.get(i));
+
+ clauses.put(bld.toString(), queryParams);
}
}
}
LOG.debug("Generated where clauses {}", clauses);
- Query query = entityManager().createNativeQuery(
- "SELECT DISTINCT id FROM " + table + " u WHERE id IN " + clauses.toString());
- for (int i = 0; i < queryParams.size(); i++) {
- query.setParameter(i + 1, queryParams.get(i));
- }
-
- return buildResult(anyUtils, query.getResultList());
+ return buildResult(anyUtils, findByDerAttrValue(table, clauses));
}
@Transactional
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnySearchDAO.java
new file mode 100644
index 0000000..693d791
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnySearchDAO.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+
+abstract class AbstractJPAJSONAnySearchDAO extends JPAAnySearchDAO {
+
+ @Override
+ SearchSupport buildSearchSupport(final AnyTypeKind kind) {
+ return new SearchSupport(kind);
+ }
+
+ @Override
+ protected void processOBS(final SearchSupport svs, final OrderBySupport obs, final StringBuilder where) {
+ obs.views.forEach(searchView -> {
+ where.append(',').
+ append(searchView.name).
+ append(' ').append(searchView.alias);
+ });
+ }
+
+ protected Pair<String, Boolean> schemaInfo(final AttrSchemaType schemaType, final AttributeCond.Type condType) {
+ String key;
+ boolean lower = false;
+ switch (schemaType) {
+ case Boolean:
+ key = "booleanValue";
+ break;
+
+ case Date:
+ key = "dateValue";
+ break;
+
+ case Double:
+ key = "doubleValue";
+ break;
+
+ case Long:
+ key = "longValue";
+ break;
+
+ case Binary:
+ key = "binaryValue";
+ break;
+
+ default:
+ lower = condType == AttributeCond.Type.IEQ || condType == AttributeCond.Type.ILIKE;
+ key = "stringValue";
+ }
+
+ return Pair.of(key, lower);
+ }
+
+ protected void appendOp(final StringBuilder query, final AttributeCond.Type condType, final boolean not) {
+ switch (condType) {
+ case LIKE:
+ case ILIKE:
+ if (not) {
+ query.append("NOT ");
+ }
+ query.append(" LIKE ");
+ break;
+
+ case GE:
+ if (not) {
+ query.append('<');
+ } else {
+ query.append(">=");
+ }
+ break;
+
+ case GT:
+ if (not) {
+ query.append("<=");
+ } else {
+ query.append('>');
+ }
+ break;
+
+ case LE:
+ if (not) {
+ query.append('>');
+ } else {
+ query.append("<=");
+ }
+ break;
+
+ case LT:
+ if (not) {
+ query.append(">=");
+ } else {
+ query.append('<');
+ }
+ break;
+
+ case EQ:
+ case IEQ:
+ default:
+ if (not) {
+ query.append('!');
+ }
+ query.append('=');
+ }
+ }
+}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnyDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnyDAO.java
new file mode 100644
index 0000000..e15e690
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnyDAO.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.persistence.Query;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
+
+public class MyJPAJSONAnyDAO extends AbstractJPAJSONAnyDAO {
+
+ @Override
+ protected String queryBegin(final String table) {
+ String view = StringUtils.containsIgnoreCase(table, AnyTypeKind.USER.name())
+ ? "user_search"
+ : StringUtils.containsIgnoreCase(table, AnyTypeKind.GROUP.name())
+ ? "group_search"
+ : "anyObject_search";
+ return "SELECT DISTINCT id FROM " + view + " ";
+ }
+
+ @Override
+ protected String attrValueMatch(
+ final AnyUtils anyUtils,
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final boolean ignoreCaseMatch) {
+
+ Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), ignoreCaseMatch);
+ if (schemaInfo.getRight()) {
+ return "plainSchema = ? "
+ + "AND "
+ + (schemaInfo.getRight() ? "LOWER(" : "")
+ + (schema.isUniqueConstraint()
+ ? "attrUniqueValue ->> '$." + schemaInfo.getLeft() + "'"
+ : schemaInfo.getLeft())
+ + (schemaInfo.getRight() ? ")" : "")
+ + " = "
+ + (schemaInfo.getRight() ? "LOWER(" : "")
+ + "?"
+ + (schemaInfo.getRight() ? ")" : "");
+ } else {
+ PlainAttr<?> container = anyUtils.newPlainAttr();
+ container.setSchema(schema);
+ if (attrValue instanceof PlainAttrUniqueValue) {
+ container.setUniqueValue((PlainAttrUniqueValue) attrValue);
+ } else {
+ ((JSONPlainAttr) container).add(attrValue);
+ }
+ return "JSON_CONTAINS(plainAttrs, '" + POJOHelper.serialize(Arrays.asList(container)) + "')";
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This method is a workaround for a bug experienced with MySQL 8.0.13, where the correct implementation (as shown
+ * in PGJPAJSONAnyDAO.findByDerAttrValue(String, Map<String, List<Object>>)) generates a core dump.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ protected List<Object> findByDerAttrValue(
+ final String table,
+ final Map<String, List<Object>> clauses) {
+
+ if (clauses.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ Set<Object> result = new HashSet<>();
+ AtomicReference<Boolean> first = new AtomicReference<>(Boolean.TRUE);
+ clauses.forEach((clause, parameters) -> {
+ Query query = entityManager().createNativeQuery(StringUtils.replaceIgnoreCase(clause, "DISTINCT", ""));
+ for (int i = 0; i < parameters.size(); i++) {
+ query.setParameter(i + 1, parameters.get(i));
+ }
+
+ Set<Object> local = new HashSet<>(query.getResultList());
+ if (first.get()) {
+ result.addAll(local);
+ first.set(Boolean.FALSE);
+ } else {
+ result.retainAll(local);
+ }
+ });
+
+ return new ArrayList<>(result);
+ }
+}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java
similarity index 55%
copy from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
copy to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java
index f240705..4707372 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.persistence.jpa.dao;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
@@ -32,21 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
-public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
-
- @Override
- SearchSupport buildSearchSupport(final AnyTypeKind kind) {
- return new SearchSupport(kind);
- }
-
- @Override
- protected void processOBS(final SearchSupport svs, final OrderBySupport obs, final StringBuilder where) {
- obs.views.forEach(searchView -> {
- where.append(',').
- append(searchView.name).
- append(' ').append(searchView.alias);
- });
- }
+public class MyJPAJSONAnySearchDAO extends AbstractJPAJSONAnySearchDAO {
@Override
protected void parseOrderByForPlainSchema(
@@ -62,8 +47,10 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
obs.views.add(svs.field());
- item.select = svs.field().alias + ".attrValues ->> '" + fieldName + "' AS " + fieldName;
- item.where = "attrs ->> 'schema' = '" + fieldName + "'";
+ item.select = svs.field().alias + "."
+ + (schema.isUniqueConstraint() ? "attrUniqueValue" : svs.fieldName(schema.getType()))
+ + " AS " + fieldName;
+ item.where = "plainSchema = '" + fieldName + "'";
item.orderBy = fieldName + " " + clause.getDirection().name();
}
@@ -76,33 +63,7 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
final boolean not,
final List<Object> parameters) {
- String key;
- boolean lower = false;
- switch (schema.getType()) {
- case Boolean:
- key = "booleanValue";
- break;
-
- case Date:
- key = "dateValue";
- break;
-
- case Double:
- key = "doubleValue";
- break;
-
- case Long:
- key = "longValue";
- break;
-
- case Binary:
- key = "binaryValue";
- break;
-
- default:
- lower = cond.getType() == AttributeCond.Type.IEQ || cond.getType() == AttributeCond.Type.ILIKE;
- key = "stringValue";
- }
+ Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), cond.getType());
if (!not && cond.getType() == AttributeCond.Type.EQ) {
PlainAttr<?> container = anyUtils.newPlainAttr();
@@ -113,71 +74,23 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
((JSONPlainAttr) container).add(attrValue);
}
- query.append("plainAttrs @> '").
+ query.append("JSON_CONTAINS(plainAttrs, '").
append(POJOHelper.serialize(Arrays.asList(container))).
- append("'::jsonb");
+ append("')");
} else {
- query.append("attrs ->> 'schema' = ?").append(setParameter(parameters, cond.getSchema())).
+ query.append("plainSchema = ?").append(setParameter(parameters, cond.getSchema())).
append(" AND ").
- append(lower ? "LOWER(" : "").
+ append(schemaInfo.getRight() ? "LOWER(" : "").
append(schema.isUniqueConstraint()
- ? "attrs -> 'uniqueValue'" : "attrValues").
- append(" ->> '").append(key).append("'").
- append(lower ? ")" : "");
-
- switch (cond.getType()) {
- case LIKE:
- case ILIKE:
- if (not) {
- query.append("NOT ");
- }
- query.append(" LIKE ");
- break;
-
- case GE:
- if (not) {
- query.append('<');
- } else {
- query.append(">=");
- }
- break;
-
- case GT:
- if (not) {
- query.append("<=");
- } else {
- query.append('>');
- }
- break;
-
- case LE:
- if (not) {
- query.append('>');
- } else {
- query.append("<=");
- }
- break;
-
- case LT:
- if (not) {
- query.append(">=");
- } else {
- query.append('<');
- }
- break;
-
- case EQ:
- case IEQ:
- default:
- if (not) {
- query.append('!');
- }
- query.append('=');
- }
+ ? "attrUniqueValue ->> '$." + schemaInfo.getLeft() + "'"
+ : schemaInfo.getLeft()).
+ append(schemaInfo.getRight() ? ")" : "");
+
+ appendOp(query, cond.getType(), not);
- query.append(lower ? "LOWER(" : "").
+ query.append(schemaInfo.getRight() ? "LOWER(" : "").
append("?").append(setParameter(parameters, cond.getExpression())).
- append(lower ? ")" : "");
+ append(schemaInfo.getRight() ? ")" : "");
}
}
@@ -208,17 +121,15 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
append(svs.field().name).append(" WHERE ");
switch (cond.getType()) {
case ISNOTNULL:
- query.append("plainAttrs @> '[{\"schema\":\"").
+ query.append("JSON_SEARCH(plainAttrs, 'one', '").
append(checked.getLeft().getKey()).
- append("\"}]'::jsonb");
+ append("', NULL, '$[*].schema') IS NOT NULL");
break;
case ISNULL:
- query.append("any_id NOT IN (").
- append("SELECT any_id FROM ").append(svs.field().name).
- append(" WHERE plainAttrs @> '[{\"schema\":\"").
+ query.append("JSON_SEARCH(plainAttrs, 'one', '").
append(checked.getLeft().getKey()).
- append("\"}]'::jsonb)");
+ append("', NULL, '$[*].schema') IS NULL");
break;
default:
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java
index 94bb72a..ed1664b 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnyDAO.java
@@ -18,100 +18,49 @@
*/
package org.apache.syncope.core.persistence.jpa.dao;
-import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import java.util.StringJoiner;
-import java.util.regex.Pattern;
import javax.persistence.Query;
-import org.apache.commons.jexl3.parser.Parser;
-import org.apache.commons.jexl3.parser.ParserConstants;
-import org.apache.commons.jexl3.parser.Token;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.DuplicateException;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.PlainAttr;
import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Transactional;
import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
-import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO;
-@Repository
-public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJSONAnyDAO {
+public class PGJPAJSONAnyDAO extends AbstractJPAJSONAnyDAO {
- @Autowired
- private PlainSchemaDAO plainSchemaDAO;
-
- @Autowired
- private DerSchemaDAO derSchemaDAO;
-
- private String queryBegin(final String table) {
+ @Override
+ protected String queryBegin(final String table) {
return "SELECT DISTINCT id FROM " + table + " u,"
+ "jsonb_array_elements(u.plainAttrs) attrs,"
+ "jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues ";
}
- private String attrValueMatch(
+ @Override
+ protected String attrValueMatch(
final AnyUtils anyUtils,
final PlainSchema schema,
final PlainAttrValue attrValue,
final boolean ignoreCaseMatch) {
- String key;
- boolean lower = false;
-
- switch (schema.getType()) {
- case Boolean:
- key = "booleanValue";
- break;
-
- case Date:
- key = "dateValue";
- break;
-
- case Double:
- key = "doubleValue";
- break;
-
- case Long:
- key = "longValue";
- break;
-
- case Binary:
- key = "binaryValue";
- break;
-
- default:
- lower = ignoreCaseMatch;
- key = "stringValue";
- }
-
- if (lower) {
+ Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), ignoreCaseMatch);
+ if (schemaInfo.getRight()) {
return "attrs ->> 'schema' = ? "
+ "AND "
- + (lower ? "LOWER(" : "")
- + (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues") + " ->> '" + key
- + "'" + (lower ? ")" : "")
+ + (schemaInfo.getRight() ? "LOWER(" : "")
+ + (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues")
+ + " ->> '" + schemaInfo.getLeft()
+ + "'" + (schemaInfo.getRight() ? ")" : "")
+ " = "
- + (lower ? "LOWER(" : "")
+ + (schemaInfo.getRight() ? "LOWER(" : "")
+ "?"
- + (lower ? ")" : "");
+ + (schemaInfo.getRight() ? ")" : "");
} else {
PlainAttr<?> container = anyUtils.newPlainAttr();
container.setSchema(schema);
@@ -124,232 +73,26 @@ public class PGJPAJSONAnyDAO extends AbstractDAO<AbstractEntity> implements JPAJ
}
}
- private <A extends Any<?>> List<A> buildResult(final AnyUtils anyUtils, final List<Object> queryResult) {
- List<A> result = new ArrayList<>();
- queryResult.forEach(anyKey -> {
- A any = anyUtils.<A>dao().find(anyKey.toString());
- if (any == null) {
- LOG.error("Could not find any for key {}", anyKey);
- } else {
- result.add(any);
- }
- });
- return result;
- }
-
- @SuppressWarnings("unchecked")
- @Transactional(readOnly = true)
@Override
- public <A extends Any<?>> List<A> findByPlainAttrValue(
- final String table,
- final AnyUtils anyUtils,
- final String schemaKey,
- final PlainAttrValue attrValue,
- final boolean ignoreCaseMatch) {
-
- PlainSchema schema = plainSchemaDAO.find(schemaKey);
- if (schema == null) {
- LOG.error("Invalid schema '{}'", schemaKey);
- return Collections.<A>emptyList();
- }
-
- Query query = entityManager().createNativeQuery(
- queryBegin(table)
- + "WHERE " + attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch));
- query.setParameter(1, schemaKey);
- query.setParameter(2, attrValue.getValue());
-
- return buildResult(anyUtils, query.getResultList());
- }
-
- @Transactional(readOnly = true)
- @Override
- public <A extends Any<?>> A findByPlainAttrUniqueValue(
- final String table,
- final AnyUtils anyUtils,
- final String schemaKey,
- final PlainAttrValue attrUniqueValue,
- final boolean ignoreCaseMatch) {
-
- PlainSchema schema = plainSchemaDAO.find(schemaKey);
- if (schema == null) {
- LOG.error("Invalid schema '{}'", schemaKey);
- return null;
- }
- if (!schema.isUniqueConstraint()) {
- LOG.error("This schema has not unique constraint: '{}'", schemaKey);
- return null;
- }
-
- List<A> result = findByPlainAttrValue(table, anyUtils, schemaKey, attrUniqueValue, ignoreCaseMatch);
- return result.isEmpty()
- ? null
- : result.get(0);
- }
-
- /**
- * Split an attribute value recurring on provided literals/tokens.
- *
- * @param attrValue value to be split
- * @param literals literals/tokens
- * @return split value
- */
- private List<String> split(final String attrValue, final List<String> literals) {
- final List<String> attrValues = new ArrayList<>();
-
- if (literals.isEmpty()) {
- attrValues.add(attrValue);
- } else {
- for (String token : attrValue.split(Pattern.quote(literals.get(0)))) {
- if (!token.isEmpty()) {
- attrValues.addAll(split(token, literals.subList(1, literals.size())));
- }
- }
- }
-
- return attrValues;
- }
-
@SuppressWarnings("unchecked")
- @Transactional(readOnly = true)
- @Override
- public <A extends Any<?>> List<A> findByDerAttrValue(
+ protected List<Object> findByDerAttrValue(
final String table,
- final AnyUtils anyUtils,
- final String schemaKey,
- final String value,
- final boolean ignoreCaseMatch) {
-
- DerSchema derSchema = derSchemaDAO.find(schemaKey);
- if (derSchema == null) {
- LOG.error("Invalid schema '{}'", schemaKey);
- return Collections.<A>emptyList();
- }
-
- Parser parser = new Parser(new StringReader(derSchema.getExpression()));
-
- // Schema keys
- List<String> identifiers = new ArrayList<>();
-
- // Literals
- List<String> literals = new ArrayList<>();
-
- // Get schema keys and literals
- for (Token token = parser.getNextToken(); token != null && StringUtils.isNotBlank(token.toString());
- token = parser.getNextToken()) {
-
- if (token.kind == ParserConstants.STRING_LITERAL) {
- literals.add(token.toString().substring(1, token.toString().length() - 1));
- }
-
- if (token.kind == ParserConstants.IDENTIFIER) {
- identifiers.add(token.toString());
- }
- }
-
- // Sort literals in order to process later literals included into others
- Collections.sort(literals, (l1, l2) -> {
- if (l1 == null && l2 == null) {
- return 0;
- } else if (l1 != null && l2 == null) {
- return -1;
- } else if (l1 == null && l2 != null) {
- return 1;
- } else if (l1.length() == l2.length()) {
- return 0;
- } else if (l1.length() > l2.length()) {
- return -1;
- } else {
- return 1;
- }
- });
-
- // Split value on provided literals
- List<String> attrValues = split(value, literals);
-
- if (attrValues.size() != identifiers.size()) {
- LOG.error("Ambiguous JEXL expression resolution: literals and values have different size");
- return Collections.emptyList();
- }
-
- StringJoiner clauses = new StringJoiner(" AND id IN ");
-
- // builder to build the clauses
- StringBuilder bld = new StringBuilder();
-
- // Contains used identifiers in order to avoid replications
- Set<String> used = new HashSet<>();
+ final Map<String, List<Object>> clauses) {
+ StringJoiner actualClauses = new StringJoiner(" AND id IN ");
List<Object> queryParams = new ArrayList<>();
- // Create several clauses: one for eanch identifiers
- for (int i = 0; i < identifiers.size(); i++) {
- if (!used.contains(identifiers.get(i))) {
- // verify schema existence and get schema type
- PlainSchema schema = plainSchemaDAO.find(identifiers.get(i));
- if (schema == null) {
- LOG.error("Invalid schema '{}', ignoring", identifiers.get(i));
- } else {
- // clear builder
- bld.delete(0, bld.length());
-
- PlainAttrValue attrValue;
- if (schema.isUniqueConstraint()) {
- attrValue = anyUtils.newPlainAttrUniqueValue();
- } else {
- attrValue = anyUtils.newPlainAttrValue();
- }
- attrValue.setStringValue(attrValues.get(i));
-
- bld.append('(').
- append(queryBegin(table)).
- append("WHERE ").
- append(attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch)).
- append(')');
- queryParams.add(schema.getKey());
- queryParams.add(attrValues.get(i));
-
- used.add(identifiers.get(i));
-
- clauses.add(bld.toString());
- }
- }
- }
-
- LOG.debug("Generated where clauses {}", clauses);
+ clauses.forEach((clause, parameters) -> {
+ actualClauses.add(clause);
+ queryParams.addAll(parameters);
+ });
Query query = entityManager().createNativeQuery(
- "SELECT DISTINCT id FROM " + table + " u WHERE id IN " + clauses.toString());
+ "SELECT DISTINCT id FROM " + table + " u WHERE id IN " + actualClauses.toString());
for (int i = 0; i < queryParams.size(); i++) {
query.setParameter(i + 1, queryParams.get(i));
}
- return buildResult(anyUtils, query.getResultList());
- }
-
- @Transactional
- @Override
- public <A extends Any<?>> void checkBeforeSave(final String table, final AnyUtils anyUtils, final A any) {
- // check UNIQUE constraints
- any.getPlainAttrs().stream().
- filter(attr -> attr.getUniqueValue() != null).
- map(JSONPlainAttr.class::cast).
- forEach(attr -> {
- String schemaKey = attr.getSchemaKey();
- List<A> others = findByPlainAttrValue(table, anyUtils, schemaKey, attr.getUniqueValue(), false);
- if (others.isEmpty() || (others.size() == 1 && others.get(0).getKey().equals(any.getKey()))) {
- LOG.debug("No duplicate value found for {}", attr.getUniqueValue().getValueAsString());
- } else {
- throw new DuplicateException(
- "Value " + attr.getUniqueValue().getValueAsString() + " existing for " + schemaKey);
- }
- });
-
- // update sysInfo - as org.apache.syncope.core.persistence.jpa.entity.PlainAttrListener is not invoked
- Date now = new Date();
- String username = AuthContextUtils.getUsername();
- LOG.debug("Set last change date '{}' and modifier '{}' for '{}'", now, username, any);
- any.setLastModifier(username);
- any.setLastChangeDate(now);
+ return query.getResultList();
}
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
index f240705..399573a 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.persistence.jpa.dao;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
@@ -32,21 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
-public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
-
- @Override
- SearchSupport buildSearchSupport(final AnyTypeKind kind) {
- return new SearchSupport(kind);
- }
-
- @Override
- protected void processOBS(final SearchSupport svs, final OrderBySupport obs, final StringBuilder where) {
- obs.views.forEach(searchView -> {
- where.append(',').
- append(searchView.name).
- append(' ').append(searchView.alias);
- });
- }
+public class PGJPAJSONAnySearchDAO extends AbstractJPAJSONAnySearchDAO {
@Override
protected void parseOrderByForPlainSchema(
@@ -76,33 +61,7 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
final boolean not,
final List<Object> parameters) {
- String key;
- boolean lower = false;
- switch (schema.getType()) {
- case Boolean:
- key = "booleanValue";
- break;
-
- case Date:
- key = "dateValue";
- break;
-
- case Double:
- key = "doubleValue";
- break;
-
- case Long:
- key = "longValue";
- break;
-
- case Binary:
- key = "binaryValue";
- break;
-
- default:
- lower = cond.getType() == AttributeCond.Type.IEQ || cond.getType() == AttributeCond.Type.ILIKE;
- key = "stringValue";
- }
+ Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(), cond.getType());
if (!not && cond.getType() == AttributeCond.Type.EQ) {
PlainAttr<?> container = anyUtils.newPlainAttr();
@@ -119,65 +78,16 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
} else {
query.append("attrs ->> 'schema' = ?").append(setParameter(parameters, cond.getSchema())).
append(" AND ").
- append(lower ? "LOWER(" : "").
- append(schema.isUniqueConstraint()
- ? "attrs -> 'uniqueValue'" : "attrValues").
- append(" ->> '").append(key).append("'").
- append(lower ? ")" : "");
-
- switch (cond.getType()) {
- case LIKE:
- case ILIKE:
- if (not) {
- query.append("NOT ");
- }
- query.append(" LIKE ");
- break;
-
- case GE:
- if (not) {
- query.append('<');
- } else {
- query.append(">=");
- }
- break;
-
- case GT:
- if (not) {
- query.append("<=");
- } else {
- query.append('>');
- }
- break;
-
- case LE:
- if (not) {
- query.append('>');
- } else {
- query.append("<=");
- }
- break;
-
- case LT:
- if (not) {
- query.append(">=");
- } else {
- query.append('<');
- }
- break;
-
- case EQ:
- case IEQ:
- default:
- if (not) {
- query.append('!');
- }
- query.append('=');
- }
+ append(schemaInfo.getRight() ? "LOWER(" : "").
+ append(schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues").
+ append(" ->> '").append(schemaInfo.getLeft()).append("'").
+ append(schemaInfo.getRight() ? ")" : "");
+
+ appendOp(query, cond.getType(), not);
- query.append(lower ? "LOWER(" : "").
+ query.append(schemaInfo.getRight() ? "LOWER(" : "").
append("?").append(setParameter(parameters, cond.getExpression())).
- append(lower ? ")" : "");
+ append(schemaInfo.getRight() ? ")" : "");
}
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java
index bcd046d..a27e5e3 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityFactory.java
@@ -140,6 +140,7 @@ public abstract class JPAJSONEntityFactory extends JPAEntityFactory implements I
@Override
public void afterPropertiesSet() throws Exception {
- beanFactory.createBean(jpaJSONAnyDAOClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+ beanFactory.registerSingleton("jpaJSONAnyDAO",
+ beanFactory.createBean(jpaJSONAnyDAOClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false));
}
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MyJPAJSONEntityFactory.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MyJPAJSONEntityFactory.java
new file mode 100644
index 0000000..ca17f7b
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MyJPAJSONEntityFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO;
+import org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnyDAO;
+import org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO;
+
+public class MyJPAJSONEntityFactory extends JPAJSONEntityFactory {
+
+ @Override
+ public Class<? extends AnySearchDAO> anySearchDAOClass() {
+ return MyJPAJSONAnySearchDAO.class;
+ }
+
+ @Override
+ protected Class<? extends JPAJSONAnyDAO> jpaJSONAnyDAOClass() {
+ return MyJPAJSONAnyDAO.class;
+ }
+}
diff --git a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml
new file mode 100644
index 0000000..1c397a0
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
+ http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
+ version="2.0">
+
+ <persistence-unit-metadata>
+ <persistence-unit-defaults>
+ <entity-listeners>
+ <entity-listener class="org.apache.syncope.core.persistence.jpa.validation.entity.EntityValidationListener">
+ <pre-persist method-name="validate"/>
+ <pre-update method-name="validate"/>
+ </entity-listener>
+ </entity-listeners>
+ </persistence-unit-defaults>
+ </persistence-unit-metadata>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject">
+ <attributes>
+ <basic name="plainAttrs">
+ <column column-definition="json"/>
+ <lob/>
+ </basic>
+ </attributes>
+ </entity>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup">
+ <attributes>
+ <basic name="plainAttrs">
+ <column column-definition="json"/>
+ <lob/>
+ </basic>
+ </attributes>
+ </entity>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser">
+ <attributes>
+ <basic name="plainAttrs">
+ <column column-definition="json"/>
+ <lob/>
+ </basic>
+ </attributes>
+ </entity>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.conf.JPAJSONConf">
+ <attributes>
+ <basic name="plainAttrs">
+ <column column-definition="json"/>
+ <lob/>
+ </basic>
+ </attributes>
+ </entity>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup">
+ <attributes>
+ <many-to-one name="userOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser"/>
+ <many-to-one name="groupOwner" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/>
+ </attributes>
+ </entity>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension">
+ <attributes>
+ <many-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/>
+ </attributes>
+ </entity>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership">
+ <attributes>
+ <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser">
+ <join-column name="user_id"/>
+ </many-to-one>
+ <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup">
+ <join-column name="group_id"/>
+ </many-to-one>
+ </attributes>
+ </entity>
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership">
+ <attributes>
+ <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/>
+ </attributes>
+ </entity>
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship">
+ <attributes>
+ <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.user.JPAJSONUser">
+ <join-column name="user_id"/>
+ </many-to-one>
+ <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject">
+ <join-column name="anyObject_id"/>
+ </many-to-one>
+ </attributes>
+ </entity>
+
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership">
+ <attributes>
+ <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject">
+ <join-column name="anyObject_id"/>
+ </many-to-one>
+ <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup">
+ <join-column name="group_id"/>
+ </many-to-one>
+ </attributes>
+ </entity>
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership">
+ <attributes>
+ <one-to-one name="group" target-entity="org.apache.syncope.core.persistence.jpa.entity.group.JPAJSONGroup"/>
+ </attributes>
+ </entity>
+ <entity class="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship">
+ <attributes>
+ <many-to-one name="leftEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject">
+ <join-column name="left_anyObject_id"/>
+ </many-to-one>
+ <many-to-one name="rightEnd" target-entity="org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAJSONAnyObject">
+ <join-column name="right_anyObject_id"/>
+ </many-to-one>
+ </attributes>
+ </entity>
+</entity-mappings>
diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties
similarity index 76%
copy from fit/core-reference/src/main/resources/mysql/domains/Master.properties
copy to core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties
index ce09eec..d945b5a 100644
--- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties
+++ b/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties
@@ -14,15 +14,15 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-Master.driverClassName=com.mysql.jdbc.Driver
-Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true
+Master.driverClassName=com.mysql.cj.jdbc.Driver
+Master.url=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
Master.schema=
Master.username=syncope
Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB)
-Master.orm=META-INF/spring-orm.xml
+Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,timestampTypeName=DATETIME(3))
+Master.orm=META-INF/spring-orm-myjson.xml
Master.pool.maxActive=10
Master.pool.minIdle=2
-Master.audit.sql=audit.sql
+Master.audit.sql=audit_mysql_innodb.sql
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
new file mode 100644
index 0000000..b16004d
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+ <comment>Additional indexes (in respect to JPA's)</comment>
+
+ <entry key="UDynGroupMembers_any_id">CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id)</entry>
+ <entry key="UDynGroupMembers_group_id">CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id)</entry>
+ <entry key="ADynGroupMembers_any_id">CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id)</entry>
+ <entry key="ADynGroupMembers_group_id">CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id)</entry>
+
+ <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry>
+ <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry>
+
+ <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry>
+ <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry>
+
+ <entry key="CPlainAttrValue_stringvalueIndex">CREATE INDEX CAttrValue_stringvalueIndex ON CPlainAttrValue(stringvalue)</entry>
+ <entry key="CPlainAttrValue_datevalueIndex">CREATE INDEX CAttrValue_datevalueIndex ON CPlainAttrValue(datevalue)</entry>
+ <entry key="CPlainAttrValue_longvalueIndex">CREATE INDEX CAttrValue_longvalueIndex ON CPlainAttrValue(longvalue)</entry>
+ <entry key="CPlainAttrValue_doublevalueIndex">CREATE INDEX CAttrValue_doublevalueIndex ON CPlainAttrValue(doublevalue)</entry>
+ <entry key="CPlainAttrValue_booleanvalueIndex">CREATE INDEX CAttrValue_booleanvalueIndex ON CPlainAttrValue(booleanvalue)</entry>
+
+ <entry key="UMembership_GroupIndex">CREATE INDEX UMembership_GroupIndex ON UMembership(group_id)</entry>
+ <entry key="UMembership_UserIndex">CREATE INDEX UMembership_UserIndex ON UMembership(user_id)</entry>
+ <entry key="AMembership_GroupIndex">CREATE INDEX AMembership_GroupIndex ON AMembership(group_id)</entry>
+ <entry key="AMembership_AnyObjectIndex">CREATE INDEX AMembership_AnyObjectIndex ON AMembership(anyObject_id)</entry>
+
+ <entry key="URelationship_RightIndex">CREATE INDEX URelationship_RightIndex ON URelationship(anyObject_id)</entry>
+ <entry key="URelationship_LeftIndex">CREATE INDEX URelationship_LeftIndex ON URelationship(user_id)</entry>
+ <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry>
+ <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry>
+
+ <entry key="CPlainAttrValue_attrIndex">CREATE INDEX CPlainAttrValue_attrIndex on CPlainAttrValue(attribute_id)</entry>
+ <entry key="CPAttrUniqueValue_attrIndex">CREATE INDEX CPAttrUniqueValue_attrIndex on CPlainAttrUniqueValue(attribute_id)</entry>
+
+ <entry key="CPlainAttr_owner_Index">CREATE INDEX CPlainAttr_owner_Index on CPlainAttr(owner_id)</entry>
+ <entry key="CPlainAttr_schema_Index">CREATE INDEX CPlainAttr_schema_Index on CPlainAttr(schema_id)</entry>
+
+ <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
+</properties>
diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
similarity index 50%
copy from fit/core-reference/src/main/resources/mysql/domains/Master.properties
copy to core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
index ce09eec..31a2dd8 100644
--- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties
+++ b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
@@ -14,15 +14,14 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-Master.driverClassName=com.mysql.jdbc.Driver
-Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB)
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit.sql
+content.directory=${conf.directory}
+entity.factory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory
+plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainSchemaDAO
+plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
+plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO
+user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
+group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
+anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
+conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONConfDAO
+openjpa.RemoteCommitProvider=sjvm
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/views.xml b/core/persistence-jpa-json/src/main/resources/myjson/views.xml
new file mode 100644
index 0000000..aa0e3c0
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/resources/myjson/views.xml
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+<properties>
+
+ <entry key="UDynGroupMembers">
+ CREATE TABLE UDynGroupMembers(
+ any_id CHAR(36),
+ group_id CHAR(36),
+ UNIQUE(any_id, group_id))
+ </entry>
+ <entry key="ADynGroupMembers">
+ CREATE TABLE ADynGroupMembers(
+ anyType_id VARCHAR(255),
+ any_id CHAR(36),
+ group_id CHAR(36),
+ UNIQUE(anyType_id, any_id, group_id))
+ </entry>
+ <entry key="DynRoleMembers">
+ CREATE TABLE DynRoleMembers(
+ any_id CHAR(36),
+ role_id VARCHAR(255),
+ UNIQUE(any_id, role_id))
+ </entry>
+ <entry key="DynRealmMembers">
+ CREATE TABLE DynRealmMembers(
+ any_id CHAR(36),
+ dynRealm_id VARCHAR(255),
+ UNIQUE(any_id, dynRealm_id))
+ </entry>
+
+ <!-- user -->
+ <entry key="user_search">
+ CREATE VIEW user_search AS
+
+ SELECT u.id as any_id, u.*, attrs.*
+ FROM SyncopeUser u, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
+ plainSchema VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.values[*]' COLUMNS (
+ binaryValue LONGBLOB PATH '$.binaryValue',
+ booleanValue INT PATH '$.booleanValue',
+ dateValue BIGINT(20) PATH '$.dateValue',
+ doubleValue DOUBLE PATH '$.doubleValue',
+ longValue BIGINT(20) PATH '$.longValue',
+ stringValue VARCHAR(255) PATH '$.stringValue'),
+ attrUniqueValue JSON PATH '$.uniqueValue')
+ ) AS attrs
+ </entry>
+ <entry key="user_search_urelationship">
+ CREATE VIEW user_search_urelationship AS
+
+ SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
+ FROM URelationship m
+ </entry>
+ <entry key="user_search_umembership">
+ CREATE VIEW user_search_umembership AS
+
+ SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
+ FROM UMembership m, SyncopeGroup g
+ WHERE m.group_id = g.id
+ </entry>
+ <entry key="user_search_role">
+ CREATE VIEW user_search_role AS
+
+ SELECT ss.user_id AS any_id, ss.role_id AS role_id
+ FROM SyncopeUser_SyncopeRole ss
+ </entry>
+ <entry key="user_search_priv">
+ CREATE VIEW user_search_priv AS
+
+ SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
+ FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
+ WHERE ss.role_id = sp.role_id
+ </entry>
+ <entry key="user_search_dynpriv">
+ CREATE VIEW user_search_dynpriv AS
+
+ SELECT any_id, privilege_id
+ FROM DynRoleMembers drm, SyncopeRole_Privilege rp
+ WHERE drm.role_id = rp.role_id
+ </entry>
+ <entry key="user_search_resource">
+ CREATE VIEW user_search_resource AS
+
+ SELECT st.user_id AS any_id, st.resource_id AS resource_id
+ FROM SyncopeUser_ExternalResource st
+ </entry>
+ <entry key="user_search_group_res">
+ CREATE VIEW user_search_group_res AS
+
+ SELECT m.user_id AS any_id, st.resource_id AS resource_id
+ FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
+ WHERE m.group_id = r.id AND st.group_id = r.id
+ </entry>
+
+ <!-- anyObject -->
+ <entry key="anyObject_search">
+ CREATE VIEW anyObject_search AS
+
+ SELECT a.id as any_id, a.*, attrs.*
+ FROM AnyObject a, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
+ plainSchema VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.values[*]' COLUMNS (
+ binaryValue LONGBLOB PATH '$.binaryValue',
+ booleanValue INT PATH '$.booleanValue',
+ dateValue BIGINT(20) PATH '$.dateValue',
+ doubleValue DOUBLE PATH '$.doubleValue',
+ longValue BIGINT(20) PATH '$.longValue',
+ stringValue VARCHAR(255) PATH '$.stringValue'),
+ attrUniqueValue JSON PATH '$.uniqueValue')
+ ) AS attrs
+ </entry>
+ <entry key="anyObject_search_arelationship">
+ CREATE VIEW anyObject_search_arelationship AS
+
+ SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
+ FROM ARelationship m
+ </entry>
+ <entry key="anyObject_search_amembership">
+ CREATE VIEW anyObject_search_amembership AS
+
+ SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
+ FROM AMembership m, SyncopeGroup g
+ WHERE m.group_id = g.id
+ </entry>
+ <entry key="anyObject_search_resource">
+ CREATE VIEW anyObject_search_resource AS
+
+ SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
+ FROM AnyObject_ExternalResource st
+ </entry>
+ <entry key="anyObject_search_group_res">
+ CREATE VIEW anyObject_search_group_res AS
+
+ SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
+ FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
+ WHERE m.group_id = r.id AND st.group_id = r.id
+ </entry>
+
+ <!-- group -->
+ <entry key="group_search">
+ CREATE VIEW group_search AS
+
+ SELECT g.id as any_id, g.*, attrs.*
+ FROM SyncopeGroup g, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
+ plainSchema VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.values[*]' COLUMNS (
+ binaryValue LONGBLOB PATH '$.binaryValue',
+ booleanValue INT PATH '$.booleanValue',
+ dateValue BIGINT(20) PATH '$.dateValue',
+ doubleValue DOUBLE PATH '$.doubleValue',
+ longValue BIGINT(20) PATH '$.longValue',
+ stringValue VARCHAR(255) PATH '$.stringValue'),
+ attrUniqueValue JSON PATH '$.uniqueValue')
+ ) AS attrs
+ </entry>
+ <entry key="group_search_resource">
+ CREATE VIEW group_search_resource AS
+
+ SELECT st.group_id AS any_id, st.resource_id AS resource_id
+ FROM SyncopeGroup_ExternalResource st
+ </entry>
+
+</properties>
diff --git a/core/persistence-jpa-json/src/test/resources/pgjsonb/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
similarity index 100%
rename from core/persistence-jpa-json/src/test/resources/pgjsonb/domains/MasterContent.xml
rename to core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 02e2535..fcf3bba 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -166,6 +166,8 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
// 1. get the query string from the search condition
StringBuilder queryString = getQuery(buildEffectiveCond(cond, filter.getRight()), parameters, svs);
+ LOG.debug("Query: {}, parameters: {}", queryString, parameters);
+
// 2. take into account realms and ordering
OrderBySupport obs = parseOrderBy(kind, svs, orderBy);
if (queryString.charAt(0) == '(') {
@@ -179,6 +181,8 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
append(filter.getLeft()).
append(buildOrderBy(obs));
+ LOG.debug("Query with auth and order by statements: {}, parameters: {}", queryString, parameters);
+
// 3. prepare the search query
Query query = entityManager().createNativeQuery(queryString.toString());
diff --git a/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql b/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql
index ff753fa..4d8426c 100644
--- a/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql
+++ b/core/persistence-jpa/src/main/resources/audit/audit_mysql_innodb.sql
@@ -21,4 +21,4 @@ CREATE TABLE IF NOT EXISTS SYNCOPEAUDIT (
LOGGER VARCHAR(255) NOT NULL,
MESSAGE TEXT NOT NULL,
THROWABLE TEXT
-) ENGINE=InnoDB
+) ENGINE=InnoDB;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java
index 1db62d8..26b1da9 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java
@@ -90,6 +90,7 @@ public class AnyTypeTest extends AbstractTest {
newType.setKey("new type");
newType.setKind(AnyTypeKind.USER);
anyTypeDAO.save(newType);
+ entityManager().flush();
});
}
@@ -100,6 +101,7 @@ public class AnyTypeTest extends AbstractTest {
newType.setKey("group");
newType.setKind(AnyTypeKind.ANY_OBJECT);
anyTypeDAO.save(newType);
+ entityManager().flush();
});
}
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 85c992c..645dcca 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -724,7 +724,7 @@ under the License.
<filtering>true</filtering>
</resource>
<resource>
- <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/pgjsonb/domains</directory>
+ <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/domains</directory>
<targetPath>${project.build.directory}/classes/domains</targetPath>
<filtering>true</filtering>
</resource>
@@ -851,7 +851,165 @@ under the License.
</testResources>
</build>
</profile>
+
+ <profile>
+ <id>myjson-it</id>
+
+ <properties>
+ <jdbcdriver.groupId>mysql</jdbcdriver.groupId>
+ <jdbcdriver.artifactId>mysql-connector-java</jdbcdriver.artifactId>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-persistence-jpa-json</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ <version>${jdbc.mysql.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <defaultGoal>clean verify</defaultGoal>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <packagingExcludes>WEB-INF/classes/domains/Two*</packagingExcludes>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <inherited>true</inherited>
+ <executions>
+ <execution>
+ <id>remove-domain-Two</id>
+ <phase>prepare-package</phase>
+ <configuration>
+ <target>
+ <delete>
+ <fileset dir="${project.build.directory}/classes/domains" includes="Two*"/>
+ </delete>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <configuration>
+ <images>
+ <image>
+ <name>mysql/mysql-server:${docker.mysql.version}</name>
+ <run>
+ <cmd>--skip-log-bin --server-id=1</cmd>
+ <env>
+ <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD>
+ <MYSQL_DATABASE>syncope</MYSQL_DATABASE>
+ <MYSQL_USER>syncope</MYSQL_USER>
+ <MYSQL_PASSWORD>syncope</MYSQL_PASSWORD>
+ </env>
+ <ports>
+ <port>3306:3306</port>
+ </ports>
+ <wait>
+ <log>MySQL init process done. Ready for start up.</log>
+ <time>30000</time>
+ </wait>
+ </run>
+ </image>
+ </images>
+ </configuration>
+ <executions>
+ <execution>
+ <id>start-mysql</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>stop-mysql</id>
+ <phase>post-integration-test</phase>
+ <goals>
+ <goal>stop</goal>
+ <goal>remove</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.cargo</groupId>
+ <artifactId>cargo-maven2-plugin</artifactId>
+ <inherited>true</inherited>
+ <executions>
+ <execution>
+ <id>start-container</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>stop-container</id>
+ <phase>post-integration-test</phase>
+ <goals>
+ <goal>stop</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ <excludes>
+ <exclude>provisioning.properties</exclude>
+ <exclude>indexes.xml</exclude>
+ <exclude>views.xml</exclude>
+ </excludes>
+ </resource>
+ <resource>
+ <directory>src/main/resources/myjson</directory>
+ <filtering>true</filtering>
+ </resource>
+ <resource>
+ <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/domains</directory>
+ <targetPath>${project.build.directory}/classes/domains</targetPath>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <testResources>
+ <testResource>
+ <directory>${basedir}/../../core/persistence-jpa-json/src/main/resources/myjson</directory>
+ <includes>
+ <include>persistence.properties</include>
+ <include>indexes.xml</include>
+ <include>views.xml</include>
+ </includes>
+ </testResource>
+ </testResources>
+ </build>
+ </profile>
+
<profile>
<id>mysql-it</id>
@@ -879,9 +1037,11 @@ under the License.
<configuration>
<images>
<image>
- <name>mysql/mysql-server:5.7</name>
+ <name>mysql/mysql-server:${docker.mysql.version}</name>
<run>
+ <cmd>--skip-log-bin --server-id=1</cmd>
<env>
+ <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD>
<MYSQL_DATABASE>syncope</MYSQL_DATABASE>
<MYSQL_USER>syncope</MYSQL_USER>
<MYSQL_PASSWORD>syncope</MYSQL_PASSWORD>
@@ -889,6 +1049,10 @@ under the License.
<ports>
<port>3306:3306</port>
</ports>
+ <wait>
+ <log>MySQL init process done. Ready for start up.</log>
+ <time>30000</time>
+ </wait>
</run>
</image>
</images>
diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/fit/core-reference/src/main/resources/myjson/domains/Master.properties
similarity index 76%
copy from fit/core-reference/src/main/resources/mysql/domains/Master.properties
copy to fit/core-reference/src/main/resources/myjson/domains/Master.properties
index ce09eec..d945b5a 100644
--- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties
+++ b/fit/core-reference/src/main/resources/myjson/domains/Master.properties
@@ -14,15 +14,15 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-Master.driverClassName=com.mysql.jdbc.Driver
-Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true
+Master.driverClassName=com.mysql.cj.jdbc.Driver
+Master.url=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
Master.schema=
Master.username=syncope
Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB)
-Master.orm=META-INF/spring-orm.xml
+Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,timestampTypeName=DATETIME(3))
+Master.orm=META-INF/spring-orm-myjson.xml
Master.pool.maxActive=10
Master.pool.minIdle=2
-Master.audit.sql=audit.sql
+Master.audit.sql=audit_mysql_innodb.sql
diff --git a/fit/core-reference/src/main/resources/mysql/provisioning.properties b/fit/core-reference/src/main/resources/myjson/provisioning.properties
similarity index 97%
copy from fit/core-reference/src/main/resources/mysql/provisioning.properties
copy to fit/core-reference/src/main/resources/myjson/provisioning.properties
index 48f9ef6..3883cb5 100644
--- a/fit/core-reference/src/main/resources/mysql/provisioning.properties
+++ b/fit/core-reference/src/main/resources/myjson/provisioning.properties
@@ -27,6 +27,6 @@ anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAn
virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mysql.sql
+quartz.sql=tables_mysql_innodb.sql
quartz.scheduler.idleWaitTime=5000
quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/fit/core-reference/src/main/resources/mysql/domains/Master.properties
index ce09eec..e36e370 100644
--- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties
+++ b/fit/core-reference/src/main/resources/mysql/domains/Master.properties
@@ -14,15 +14,15 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-Master.driverClassName=com.mysql.jdbc.Driver
-Master.url=jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true
+Master.driverClassName=com.mysql.cj.jdbc.Driver
+Master.url=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
Master.schema=
Master.username=syncope
Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB)
+Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,timestampTypeName=DATETIME(3))
Master.orm=META-INF/spring-orm.xml
Master.pool.maxActive=10
Master.pool.minIdle=2
-Master.audit.sql=audit.sql
+Master.audit.sql=audit_mysql_innodb.sql
diff --git a/fit/core-reference/src/main/resources/mysql/provisioning.properties b/fit/core-reference/src/main/resources/mysql/provisioning.properties
index 48f9ef6..3883cb5 100644
--- a/fit/core-reference/src/main/resources/mysql/provisioning.properties
+++ b/fit/core-reference/src/main/resources/mysql/provisioning.properties
@@ -27,6 +27,6 @@ anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAn
virAttrCache=org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache
quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mysql.sql
+quartz.sql=tables_mysql_innodb.sql
quartz.scheduler.idleWaitTime=5000
quartz.disableInstance=false
diff --git a/pom.xml b/pom.xml
index 25df74d..5293fff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -396,7 +396,7 @@ under the License.
<spring.version>5.1.3.RELEASE</spring.version>
<spring-security.version>5.1.2.RELEASE</spring-security.version>
- <openjpa.version>3.0.0</openjpa.version>
+ <openjpa.version>3.0.1-SNAPSHOT</openjpa.version>
<hikaricp.version>3.2.0</hikaricp.version>
<bval.version>1.1.2</bval.version>
@@ -518,10 +518,11 @@ under the License.
<protractor.version>5.4.0</protractor.version>
<docker.postgresql.version>11.1</docker.postgresql.version>
+ <docker.mysql.version>8.0</docker.mysql.version>
<docker.mariadb.version>10.4</docker.mariadb.version>
<jdbc.postgresql.version>42.2.5</jdbc.postgresql.version>
- <jdbc.mysql.version>5.1.47</jdbc.mysql.version>
+ <jdbc.mysql.version>8.0.13</jdbc.mysql.version>
<jdbc.mariadb.version>2.3.0</jdbc.mariadb.version>
<jdbc.mssql.version>6.4.0.jre</jdbc.mssql.version>