This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 3d595b642d [SYNCOPE-1662] Introducing JSON support for MariaDB (#798)
3d595b642d is described below
commit 3d595b642d82c17ee44de499d73bab0da6a86a1a
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Mon Jul 29 14:02:57 2024 +0200
[SYNCOPE-1662] Introducing JSON support for MariaDB (#798)
---
.github/workflows/mariadb.yml | 28 +++
core/persistence-jpa-json/pom.xml | 116 ++++++++-
.../jpa/MaJPAJSONPersistenceContext.java | 98 ++++++++
.../persistence/jpa/dao/AbstractJPAJSONAnyDAO.java | 45 ++--
.../core/persistence/jpa/dao/MaJPAJSONAnyDAO.java | 81 +++++++
.../persistence/jpa/dao/MaJPAJSONAnySearchDAO.java | 144 +++++++++++
.../core/persistence/jpa/dao/MyJPAJSONAnyDAO.java | 9 +-
.../core/persistence/jpa/dao/PGJPAJSONAnyDAO.java | 9 +-
.../jpa/dao/repo/PlainSchemaRepoExtMaJSONImpl.java | 47 ++++
.../jpa/entity/MaJPAJSONEntityFactory.java | 30 +++
.../main/resources/META-INF/spring-orm-majson.xml | 137 +++++++++++
...rk.boot.autoconfigure.AutoConfiguration.imports | 1 +
...onfiguration.imports => core-majson.properties} | 19 +-
.../src/main/resources/majson/indexes.xml | 63 +++++
.../src/main/resources/majson/views.xml | 262 +++++++++++++++++++++
.../jpa/JPAJSONTestContextCustomizer.java | 1 +
.../resources/core-majson-test.properties} | 17 +-
.../core/persistence/jpa/dao/JPAAnySearchDAO.java | 22 +-
.../jpa/dao/repo/AbstractAnyRepoExt.java | 7 +-
.../core/persistence/jpa/inner/UserTest.java | 4 +-
.../core/src/main/resources/core-majson.properties | 19 +-
...mpose-mariadb.yml => docker-compose-majson.yml} | 9 +-
.../docker-compose/docker-compose-mariadb.yml | 1 +
fit/core-reference/pom.xml | 117 +++++++++
.../src/main/resources/core-majson.properties | 34 +++
pom.xml | 2 +-
.../reference-guide/configuration/dbms.adoc | 64 +++++
src/site/xdoc/building.xml | 2 +-
28 files changed, 1319 insertions(+), 69 deletions(-)
diff --git a/.github/workflows/mariadb.yml b/.github/workflows/mariadb.yml
index b6c356d020..94b3711709 100644
--- a/.github/workflows/mariadb.yml
+++ b/.github/workflows/mariadb.yml
@@ -51,3 +51,31 @@ jobs:
run: mvn -U -T 1C -P 'skipTests,all'
- name: 'Integration Tests: MariaDB'
run: mvn -f fit/core-reference/pom.xml -P mariadb-it
-Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true
-Dcheckstyle.skip=true -Djacoco.skip=true
+
+ majson:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Setup Java JDK
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: 21
+ - name: Setup Maven
+ uses: stCarolas/setup-maven@v5
+ with:
+ maven-version: 3.9.6
+ - uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+ - name: Build
+ run: mvn -U -T 1C -P 'skipTests,all'
+ - name: 'Unit Tests: MariaDB JPA JSON'
+ run: mvn -f core/persistence-jpa-json/pom.xml -P majson
-Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none
-Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+ - name: 'Integration Tests: MariaDB JPA JSON'
+ run: mvn -f fit/core-reference/pom.xml -P majson-it
-Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true
-Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/core/persistence-jpa-json/pom.xml
b/core/persistence-jpa-json/pom.xml
index d5231fef0f..a2b153959f 100644
--- a/core/persistence-jpa-json/pom.xml
+++ b/core/persistence-jpa-json/pom.xml
@@ -165,7 +165,7 @@ under the License.
<!-- possible values: sql | schema -->
<action>sql</action>
- <!-- possible values: pgjsonb | myjson | ojson -->
+ <!-- possible values: pgjsonb | myjson | majson | ojson -->
<orm>ojson</orm>
<skipTests>true</skipTests>
@@ -422,6 +422,120 @@ under the License.
</build>
</profile>
+ <profile>
+ <id>majson</id>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.mariadb.jdbc</groupId>
+ <artifactId>mariadb-java-client</artifactId>
+ <version>${jdbc.mariadb.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <defaultGoal>clean verify</defaultGoal>
+
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-test-source</id>
+ <phase>generate-test-sources</phase>
+ <goals>
+ <goal>add-test-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+
<source>${basedir}/../persistence-jpa/src/test/java</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <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>
+ <systemPropertyVariables>
+ <profileId>${project.activeProfiles[0].id}</profileId>
+
<CORE_PROPERTIES>classpath:core-majson.properties,classpath:core-majson-test.properties</CORE_PROPERTIES>
+
<DB_CONTAINER_IP>${docker.container.mariadb.ip}</DB_CONTAINER_IP>
+
<syncope.connid.location>file:${bundles.directory}/</syncope.connid.location>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <configuration>
+ <images>
+ <image>
+ <alias>mariadb</alias>
+ <name>mariadb:${docker.mariadb.version}</name>
+ <run>
+ <env>
+ <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD>
+ <MYSQL_DATABASE>syncope</MYSQL_DATABASE>
+ <MYSQL_USER>syncope</MYSQL_USER>
+ <MYSQL_PASSWORD>syncope</MYSQL_PASSWORD>
+ </env>
+ <tmpfs>
+ <mount>/var/lib/mysql:rw</mount>
+ </tmpfs>
+ <wait>
+ <log>MariaDB init process done. Ready for start up.</log>
+ <time>30000</time>
+ </wait>
+ </run>
+ </image>
+ </images>
+ </configuration>
+ <executions>
+ <execution>
+ <id>start-mariadb</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>stop-mariadb</id>
+ <phase>post-integration-test</phase>
+ <goals>
+ <goal>stop</goal>
+ <goal>remove</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
+ </build>
+ </profile>
+
<profile>
<id>ojson</id>
diff --git
a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MaJPAJSONPersistenceContext.java
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MaJPAJSONPersistenceContext.java
new file mode 100644
index 0000000000..1e59da7977
--- /dev/null
+++
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MaJPAJSONPersistenceContext.java
@@ -0,0 +1,98 @@
+/*
+ * 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;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import
org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+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.dao.RealmSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.jpa.dao.MaJPAJSONAnyDAO;
+import org.apache.syncope.core.persistence.jpa.dao.MaJPAJSONAnySearchDAO;
+import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt;
+import
org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExtMaJSONImpl;
+import org.apache.syncope.core.persistence.jpa.entity.MaJPAJSONEntityFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Lazy;
+
+@ConditionalOnClass(name = "org.mariadb.jdbc.Driver")
+public class MaJPAJSONPersistenceContext extends JPAJSONPersistenceContext {
+
+ @ConditionalOnMissingBean(name = "maJPAJSONEntityFactory")
+ @Bean
+ public EntityFactory entityFactory() {
+ return new MaJPAJSONEntityFactory();
+ }
+
+ @ConditionalOnMissingBean(name = "maJPAJSONAnyDAO")
+ @Bean
+ public JPAJSONAnyDAO anyDAO(final @Lazy PlainSchemaDAO plainSchemaDAO,
final EntityManager entityManager) {
+ return new MaJPAJSONAnyDAO(plainSchemaDAO, entityManager);
+ }
+
+ @ConditionalOnMissingBean(name = "maJPAJSONAnySearchDAO")
+ @Bean
+ public AnySearchDAO anySearchDAO(
+ final @Lazy RealmSearchDAO realmSearchDAO,
+ final @Lazy DynRealmDAO dynRealmDAO,
+ final @Lazy UserDAO userDAO,
+ final @Lazy GroupDAO groupDAO,
+ final @Lazy AnyObjectDAO anyObjectDAO,
+ final @Lazy PlainSchemaDAO schemaDAO,
+ final @Lazy EntityFactory entityFactory,
+ final AnyUtilsFactory anyUtilsFactory,
+ final PlainAttrValidationManager validator,
+ final EntityManagerFactory entityManagerFactory,
+ final EntityManager entityManager) {
+
+ return new MaJPAJSONAnySearchDAO(
+ realmSearchDAO,
+ dynRealmDAO,
+ userDAO,
+ groupDAO,
+ anyObjectDAO,
+ schemaDAO,
+ entityFactory,
+ anyUtilsFactory,
+ validator,
+ entityManagerFactory,
+ entityManager);
+ }
+
+ @ConditionalOnMissingBean(name = "maJPAJSONPlainSchemaRepoExt")
+ @Bean
+ public PlainSchemaRepoExt plainSchemaRepoExt(
+ final AnyUtilsFactory anyUtilsFactory,
+ final @Lazy ExternalResourceDAO resourceDAO,
+ final EntityManager entityManager) {
+
+ return new PlainSchemaRepoExtMaJSONImpl(anyUtilsFactory, resourceDAO,
entityManager);
+ }
+}
diff --git
a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java
index fb6650c0ab..55d02d1542 100644
---
a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java
+++
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java
@@ -64,7 +64,7 @@ abstract class AbstractJPAJSONAnyDAO implements JPAJSONAnyDAO
{
* @param literals literals/tokens
* @return split value
*/
- protected static List<String> split(final String attrValue, final
List<String> literals) {
+ private static List<String> split(final String attrValue, final
List<String> literals) {
List<String> attrValues = new ArrayList<>();
if (literals.isEmpty()) {
@@ -154,6 +154,20 @@ abstract class AbstractJPAJSONAnyDAO implements
JPAJSONAnyDAO {
return result;
}
+ protected String plainAttrQuery(
+ final String table,
+ final AnyUtils anyUtils,
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final boolean ignoreCaseMatch,
+ final List<Object> queryParams) {
+
+ queryParams.add(schema.getKey());
+ queryParams.add(getAttrValue(schema, attrValue, ignoreCaseMatch));
+
+ return queryBegin(table) + "WHERE " + attrValueMatch(anyUtils, schema,
attrValue, ignoreCaseMatch);
+ }
+
@SuppressWarnings("unchecked")
@Transactional(readOnly = true)
@Override
@@ -169,11 +183,12 @@ abstract class AbstractJPAJSONAnyDAO implements
JPAJSONAnyDAO {
return List.of();
}
+ List<Object> queryParams = new ArrayList<>();
Query query = entityManager.createNativeQuery(
- queryBegin(table)
- + "WHERE " + attrValueMatch(anyUtils, schema, attrValue,
ignoreCaseMatch));
- query.setParameter(1, schema.getKey());
- query.setParameter(2, getAttrValue(schema, attrValue,
ignoreCaseMatch));
+ plainAttrQuery(table, anyUtils, schema, attrValue,
ignoreCaseMatch, queryParams));
+ for (int i = 0; i < queryParams.size(); i++) {
+ query.setParameter(i + 1, queryParams.get(i));
+ }
return buildResult(anyUtils, query.getResultList());
}
@@ -203,7 +218,7 @@ abstract class AbstractJPAJSONAnyDAO implements
JPAJSONAnyDAO {
}
@SuppressWarnings("unchecked")
- protected List<Object> findByDerAttrValue(
+ private List<Object> findByDerAttrValue(
final String table,
final Map<String, List<Object>> clauses) {
@@ -305,26 +320,18 @@ abstract class AbstractJPAJSONAnyDAO implements
JPAJSONAnyDAO {
// clear builder
bld.delete(0, bld.length());
- PlainAttrValue attrValue;
- if (schema.isUniqueConstraint()) {
- attrValue = anyUtils.newPlainAttrUniqueValue();
- } else {
- attrValue = anyUtils.newPlainAttrValue();
- }
+ PlainAttrValue attrValue = schema.isUniqueConstraint()
+ ? anyUtils.newPlainAttrUniqueValue()
+ : anyUtils.newPlainAttrValue();
attrValue.setStringValue(attrValues.get(i));
+ List<Object> queryParams = new ArrayList<>();
bld.append('(').
- append(queryBegin(table)).
- append("WHERE ").
- append(attrValueMatch(anyUtils, schema, attrValue,
ignoreCaseMatch)).
+ append(plainAttrQuery(table, anyUtils, schema,
attrValue, ignoreCaseMatch, queryParams)).
append(')');
used.add(identifiers.get(i));
- List<Object> queryParams = new ArrayList<>();
- queryParams.add(schema.getKey());
- queryParams.add(getAttrValue(schema, attrValue,
ignoreCaseMatch));
-
clauses.put(bld.toString(), queryParams);
}
}
diff --git
a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MaJPAJSONAnyDAO.java
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MaJPAJSONAnyDAO.java
new file mode 100644
index 0000000000..feb05c38a2
--- /dev/null
+++
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MaJPAJSONAnyDAO.java
@@ -0,0 +1,81 @@
+/*
+ * 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 jakarta.persistence.EntityManager;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Optional;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+
+public class MaJPAJSONAnyDAO extends AbstractJPAJSONAnyDAO {
+
+ public MaJPAJSONAnyDAO(final PlainSchemaDAO plainSchemaDAO, final
EntityManager entityManager) {
+ super(plainSchemaDAO, entityManager);
+ }
+
+ @Override
+ protected String queryBegin(final String table) {
+ throw new UnsupportedOperationException("This method shall never be
called");
+ }
+
+ @Override
+ protected String attrValueMatch(
+ final AnyUtils anyUtils,
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final boolean ignoreCaseMatch) {
+
+ throw new UnsupportedOperationException("This method shall never be
called");
+ }
+
+ @Override
+ protected String plainAttrQuery(
+ final String table,
+ final AnyUtils anyUtils,
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final boolean ignoreCaseMatch,
+ final List<Object> queryParams) {
+
+ queryParams.add(schema.getKey());
+ queryParams.add(attrValue.getStringValue());
+ queryParams.add(attrValue.getBooleanValue());
+ queryParams.add(Optional.ofNullable(attrValue.getDateValue()).
+
map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format).orElse(null));
+ queryParams.add(attrValue.getLongValue());
+ queryParams.add(attrValue.getDoubleValue());
+
+ SearchViewSupport svs = new SearchViewSupport(anyUtils.anyTypeKind());
+ return "SELECT DISTINCT any_id FROM "
+ + (schema.isUniqueConstraint() ? svs.uniqueAttr().name() :
svs.attr().name())
+ + " WHERE schema_id = ? AND ((stringValue IS NOT NULL"
+ + " AND "
+ + (ignoreCaseMatch ? "LOWER(" : "BINARY ") + "stringValue" +
(ignoreCaseMatch ? ")" : "")
+ + " = "
+ + (ignoreCaseMatch ? "LOWER(" : "") + "?" + (ignoreCaseMatch ?
")" : "") + ')'
+ + " OR (booleanValue IS NOT NULL AND booleanValue = ?)"
+ + " OR (dateValue IS NOT NULL AND dateValue = ?)"
+ + " OR (longValue IS NOT NULL AND longValue = ?)"
+ + " OR (doubleValue IS NOT NULL AND doubleValue = ?))";
+ }
+}
diff --git
a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MaJPAJSONAnySearchDAO.java
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MaJPAJSONAnySearchDAO.java
new file mode 100644
index 0000000000..d7754950bf
--- /dev/null
+++
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MaJPAJSONAnySearchDAO.java
@@ -0,0 +1,144 @@
+/*
+ * 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 jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Optional;
+import org.apache.commons.lang3.tuple.Pair;
+import
org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
+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;
+
+public class MaJPAJSONAnySearchDAO extends JPAAnySearchDAO {
+
+ public MaJPAJSONAnySearchDAO(
+ final RealmSearchDAO realmSearchDAO,
+ final DynRealmDAO dynRealmDAO,
+ final UserDAO userDAO,
+ final GroupDAO groupDAO,
+ final AnyObjectDAO anyObjectDAO,
+ final PlainSchemaDAO schemaDAO,
+ final EntityFactory entityFactory,
+ final AnyUtilsFactory anyUtilsFactory,
+ final PlainAttrValidationManager validator,
+ final EntityManagerFactory entityManagerFactory,
+ final EntityManager entityManager) {
+
+ super(
+ realmSearchDAO,
+ dynRealmDAO,
+ userDAO,
+ groupDAO,
+ anyObjectDAO,
+ schemaDAO,
+ entityFactory,
+ anyUtilsFactory,
+ validator,
+ entityManagerFactory,
+ entityManager);
+ }
+
+ @Override
+ protected String getQuery(
+ final AttrCond cond,
+ final boolean not,
+ final List<Object> parameters,
+ final SearchSupport svs) {
+
+ Pair<PlainSchema, PlainAttrValue> checked = check(cond,
svs.anyTypeKind);
+
+ // normalize NULL / NOT NULL checks
+ if (not) {
+ if (cond.getType() == AttrCond.Type.ISNULL) {
+ cond.setType(AttrCond.Type.ISNOTNULL);
+ } else if (cond.getType() == AttrCond.Type.ISNOTNULL) {
+ cond.setType(AttrCond.Type.ISNULL);
+ }
+ }
+
+ StringBuilder query =
+ new StringBuilder("SELECT DISTINCT any_id FROM
").append(svs.field().name()).append(" WHERE ");
+ switch (cond.getType()) {
+ case ISNOTNULL:
+ query.append("JSON_SEARCH(plainAttrs, 'one', '").
+ append(checked.getLeft().getKey()).
+ append("', NULL, '$[*].schema') IS NOT NULL");
+ break;
+
+ case ISNULL:
+ query.append("JSON_SEARCH(plainAttrs, 'one', '").
+ append(checked.getLeft().getKey()).
+ append("', NULL, '$[*].schema') IS NULL");
+ break;
+
+ default:
+ if (!not && cond.getType() == AttrCond.Type.EQ) {
+ PlainAttr<?> container =
anyUtilsFactory.getInstance(svs.anyTypeKind).newPlainAttr();
+ container.setSchema(checked.getLeft());
+ if (checked.getRight() instanceof PlainAttrUniqueValue
plainAttrUniqueValue) {
+ container.setUniqueValue(plainAttrUniqueValue);
+ } else {
+ ((JSONPlainAttr) container).add(checked.getRight());
+ }
+
+ query.append("JSON_CONTAINS(plainAttrs, '").
+
append(POJOHelper.serialize(List.of(container)).replace("'", "''")).
+ append("')");
+ } else {
+ query = new StringBuilder("SELECT DISTINCT any_id FROM ");
+ if (not && !(cond instanceof AnyCond) &&
checked.getLeft().isMultivalue()) {
+ query.append(svs.field().name()).append(" WHERE ");
+ } else {
+ query.append((checked.getLeft().isUniqueConstraint()
+ ? svs.asSearchViewSupport().uniqueAttr().name()
+ : svs.asSearchViewSupport().attr().name())).
+ append(" WHERE
schema_id='").append(checked.getLeft().getKey());
+ }
+
+ Optional.ofNullable(checked.getRight().getDateValue()).
+
map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format).
+ ifPresent(formatted -> {
+ checked.getRight().setDateValue(null);
+ checked.getRight().setStringValue(formatted);
+ });
+
+ fillAttrQuery(query, checked.getRight(),
checked.getLeft(), cond, not, parameters, svs);
+ }
+ }
+
+ return query.toString();
+ }
+}
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
index afb3d0fa49..5736eecc56 100644
---
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
@@ -51,16 +51,11 @@ public class MyJPAJSONAnyDAO extends AbstractJPAJSONAnyDAO {
Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(),
ignoreCaseMatch);
if (schemaInfo.getRight()) {
return "plainSchema = ? "
- + "AND "
- + (schemaInfo.getRight() ? "LOWER(" : "")
+ + "AND LOWER("
+ (schema.isUniqueConstraint()
? "attrUniqueValue ->> '$." + schemaInfo.getLeft() + '\''
: schemaInfo.getLeft())
- + (schemaInfo.getRight() ? ")" : "")
- + " = "
- + (schemaInfo.getRight() ? "LOWER(" : "")
- + '?'
- + (schemaInfo.getRight() ? ")" : "");
+ + ") = LOWER(?)";
} else {
PlainAttr<?> container = anyUtils.newPlainAttr();
container.setSchema(schema);
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 1caa7120de..a0d4d91e43 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
@@ -53,15 +53,10 @@ public class PGJPAJSONAnyDAO extends AbstractJPAJSONAnyDAO {
Pair<String, Boolean> schemaInfo = schemaInfo(schema.getType(),
ignoreCaseMatch);
if (schemaInfo.getRight()) {
return "attrs ->> 'schema' = ? "
- + "AND "
- + (schemaInfo.getRight() ? "LOWER(" : "")
+ + "AND LOWER("
+ (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'"
: "attrValues")
+ " ->> '" + schemaInfo.getLeft()
- + '\'' + (schemaInfo.getRight() ? ")" : "")
- + " = "
- + (schemaInfo.getRight() ? "LOWER(" : "")
- + '?'
- + (schemaInfo.getRight() ? ")" : "");
+ + "') = LOWER(?)";
}
PlainAttr<?> container = anyUtils.newPlainAttr();
diff --git
a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExtMaJSONImpl.java
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExtMaJSONImpl.java
new file mode 100644
index 0000000000..de81c1c387
--- /dev/null
+++
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExtMaJSONImpl.java
@@ -0,0 +1,47 @@
+/*
+ * 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.repo;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.Query;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.jpa.dao.SearchSupport;
+
+public class PlainSchemaRepoExtMaJSONImpl extends
AbstractPlainSchemaRepoExtJSON {
+
+ public PlainSchemaRepoExtMaJSONImpl(
+ final AnyUtilsFactory anyUtilsFactory,
+ final ExternalResourceDAO resourceDAO,
+ final EntityManager entityManager) {
+
+ super(anyUtilsFactory, resourceDAO, entityManager);
+ }
+
+ @Override
+ public <T extends PlainAttr<?>> boolean hasAttrs(final PlainSchema schema,
final Class<T> reference) {
+ Query query = entityManager.createNativeQuery(
+ "SELECT COUNT(id) FROM " + new
SearchSupport(getAnyTypeKind(reference)).field().name()
+ + " WHERE JSON_CONTAINS(plainAttrs, '[{\"schema\":\"" +
schema.getKey() + "\"}]')");
+
+ return ((Number) query.getSingleResult()).intValue() > 0;
+ }
+}
diff --git
a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MaJPAJSONEntityFactory.java
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MaJPAJSONEntityFactory.java
new file mode 100644
index 0000000000..c509826cf3
--- /dev/null
+++
b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/MaJPAJSONEntityFactory.java
@@ -0,0 +1,30 @@
+/*
+ * 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.jpa.dao.MaJPAJSONAnySearchDAO;
+
+public class MaJPAJSONEntityFactory extends JPAJSONEntityFactory {
+
+ @Override
+ public Class<? extends AnySearchDAO> anySearchDAOClass() {
+ return MaJPAJSONAnySearchDAO.class;
+ }
+}
diff --git
a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-majson.xml
b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-majson.xml
new file mode 100644
index 0000000000..e80b733863
--- /dev/null
+++
b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-majson.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.openjpa.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.user.JPAJSONLinkedAccount">
+ <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/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
b/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 0a81de30b1..3b77233a78 100644
---
a/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++
b/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -16,4 +16,5 @@
# under the License.
org.apache.syncope.core.persistence.jpa.PGJPAJSONPersistenceContext
org.apache.syncope.core.persistence.jpa.MyJPAJSONPersistenceContext
+org.apache.syncope.core.persistence.jpa.MaJPAJSONPersistenceContext
org.apache.syncope.core.persistence.jpa.OJPAJSONPersistenceContext
diff --git
a/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
b/core/persistence-jpa-json/src/main/resources/core-majson.properties
similarity index 51%
copy from
core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
copy to core/persistence-jpa-json/src/main/resources/core-majson.properties
index 0a81de30b1..6e9074df4b 100644
---
a/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/core/persistence-jpa-json/src/main/resources/core-majson.properties
@@ -14,6 +14,19 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-org.apache.syncope.core.persistence.jpa.PGJPAJSONPersistenceContext
-org.apache.syncope.core.persistence.jpa.MyJPAJSONPersistenceContext
-org.apache.syncope.core.persistence.jpa.OJPAJSONPersistenceContext
+
+persistence.indexesXML=classpath:majson/indexes.xml
+persistence.viewsXML=classpath:majson/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
+persistence.domain[0].jdbcURL=jdbc:mariadb://localhost:3306/syncope?characterEncoding=UTF-8
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].orm=META-INF/spring-orm-majson.xml
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mariadb.sql
diff --git a/core/persistence-jpa-json/src/main/resources/majson/indexes.xml
b/core/persistence-jpa-json/src/main/resources/majson/indexes.xml
new file mode 100644
index 0000000000..8bc611e0ae
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/resources/majson/indexes.xml
@@ -0,0 +1,63 @@
+<?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="Realm_parent_id">CREATE INDEX Realm_parent_id ON
Realm(parent_id)</entry>
+
+ <entry key="SyncopeUser_realm_id">CREATE INDEX SyncopeUser_realm_id ON
SyncopeUser(realm_id)</entry>
+ <entry key="SyncopeUser_username">CREATE UNIQUE INDEX SyncopeUser_username
ON SyncopeUser(username)</entry>
+
+ <entry key="SyncopeGroup_realm_id">CREATE INDEX SyncopeGroup_realm_id ON
SyncopeGroup(realm_id)</entry>
+ <entry key="SyncopeGroup_name">CREATE UNIQUE INDEX SyncopeGroup_name ON
SyncopeGroup(name)</entry>
+
+ <entry key="AnyObject_realm_id">CREATE INDEX AnyObject_realm_id ON
AnyObject(realm_id)</entry>
+ <entry key="AnyObject_name">CREATE UNIQUE INDEX AnyObject_name ON
AnyObject(type_id,name)</entry>
+
+ <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="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="Task_executedIndex">CREATE INDEX Task_executedIndex ON
NotificationTask(executed)</entry>
+ <entry key="TaskExec1_TaskIdIndex">CREATE INDEX TaskExec1_TaskIdIndex ON
PropagationTaskExec(task_id)</entry>
+ <entry key="TaskExec2_TaskIdIndex">CREATE INDEX TaskExec2_TaskIdIndex ON
PullTaskExec(task_id)</entry>
+ <entry key="TaskExec3_TaskIdIndex">CREATE INDEX TaskExec3_TaskIdIndex ON
PushTaskExec(task_id)</entry>
+ <entry key="TaskExec4_TaskIdIndex">CREATE INDEX TaskExec4_TaskIdIndex ON
NotificationTaskExec(task_id)</entry>
+ <entry key="TaskExec5_TaskIdIndex">CREATE INDEX TaskExec5_TaskIdIndex ON
SchedTaskExec(task_id)</entry>
+ <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex
ON AnyTemplatePullTask(pullTask_id)</entry>
+</properties>
diff --git a/core/persistence-jpa-json/src/main/resources/majson/views.xml
b/core/persistence-jpa-json/src/main/resources/majson/views.xml
new file mode 100644
index 0000000000..ca89fe82ae
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/resources/majson/views.xml
@@ -0,0 +1,262 @@
+<?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.* FROM SyncopeUser u
+ </entry>
+ <entry key="user_search_unique_attr">
+ CREATE VIEW user_search_unique_attr AS
+
+ SELECT u.id as any_id, attrs.*
+ FROM SyncopeUser u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'),
'$[*]' COLUMNS (
+ schema_id VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.uniqueValue' COLUMNS (
+ booleanvalue INT PATH '$.booleanValue',
+ datevalue VARCHAR(32) PATH '$.dateValue',
+ doublevalue DOUBLE PATH '$.doubleValue',
+ longvalue BIGINT(20) PATH '$.longValue',
+ stringvalue VARCHAR(255) PATH '$.stringValue'))
+ ) AS attrs ON 1=1
+ WHERE schema_id IS NOT NULL
+ AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS
NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
+ </entry>
+ <entry key="user_search_attr">
+ CREATE VIEW user_search_attr AS
+
+ SELECT u.id as any_id, attrs.*
+ FROM SyncopeUser u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'),
'$[*]' COLUMNS (
+ schema_id VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.values[*]' COLUMNS (
+ booleanvalue INT PATH '$.booleanValue',
+ datevalue VARCHAR(32) PATH '$.dateValue',
+ doublevalue DOUBLE PATH '$.doubleValue',
+ longvalue BIGINT(20) PATH '$.longValue',
+ stringvalue VARCHAR(255) PATH '$.stringValue'))
+ ) AS attrs ON 1=1
+ WHERE schema_id IS NOT NULL
+ AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS
NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
+ </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_auxClass">
+ CREATE VIEW user_search_auxClass AS
+
+ SELECT st.user_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+ FROM SyncopeUser_AnyTypeClass st
+ </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.* FROM AnyObject a
+ </entry>
+ <entry key="anyObject_search_unique_attr">
+ CREATE VIEW anyObject_search_unique_attr AS
+
+ SELECT u.id as any_id, attrs.*
+ FROM AnyObject u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'),
'$[*]' COLUMNS (
+ schema_id VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.uniqueValue' COLUMNS (
+ booleanvalue INT PATH '$.booleanValue',
+ datevalue VARCHAR(32) PATH '$.dateValue',
+ doublevalue DOUBLE PATH '$.doubleValue',
+ longvalue BIGINT(20) PATH '$.longValue',
+ stringvalue VARCHAR(255) PATH '$.stringValue'))
+ ) AS attrs ON 1=1
+ WHERE schema_id IS NOT NULL
+ AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS
NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
+ </entry>
+ <entry key="anyObject_search_attr">
+ CREATE VIEW anyObject_search_attr AS
+
+ SELECT u.id as any_id, attrs.*
+ FROM AnyObject u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'),
'$[*]' COLUMNS (
+ schema_id VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.values[*]' COLUMNS (
+ booleanvalue INT PATH '$.booleanValue',
+ datevalue VARCHAR(32) PATH '$.dateValue',
+ doublevalue DOUBLE PATH '$.doubleValue',
+ longvalue BIGINT(20) PATH '$.longValue',
+ stringvalue VARCHAR(255) PATH '$.stringValue'))
+ ) AS attrs ON 1=1
+ WHERE schema_id IS NOT NULL
+ AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS
NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
+ </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_auxClass">
+ CREATE VIEW anyObject_search_auxClass AS
+
+ SELECT st.anyObject_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+ FROM AnyObject_AnyTypeClass st
+ </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 r.id as any_id, r.* FROM SyncopeGroup r
+ </entry>
+ <entry key="group_search_unique_attr">
+ CREATE VIEW group_search_unique_attr AS
+
+ SELECT u.id as any_id, attrs.*
+ FROM SyncopeGroup u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'),
'$[*]' COLUMNS (
+ schema_id VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.uniqueValue' COLUMNS (
+ booleanvalue INT PATH '$.booleanValue',
+ datevalue VARCHAR(32) PATH '$.dateValue',
+ doublevalue DOUBLE PATH '$.doubleValue',
+ longvalue BIGINT(20) PATH '$.longValue',
+ stringvalue VARCHAR(255) PATH '$.stringValue'))
+ ) AS attrs ON 1=1
+ WHERE schema_id IS NOT NULL
+ AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS
NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
+ </entry>
+ <entry key="group_search_attr">
+ CREATE VIEW group_search_attr AS
+
+ SELECT u.id as any_id, attrs.*
+ FROM SyncopeGroup u LEFT OUTER JOIN JSON_TABLE(COALESCE(plainAttrs, '[]'),
'$[*]' COLUMNS (
+ schema_id VARCHAR(255) PATH '$.schema',
+ NESTED PATH '$.values[*]' COLUMNS (
+ booleanvalue INT PATH '$.booleanValue',
+ datevalue VARCHAR(32) PATH '$.dateValue',
+ doublevalue DOUBLE PATH '$.doubleValue',
+ longvalue BIGINT(20) PATH '$.longValue',
+ stringvalue VARCHAR(255) PATH '$.stringValue'))
+ ) AS attrs ON 1=1
+ WHERE schema_id IS NOT NULL
+ AND (booleanvalue IS NOT NULL OR datevalue IS NOT NULL OR doublevalue IS
NOT NULL OR longvalue IS NOT NULL OR stringvalue IS NOT NULL)
+ </entry>
+ <entry key="group_search_auxClass">
+ CREATE VIEW group_search_auxClass AS
+
+ SELECT st.group_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+ FROM SyncopeGroup_AnyTypeClass st
+ </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/java/org/apache/syncope/core/persistence/jpa/JPAJSONTestContextCustomizer.java
b/core/persistence-jpa-json/src/test/java/org/apache/syncope/core/persistence/jpa/JPAJSONTestContextCustomizer.java
index df3f61773f..d0d62325bb 100644
---
a/core/persistence-jpa-json/src/test/java/org/apache/syncope/core/persistence/jpa/JPAJSONTestContextCustomizer.java
+++
b/core/persistence-jpa-json/src/test/java/org/apache/syncope/core/persistence/jpa/JPAJSONTestContextCustomizer.java
@@ -42,6 +42,7 @@ public class JPAJSONTestContextCustomizer implements
ContextCustomizer {
AnnotatedBeanDefinitionReader reader = new
AnnotatedBeanDefinitionReader(getBeanDefinitionRegistry(ctx));
reader.registerBean(PGJPAJSONPersistenceContext.class,
"PGJPAJSONPersistenceContext");
reader.registerBean(MyJPAJSONPersistenceContext.class,
"MyJPAJSONPersistenceContext");
+ reader.registerBean(MaJPAJSONPersistenceContext.class,
"MaJPAJSONPersistenceContext");
reader.registerBean(OJPAJSONPersistenceContext.class,
"OJPAJSONPersistenceContext");
}
}
diff --git
a/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
b/core/persistence-jpa-json/src/test/resources/core-majson-test.properties
similarity index 59%
copy from
core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
copy to core/persistence-jpa-json/src/test/resources/core-majson-test.properties
index 0a81de30b1..9f235bece1 100644
---
a/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/core/persistence-jpa-json/src/test/resources/core-majson-test.properties
@@ -14,6 +14,17 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-org.apache.syncope.core.persistence.jpa.PGJPAJSONPersistenceContext
-org.apache.syncope.core.persistence.jpa.MyJPAJSONPersistenceContext
-org.apache.syncope.core.persistence.jpa.OJPAJSONPersistenceContext
+
+security.adminUser=${adminUser}
+security.anonymousUser=${anonymousUser}
+security.jwsKey=${jwsKey}
+security.secretKey=${secretKey}
+
+persistence.domain[0].jdbcURL=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncope?characterEncoding=UTF-8
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+# keep the next two lines until https://jira.mariadb.org/browse/MDEV-27898 is
fixed
+persistence.domain[0].dbUsername=root
+persistence.domain[0].dbPassword=password
+
+provisioning.connIdLocation=${syncope.connid.location}
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 e1ca1992d0..448554b3f7 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
@@ -932,13 +932,11 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO
{
&& !(cond instanceof AnyCond)
&& cond.getType() != AttrCond.Type.ISNULL && cond.getType() !=
AttrCond.Type.ISNOTNULL) {
- query.append("any_id NOT IN (SELECT DISTINCT any_id FROM ");
- if (schema.isUniqueConstraint()) {
- query.append(svs.asSearchViewSupport().uniqueAttr().name());
- } else {
- query.append(svs.asSearchViewSupport().attr().name());
- }
- query.append(" WHERE schema_id='").append(schema.getKey());
+ query.append("any_id NOT IN (SELECT DISTINCT any_id FROM ").
+ append((schema.isUniqueConstraint()
+ ? svs.asSearchViewSupport().uniqueAttr().name()
+ : svs.asSearchViewSupport().attr().name())).
+ append(" WHERE schema_id='").append(schema.getKey());
fillAttrQuery(query, attrValue, schema, cond, false, parameters,
svs);
query.append(')');
} else {
@@ -1082,12 +1080,10 @@ public class JPAAnySearchDAO extends
AbstractAnySearchDAO {
if (not && !(cond instanceof AnyCond) &&
checked.getLeft().isMultivalue()) {
query.append(svs.field().name()).append(" WHERE ");
} else {
- if (checked.getLeft().isUniqueConstraint()) {
-
query.append(svs.asSearchViewSupport().uniqueAttr().name());
- } else {
- query.append(svs.asSearchViewSupport().attr().name());
- }
- query.append(" WHERE
schema_id='").append(checked.getLeft().getKey());
+ query.append((checked.getLeft().isUniqueConstraint()
+ ? svs.asSearchViewSupport().uniqueAttr().name()
+ : svs.asSearchViewSupport().attr().name())).
+ append(" WHERE
schema_id='").append(checked.getLeft().getKey());
}
fillAttrQuery(query, checked.getRight(), checked.getLeft(),
cond, not, parameters, svs);
}
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java
index 287ada1a8d..598fb62813 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java
@@ -182,11 +182,8 @@ public abstract class AbstractAnyRepoExt<A extends Any<?>>
implements AnyRepoExt
query.setParameter("schemaKey", schema.getKey());
query.setParameter("stringValue", attrValue.getStringValue());
query.setParameter("booleanValue", attrValue.getBooleanValue());
- if (attrValue.getDateValue() == null) {
- query.setParameter("dateValue", null);
- } else {
- query.setParameter("dateValue",
attrValue.getDateValue().toInstant());
- }
+ query.setParameter("dateValue",
Optional.ofNullable(attrValue.getDateValue()).
+ map(OffsetDateTime::toInstant).orElse(null));
query.setParameter("longValue", attrValue.getLongValue());
query.setParameter("doubleValue", attrValue.getDoubleValue());
diff --git
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
index b1c5d46b6c..6105bcf5a2 100644
---
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
+++
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
@@ -87,8 +87,8 @@ public class UserTest extends AbstractTest {
assertNull(user.getSecurityQuestion());
assertNull(user.getSecurityAnswer());
assertEquals("admin", user.getCreator());
- assertEquals("Giacomo",
user.getPlainAttr("firstname").get().getValuesAsStrings().get(0));
- assertEquals("Puccini",
user.getPlainAttr("surname").get().getValuesAsStrings().get(0));
+ assertEquals("Giacomo",
user.getPlainAttr("firstname").orElseThrow().getValuesAsStrings().get(0));
+ assertEquals("Puccini",
user.getPlainAttr("surname").orElseThrow().getValuesAsStrings().get(0));
}
@Test
diff --git
a/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
b/docker/core/src/main/resources/core-majson.properties
similarity index 51%
copy from
core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
copy to docker/core/src/main/resources/core-majson.properties
index 0a81de30b1..bf285635a0 100644
---
a/core/persistence-jpa-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/docker/core/src/main/resources/core-majson.properties
@@ -14,6 +14,19 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-org.apache.syncope.core.persistence.jpa.PGJPAJSONPersistenceContext
-org.apache.syncope.core.persistence.jpa.MyJPAJSONPersistenceContext
-org.apache.syncope.core.persistence.jpa.OJPAJSONPersistenceContext
+
+persistence.indexesXML=classpath:majson/indexes.xml
+persistence.viewsXML=classpath:majson/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].orm=META-INF/spring-orm-majson.xml
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mariadb.sql
diff --git
a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
b/docker/src/main/resources/docker-compose/docker-compose-majson.yml
similarity index 91%
copy from docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
copy to docker/src/main/resources/docker-compose/docker-compose-majson.yml
index 15cb1c5651..9331524fb0 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-majson.yml
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-# Full deployment (Core, Console, Enduser) on MariaDB
+# Full deployment (Core, Console, Enduser) on MariaDB with JSON_TABLE support
services:
db:
@@ -30,15 +30,16 @@ services:
syncope:
depends_on:
- db
+ command: ["wait-for-it", "db:3306", "-t", "60", "--",
"/opt/syncope/bin/startup.sh"]
image: apache/syncope:${SYNCOPE_VERSION}
ports:
- "18080:8080"
restart: always
environment:
- SPRING_PROFILES_ACTIVE: docker,mariadb,saml2
+ SPRING_PROFILES_ACTIVE: docker,majson,saml2
DB_URL:
jdbc:mariadb://db:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true&useSSL=false
- DB_USER: syncope
- DB_PASSWORD: syncope
+ DB_USER: root
+ DB_PASSWORD: password
DB_POOL_MAX: 20
DB_POOL_MIN: 5
OPENJPA_REMOTE_COMMIT: sjvm
diff --git
a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
index 15cb1c5651..17447baf60 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
@@ -30,6 +30,7 @@ services:
syncope:
depends_on:
- db
+ command: ["wait-for-it", "db:3306", "-t", "60", "--",
"/opt/syncope/bin/startup.sh"]
image: apache/syncope:${SYNCOPE_VERSION}
ports:
- "18080:8080"
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index fae44c327c..3462115493 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -1144,7 +1144,124 @@ under the License.
</plugins>
</build>
</profile>
+
+ <profile>
+ <id>majson-it</id>
+
+ <properties>
+ <jdbcdriver.groupId>org.mariadb.jdbc</jdbcdriver.groupId>
+ <jdbcdriver.artifactId>mariadb-java-client</jdbcdriver.artifactId>
+
+ <spring.profiles.active>embedded,majson</spring.profiles.active>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-persistence-jpa-json</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mariadb.jdbc</groupId>
+ <artifactId>mariadb-java-client</artifactId>
+ <version>${jdbc.mariadb.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-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <configuration>
+ <images>
+ <image>
+ <alias>mariadb</alias>
+ <name>mariadb:${docker.mariadb.version}</name>
+ <run>
+ <env>
+ <MYSQL_ROOT_PASSWORD>password</MYSQL_ROOT_PASSWORD>
+ <MYSQL_DATABASE>syncope</MYSQL_DATABASE>
+ <MYSQL_USER>syncope</MYSQL_USER>
+ <MYSQL_PASSWORD>syncope</MYSQL_PASSWORD>
+ </env>
+ <tmpfs>
+ <mount>/var/lib/mysql:rw</mount>
+ </tmpfs>
+ <wait>
+ <log>MariaDB init process done. Ready for start up.</log>
+ <time>30000</time>
+ </wait>
+ </run>
+ </image>
+ </images>
+ </configuration>
+ <executions>
+ <execution>
+ <id>start-mariadb</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>start</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>stop-mariadb</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-maven3-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <container>
+ <systemProperties>
+
<DB_CONTAINER_IP>${docker.container.mariadb.ip}</DB_CONTAINER_IP>
+ </systemProperties>
+ </container>
+ </configuration>
+ </plugin>
+ </plugins>
+
+ <resources>
+ <resource>
+
<directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/domains</directory>
+ <targetPath>${project.build.outputDirectory}/domains</targetPath>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+ </profile>
+
<profile>
<id>mariadb-it</id>
diff --git a/fit/core-reference/src/main/resources/core-majson.properties
b/fit/core-reference/src/main/resources/core-majson.properties
new file mode 100644
index 0000000000..e9857c287b
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-majson.properties
@@ -0,0 +1,34 @@
+# 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.
+
+persistence.indexesXML=classpath:majson/indexes.xml
+persistence.viewsXML=classpath:majson/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
+persistence.domain[0].jdbcURL=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncope?characterEncoding=UTF-8
+# keep the next two lines until https://jira.mariadb.org/browse/MDEV-27898 is
fixed
+persistence.domain[0].dbUsername=root
+persistence.domain[0].dbPassword=password
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].orm=META-INF/spring-orm-majson.xml
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mariadb.sql
+
diff --git a/pom.xml b/pom.xml
index 3de80158f7..6f327b6673 100644
--- a/pom.xml
+++ b/pom.xml
@@ -501,7 +501,7 @@ under the License.
<docker.postgresql.version>16</docker.postgresql.version>
<docker.mysql.version>9.0</docker.mysql.version>
<docker.mariadb.version>11</docker.mariadb.version>
- <docker.neo4j.version>5.21.0</docker.neo4j.version>
+ <docker.neo4j.version>5.22.0</docker.neo4j.version>
<jdbc.postgresql.version>42.7.3</jdbc.postgresql.version>
<jdbc.mysql.version>9.0.0</jdbc.mysql.version>
diff --git a/src/main/asciidoc/reference-guide/configuration/dbms.adoc
b/src/main/asciidoc/reference-guide/configuration/dbms.adoc
index 3ffe919f34..9d7da3d1bf 100644
--- a/src/main/asciidoc/reference-guide/configuration/dbms.adoc
+++ b/src/main/asciidoc/reference-guide/configuration/dbms.adoc
@@ -224,6 +224,70 @@
https://mariadb.com/kb/en/configuring-mariadb-with-option-files/[option file^].
This assumes that you have a MariaDB instance running on localhost, listening
on its default port 3306 with a database
`syncope` fully accessible by user `syncope` with password `syncope`.
+==== MariaDB (JSON)
+
+[NOTE]
+With the configurations reported below, Apache Syncope will leverage the
+https://mariadb.com/kb/en/json_table/[JSON_TABLE^] function.
+
+[NOTE]
+Apache Syncope {docVersion} is verified with MariaDB server >= {mariadb} and
JDBC driver >= {mariadbJDBC}.
+
+Add the following dependency to `core/pom.xml`:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-persistence-jpa-json</artifactId>
+ <version>${syncope.version}</version>
+</dependency>
+----
+
+Create
+
+[source]
+....
+persistence.indexesXML=classpath:majson/indexes.xml
+persistence.viewsXML=classpath:majson/views.xml
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
+persistence.domain[0].jdbcURL=jdbc:mariadb://localhost:3306/syncope?characterEncoding=UTF-8
+persistence.domain[0].dbUsername=root
+persistence.domain[0].dbPassword=password
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].orm=META-INF/spring-orm-majson.xml
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mariadb.sql
+....
+
+as `core/src/main/resources/core-majson.properties`.
+
+Do not forget to include `majson` as
+https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles.adding-active-profiles[Spring
Boot profile^]
+for the Core application.
+
+[CAUTION]
+It is important to set the collation to `utf8_general_ci` after creation of
`syncope` database.
+
+[WARNING]
+====
+It is necessary to use `utf8mb4_unicode_ci` instead of `utf8mb4_general_ci` if
case-sensitive queries are required.
+In this case, set
+....
+init_connect = "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
+....
+under either the `[mysqld]` section or the `[mariadb]` section of your
+https://mariadb.com/kb/en/configuring-mariadb-with-option-files/[option file^].
+====
+
+[WARNING]
+This assumes that you have a MariaDB instance running on localhost, listening
on its default port 3306 with a database
+`syncope` and super-admin user `root` with password `password`.
+Super-admin user is required until
https://jira.mariadb.org/browse/MDEV-27898[this bug^] is fixed.
+
==== Oracle Database
[NOTE]
diff --git a/src/site/xdoc/building.xml b/src/site/xdoc/building.xml
index e913e553f7..638ebf2f19 100644
--- a/src/site/xdoc/building.xml
+++ b/src/site/xdoc/building.xml
@@ -171,7 +171,7 @@ under the License.
<p>This build profile requires <a
href="https://www.docker.com/">Docker</a> to work.</p>
</div>
Perform the full test suite against a real <a
href="https://mariadb.org/">MariaDB</a> database via
- <source>$ mvn -Pmariadb-it</source>
+ <source>$ mvn -Pmariadb-it</source> or <source>$ mvn
-Pmajson-it</source> (for JSON support)
<h5>Oracle database</h5>
<div class="alert alert-warning">