From 651a552cf70ae153a070724a2eeff40007f61a96 Mon Sep 17 00:00:00 2001
From: alaskowski <alaskowski@ethz.ch>
Date: Mon, 3 Jul 2023 15:52:46 +0200
Subject: [PATCH] SSDM-55: Changed Java API - changed properties map to
 <String, Serializable>; added isMultiValue to PropertyType; Removed
 constraint on ExperimentProperties table

---
 .../common/interfaces/IPropertiesHolder.java  |  5 +-
 .../PropertySerializableDeserializer.java     | 46 +++++++++++++++++
 .../asapi/v3/dto/common/property/Util.java    | 49 +++++++++++++++++++
 .../generic/asapi/v3/dto/dataset/DataSet.java | 11 +++--
 .../dto/dataset/create/DataSetCreation.java   | 10 ++--
 .../v3/dto/dataset/update/DataSetUpdate.java  |  9 ++--
 .../asapi/v3/dto/experiment/Experiment.java   | 17 ++++---
 .../experiment/create/ExperimentCreation.java | 10 ++--
 .../experiment/update/ExperimentUpdate.java   | 13 +++--
 .../asapi/v3/dto/material/Material.java       | 10 ++--
 .../dto/material/create/MaterialCreation.java |  9 ++--
 .../dto/material/update/MaterialUpdate.java   |  9 ++--
 .../asapi/v3/dto/property/PropertyType.java   | 16 ++++++
 .../generic/asapi/v3/dto/sample/Sample.java   | 11 +++--
 .../v3/dto/sample/create/SampleCreation.java  | 10 ++--
 .../v3/dto/sample/update/SampleUpdate.java    |  9 ++--
 .../generic/shared/api/v1/dto/Experiment.java | 20 +++++---
 .../v1/util/PropertyValueDeserializer.java    | 46 +++++++++++++++++
 .../source/sql/generic/193/schema-193.sql     |  3 +-
 .../migration/migration-192-193.sql           |  5 ++
 20 files changed, 255 insertions(+), 63 deletions(-)
 create mode 100644 api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/PropertySerializableDeserializer.java
 create mode 100644 api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/Util.java
 create mode 100644 api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/util/PropertyValueDeserializer.java

diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java
index 4b3f231c47c..3e6dcb3aa60 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.util.Map;
 
@@ -28,9 +29,9 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 public interface IPropertiesHolder
 {
 
-    Map<String, String> getProperties();
+    Map<String, Serializable> getProperties();
 
-    void setProperties(Map<String, String> properties);
+    void setProperties(Map<String, Serializable> properties);
 
     String getProperty(String propertyName);
 
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/PropertySerializableDeserializer.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/PropertySerializableDeserializer.java
new file mode 100644
index 00000000000..cfa48a147fc
--- /dev/null
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/PropertySerializableDeserializer.java
@@ -0,0 +1,46 @@
+/*
+ *  Copyright ETH 2023 Zürich, Scientific IT Services
+ *
+ *  Licensed 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 ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+public class PropertySerializableDeserializer extends JsonDeserializer<Serializable>
+{
+    @Override
+    public Serializable deserialize(JsonParser p, DeserializationContext ctxt)
+            throws IOException
+    {
+        JsonNode node = p.readValueAsTree();
+        if(node.isArray()) {
+            ArrayList<String> list = new ArrayList<>();
+            node.forEach(value -> list.add(value.textValue()));
+            return list.toArray(new String[0]);
+        } else {
+            return node.textValue();
+        }
+    }
+
+
+}
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/Util.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/Util.java
new file mode 100644
index 00000000000..d32e4ac8e3a
--- /dev/null
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/property/Util.java
@@ -0,0 +1,49 @@
+/*
+ *  Copyright ETH 2023 Zürich, Scientific IT Services
+ *
+ *  Licensed 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 ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property;
+
+import java.io.Serializable;
+import java.util.stream.Stream;
+
+public final class Util
+{
+    private Util() {}
+
+    public static String getPropertyAsString(Serializable propertyValue) {
+        if(propertyValue == null) {
+            return null;
+        } else {
+            if(propertyValue.getClass().isArray()) {
+                Serializable[] values = (Serializable[]) propertyValue;
+                StringBuilder builder = new StringBuilder("[");
+                for(Serializable value : values) {
+                    if(builder.length() > 1) {
+                        builder.append(", ");
+                    }
+                    builder.append(value);
+                }
+                builder.append("]");
+                return builder.toString();
+            } else {
+                return (String) propertyValue;
+            }
+        }
+
+
+    }
+}
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java
index 1e3fc1ba614..e061fefc820 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java
@@ -28,6 +28,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationD
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistratorHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ISampleHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ITagsHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.Util;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetKind;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.LinkedData;
@@ -117,7 +118,7 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
     private Sample sample;
 
     @JsonProperty
-    private Map<String, String> properties;
+    private Map<String, Serializable> properties;
 
     @JsonProperty
     private Map<String, Material> materialProperties;
@@ -465,7 +466,7 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         if (getFetchOptions() != null && getFetchOptions().hasProperties())
         {
@@ -479,7 +480,7 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
 
     // Method automatically generated with DtoGenerator
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
@@ -941,7 +942,7 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
     @Override
     public String getProperty(String propertyName)
     {
-        return getProperties() != null ? getProperties().get(propertyName) : null;
+        return getProperties() != null ? Util.getPropertyAsString(getProperties().get(propertyName)) : null;
     }
 
     @Override
@@ -949,7 +950,7 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
     {
         if (properties == null)
         {
-            properties = new HashMap<String, String>();
+            properties = new HashMap<String, Serializable>();
         }
         properties.put(propertyName, propertyValue);
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/create/DataSetCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/create/DataSetCreation.java
index 99894d689de..87e34584a09 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/create/DataSetCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/create/DataSetCreation.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -25,6 +26,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.IObjectCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.CreationId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICreationIdHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.Util;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetKind;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.id.IDataStoreId;
@@ -67,7 +69,7 @@ public class DataSetCreation implements ICreation, ICreationIdHolder, IObjectCre
 
     private List<? extends ITagId> tagIds;
 
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, Serializable> properties = new HashMap<String, Serializable>();
 
     private List<? extends IDataSetId> containerIds;
 
@@ -262,17 +264,17 @@ public class DataSetCreation implements ICreation, ICreationIdHolder, IObjectCre
     @Override
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return getProperties() != null ? Util.getPropertyAsString(getProperties().get(propertyName)) : null;
     }
 
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/update/DataSetUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/update/DataSetUpdate.java
index c003dfcf7d3..65682fe6592 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/update/DataSetUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/update/DataSetUpdate.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.update;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -77,7 +78,7 @@ public class DataSetUpdate implements IUpdate, IObjectUpdate<IDataSetId>, IPrope
     private IdListUpdateValue<ITagId> tagIds = new IdListUpdateValue<ITagId>();
 
     @JsonProperty
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, Serializable> properties = new HashMap<String, Serializable>();
 
     @JsonProperty
     private IdListUpdateValue<IDataSetId> containerIds = new IdListUpdateValue<IDataSetId>();
@@ -229,19 +230,19 @@ public class DataSetUpdate implements IUpdate, IObjectUpdate<IDataSetId>, IPrope
     @JsonIgnore
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return properties != null ? (String) properties.get(propertyName) : null;
     }
 
     @Override
     @JsonIgnore
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
     @JsonIgnore
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
index 2606c9f8c19..9591fed97be 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
@@ -31,6 +31,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationD
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistratorHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ISamplesHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ITagsHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.Util;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.ExperimentType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentFetchOptions;
@@ -58,12 +59,16 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Stream;
 
 /*
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.experiment.Experiment")
-public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder, IDataSetsHolder, IEntityTypeHolder, IIdentifierHolder, IMaterialPropertiesHolder, IModificationDateHolder, IModifierHolder, IPermIdHolder, IProjectHolder, IPropertiesHolder, IRegistrationDateHolder, IRegistratorHolder, ISamplesHolder, ITagsHolder
+public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder, IDataSetsHolder,
+        IEntityTypeHolder, IIdentifierHolder, IMaterialPropertiesHolder, IModificationDateHolder,
+        IModifierHolder, IPermIdHolder, IProjectHolder, IPropertiesHolder, IRegistrationDateHolder,
+        IRegistratorHolder, ISamplesHolder, ITagsHolder
 {
     private static final long serialVersionUID = 1L;
 
@@ -125,7 +130,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
     private List<HistoryEntry> unknownHistory;
 
     @JsonProperty
-    private Map<String, String> properties;
+    private Map<String, Serializable> properties;
 
     @JsonProperty
     private Map<String, Material> materialProperties;
@@ -478,7 +483,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         if (getFetchOptions() != null && getFetchOptions().hasProperties())
         {
@@ -492,7 +497,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
 
     // Method automatically generated with DtoGenerator
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
@@ -626,7 +631,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
     @Override
     public String getProperty(String propertyName)
     {
-        return getProperties() != null ? getProperties().get(propertyName) : null;
+        return getProperties() != null ? Util.getPropertyAsString(getProperties().get(propertyName)) : null;
     }
 
     @Override
@@ -634,7 +639,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
     {
         if (properties == null)
         {
-            properties = new HashMap<String, String>();
+            properties = new HashMap<String, Serializable>();
         }
         properties.put(propertyName, propertyValue);
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentCreation.java
index f32fe52a060..7394e1dc04d 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentCreation.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.create;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -26,6 +27,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.IObjectCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.CreationId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICreationIdHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.Util;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.IEntityTypeId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.IProjectId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
@@ -48,7 +50,7 @@ public class ExperimentCreation implements ICreation, IObjectCreation, ICreation
 
     private List<? extends ITagId> tagIds;
 
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, Serializable> properties = new HashMap<>();
 
     private List<AttachmentCreation> attachments;
 
@@ -115,17 +117,17 @@ public class ExperimentCreation implements ICreation, IObjectCreation, ICreation
     @Override
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return properties != null ? Util.getPropertyAsString(properties.get(propertyName)) : null;
     }
 
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentUpdate.java
index 675f6bfa58c..507e2329aee 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentUpdate.java
@@ -15,13 +15,16 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.update;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.PropertySerializableDeserializer;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IMetaDataUpdateHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.*;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
@@ -33,6 +36,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.IProjectId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.id.ITagId;
 import ch.systemsx.cisd.base.annotation.JsonObject;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 
 /**
  * @author pkupczyk
@@ -57,7 +61,8 @@ public class ExperimentUpdate implements IUpdate, IObjectUpdate<IExperimentId>,
     private boolean freezeForSamples;
 
     @JsonProperty
-    private Map<String, String> properties = new HashMap<String, String>();
+    @JsonDeserialize(contentUsing = PropertySerializableDeserializer.class)
+    private Map<String, Serializable> properties = new HashMap<>();
 
     @JsonProperty
     private FieldUpdateValue<IProjectId> projectId = new FieldUpdateValue<IProjectId>();
@@ -136,19 +141,19 @@ public class ExperimentUpdate implements IUpdate, IObjectUpdate<IExperimentId>,
     @JsonIgnore
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return properties != null ? (String) properties.get(propertyName) : null;
     }
 
     @Override
     @JsonIgnore
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
     @JsonIgnore
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java
index 05af8f4f378..2b14882f954 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java
@@ -81,7 +81,7 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
     private Date modificationDate;
 
     @JsonProperty
-    private Map<String, String> properties;
+    private Map<String, Serializable> properties;
 
     @JsonProperty
     private Map<String, Material> materialProperties;
@@ -223,7 +223,7 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         if (getFetchOptions() != null && getFetchOptions().hasProperties())
         {
@@ -237,7 +237,7 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
 
     // Method automatically generated with DtoGenerator
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
@@ -288,7 +288,7 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
     @Override
     public String getProperty(String propertyName)
     {
-        return getProperties() != null ? getProperties().get(propertyName) : null;
+        return getProperties() != null ? (String) getProperties().get(propertyName) : null;
     }
 
     @Override
@@ -296,7 +296,7 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
     {
         if (properties == null)
         {
-            properties = new HashMap<String, String>();
+            properties = new HashMap<>();
         }
         properties.put(propertyName, propertyValue);
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/create/MaterialCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/create/MaterialCreation.java
index b09ac0ad5cd..b742c378928 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/create/MaterialCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/create/MaterialCreation.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.material.create;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -46,7 +47,7 @@ public class MaterialCreation implements ICreation, IObjectCreation, ICreationId
 
     private List<? extends ITagId> tagIds;
 
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, Serializable> properties = new HashMap<>();
 
     private CreationId creationId;
 
@@ -89,17 +90,17 @@ public class MaterialCreation implements ICreation, IObjectCreation, ICreationId
     @Override
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return properties != null ? (String) properties.get(propertyName) : null;
     }
 
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/update/MaterialUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/update/MaterialUpdate.java
index d1cfe873086..0c4456ed86f 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/update/MaterialUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/update/MaterialUpdate.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.material.update;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -48,7 +49,7 @@ public class MaterialUpdate implements IUpdate, IObjectUpdate<IMaterialId>, IPro
     private IdListUpdateValue<ITagId> tagIds = new IdListUpdateValue<ITagId>();
 
     @JsonProperty
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, Serializable> properties = new HashMap<>();
 
     @Override
     @JsonIgnore
@@ -80,19 +81,19 @@ public class MaterialUpdate implements IUpdate, IObjectUpdate<IMaterialId>, IPro
     @JsonIgnore
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return properties != null ? (String) properties.get(propertyName) : null;
     }
 
     @Override
     @JsonIgnore
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
     @JsonIgnore
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java
index ef0ca49f8ac..19d0a956bfc 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java
@@ -96,6 +96,9 @@ public class PropertyType implements Serializable, ICodeHolder, IDescriptionHold
     @JsonProperty
     private Map<String, String> metaData;
 
+    @JsonProperty
+    private Boolean multiValue;
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public PropertyTypeFetchOptions getFetchOptions()
@@ -357,6 +360,19 @@ public class PropertyType implements Serializable, ICodeHolder, IDescriptionHold
         this.metaData = metaData;
     }
 
+    // Method automatically generated with DtoGenerator
+    @JsonIgnore
+    public Boolean isMultiValue()
+    {
+        return multiValue;
+    }
+
+    // Method automatically generated with DtoGenerator
+    public void setMultiValue(Boolean multiValue)
+    {
+        this.multiValue = multiValue;
+    }
+
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
index 16201e1b94f..7dae09d7704 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
@@ -34,6 +34,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationD
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistratorHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ISpaceHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ITagsHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.Util;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.history.HistoryEntry;
@@ -117,7 +118,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
     private Experiment experiment;
 
     @JsonProperty
-    private Map<String, String> properties;
+    private Map<String, Serializable> properties;
 
     @JsonProperty
     private Map<String, Material> materialProperties;
@@ -429,7 +430,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         if (getFetchOptions() != null && getFetchOptions().hasProperties())
         {
@@ -443,7 +444,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
 
     // Method automatically generated with DtoGenerator
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
@@ -940,7 +941,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
     @Override
     public String getProperty(String propertyName)
     {
-        return getProperties() != null ? getProperties().get(propertyName) : null;
+        return getProperties() != null ? Util.getPropertyAsString(getProperties().get(propertyName)) : null;
     }
 
     @Override
@@ -948,7 +949,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
     {
         if (properties == null)
         {
-            properties = new HashMap<String, String>();
+            properties = new HashMap<>();
         }
         properties.put(propertyName, propertyValue);
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java
index 91d7b64f3ef..66b3ea6d6b5 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java
@@ -15,10 +15,12 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.Util;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -62,7 +64,7 @@ public class SampleCreation implements ICreation, ICreationIdHolder, IProperties
 
     private List<? extends ITagId> tagIds;
 
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, Serializable> properties = new HashMap<>();
 
     private ISampleId containerId;
 
@@ -243,17 +245,17 @@ public class SampleCreation implements ICreation, ICreationIdHolder, IProperties
     @Override
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return getProperties() != null ? Util.getPropertyAsString(getProperties().get(propertyName)) : null;
     }
 
     @Override
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java
index 4e35da2d9be..eb70bc969b0 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.update;
 
+import java.io.Serializable;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -78,7 +79,7 @@ public class SampleUpdate implements IUpdate, IPropertiesHolder, IObjectUpdate<I
     private IdListUpdateValue<ITagId> tagIds = new IdListUpdateValue<ITagId>();
 
     @JsonProperty
-    private Map<String, String> properties = new HashMap<String, String>();
+    private Map<String, Serializable> properties = new HashMap<>();
 
     @JsonProperty
     private FieldUpdateValue<ISampleId> containerId = new FieldUpdateValue<ISampleId>();
@@ -238,19 +239,19 @@ public class SampleUpdate implements IUpdate, IPropertiesHolder, IObjectUpdate<I
     @JsonIgnore
     public String getProperty(String propertyName)
     {
-        return properties != null ? properties.get(propertyName) : null;
+        return properties != null ? (String) properties.get(propertyName) : null;
     }
 
     @Override
     @JsonIgnore
-    public void setProperties(Map<String, String> properties)
+    public void setProperties(Map<String, Serializable> properties)
     {
         this.properties = properties;
     }
 
     @Override
     @JsonIgnore
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return properties;
     }
diff --git a/api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java b/api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java
index c479f01fef9..87e7c250c96 100644
--- a/api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java
+++ b/api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java
@@ -22,6 +22,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.util.PropertyValueDeserializer;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -71,7 +73,7 @@ public final class Experiment implements Serializable, IIdentifierHolder, IIdHol
 
         private boolean isStub;
 
-        private HashMap<String, String> properties = new HashMap<String, String>();
+        private HashMap<String, Serializable> properties = new HashMap<>();
 
         private List<Metaproject> metaprojects = new ArrayList<Metaproject>();
 
@@ -125,14 +127,19 @@ public final class Experiment implements Serializable, IIdentifierHolder, IIdHol
             return experimentTypeCode;
         }
 
-        public HashMap<String, String> getProperties()
+        public HashMap<String, Serializable> getProperties()
         {
             return properties;
         }
 
         public void putProperty(String propCode, String value)
         {
-            properties.put(propCode, value);
+            if(properties.containsKey(propCode)) {
+                properties.put(propCode, properties.get(propCode) + ", " + value);
+            } else
+            {
+                properties.put(propCode, value);
+            }
         }
 
         public List<Metaproject> getMetaprojects()
@@ -173,7 +180,8 @@ public final class Experiment implements Serializable, IIdentifierHolder, IIdHol
 
     private EntityRegistrationDetails registrationDetails;
 
-    private HashMap<String, String> properties;
+    @JsonDeserialize(contentUsing = PropertyValueDeserializer.class)
+    private HashMap<String, Serializable> properties;
 
     private List<Metaproject> metaprojects;
 
@@ -267,7 +275,7 @@ public final class Experiment implements Serializable, IIdentifierHolder, IIdHol
         return registrationDetails;
     }
 
-    public Map<String, String> getProperties()
+    public Map<String, Serializable> getProperties()
     {
         return Collections.unmodifiableMap(properties);
     }
@@ -378,7 +386,7 @@ public final class Experiment implements Serializable, IIdentifierHolder, IIdHol
         this.registrationDetails = registrationDetails;
     }
 
-    private void setProperties(HashMap<String, String> properties)
+    private void setProperties(HashMap<String, Serializable> properties)
     {
         this.properties = properties;
     }
diff --git a/api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/util/PropertyValueDeserializer.java b/api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/util/PropertyValueDeserializer.java
new file mode 100644
index 00000000000..037277b6277
--- /dev/null
+++ b/api-openbis-java/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/util/PropertyValueDeserializer.java
@@ -0,0 +1,46 @@
+/*
+ *  Copyright ETH 2023 Zürich, Scientific IT Services
+ *
+ *  Licensed 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 ch.systemsx.cisd.openbis.generic.shared.api.v1.util;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+public class PropertyValueDeserializer extends JsonDeserializer<Serializable>
+{
+    @Override
+    public Serializable deserialize(JsonParser p, DeserializationContext ctxt)
+            throws IOException
+    {
+        JsonNode node = p.readValueAsTree();
+        if(node.isArray()) {
+            ArrayList<String> list = new ArrayList<>();
+            node.forEach(value -> list.add(value.textValue()));
+            return list.toArray(new String[0]);
+        } else {
+            return node.textValue();
+        }
+    }
+
+
+}
diff --git a/server-application-server/source/sql/generic/193/schema-193.sql b/server-application-server/source/sql/generic/193/schema-193.sql
index 0b60665ebf8..f7f21fbe9c8 100644
--- a/server-application-server/source/sql/generic/193/schema-193.sql
+++ b/server-application-server/source/sql/generic/193/schema-193.sql
@@ -31,7 +31,7 @@ CREATE TABLE MATERIAL_TYPE_PROPERTY_TYPES (ID TECH_ID NOT NULL,MATY_ID TECH_ID N
 CREATE TABLE DATA_SET_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, MAIN_DS_PATTERN VARCHAR(300), MAIN_DS_PATH VARCHAR(1000), DELETION_DISALLOW BOOLEAN_CHAR DEFAULT 'F', VALIDATION_SCRIPT_ID TECH_ID, META_DATA JSONB);
 CREATE TABLE PERSONS (ID TECH_ID NOT NULL,FIRST_NAME VARCHAR(30),LAST_NAME VARCHAR(30),USER_ID USER_ID NOT NULL,EMAIL OBJECT_NAME,SPACE_ID TECH_ID,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,PERS_ID_REGISTERER TECH_ID, DISPLAY_SETTINGS FILE, IS_ACTIVE BOOLEAN DEFAULT TRUE);
 CREATE TABLE PROJECTS (ID TECH_ID NOT NULL,PERM_ID CODE NOT NULL,CODE CODE NOT NULL,SPACE_ID TECH_ID NOT NULL,PERS_ID_LEADER TECH_ID,DESCRIPTION TEXT_VALUE,PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, PERS_ID_MODIFIER TECH_ID, VERSION INTEGER DEFAULT 0, FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_EXP BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_SAMP BOOLEAN_CHAR NOT NULL DEFAULT 'F', SPACE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F');
-CREATE TABLE PROPERTY_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000 NOT NULL,LABEL COLUMN_LABEL NOT NULL,DATY_ID TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,PERS_ID_REGISTERER TECH_ID NOT NULL,COVO_ID TECH_ID,IS_MANAGED_INTERNALLY BOOLEAN_CHAR NOT NULL DEFAULT 'F', MATY_PROP_ID TECH_ID, SATY_PROP_ID TECH_ID, SCHEMA TEXT_VALUE, TRANSFORMATION TEXT_VALUE, META_DATA JSONB);
+CREATE TABLE PROPERTY_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000 NOT NULL,LABEL COLUMN_LABEL NOT NULL,DATY_ID TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,PERS_ID_REGISTERER TECH_ID NOT NULL,COVO_ID TECH_ID,IS_MANAGED_INTERNALLY BOOLEAN_CHAR NOT NULL DEFAULT 'F', MATY_PROP_ID TECH_ID, SATY_PROP_ID TECH_ID, SCHEMA TEXT_VALUE, TRANSFORMATION TEXT_VALUE, META_DATA JSONB, IS_MULTI_VALUE BOOLEAN_CHAR NOT NULL DEFAULT 'F');
 CREATE TABLE ROLE_ASSIGNMENTS (ID TECH_ID NOT NULL,ROLE_CODE AUTHORIZATION_ROLE NOT NULL,SPACE_ID TECH_ID, PROJECT_ID TECH_ID, PERS_ID_GRANTEE TECH_ID, AG_ID_GRANTEE TECH_ID, PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP);
 CREATE TABLE SAMPLES_ALL (ID TECH_ID NOT NULL,PERM_ID CODE NOT NULL, sample_identifier sample_identifier, CODE CODE NOT NULL, EXPE_ID TECH_ID,SATY_ID TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP,PERS_ID_REGISTERER TECH_ID NOT NULL,DEL_ID TECH_ID, ORIG_DEL TECH_ID, SPACE_ID TECH_ID, SAMP_ID_PART_OF TECH_ID, PERS_ID_MODIFIER TECH_ID, code_unique_check character varying(300), subcode_unique_check character varying(300), VERSION INTEGER DEFAULT 0, PROJ_ID TECH_ID, FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_COMP BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_CHILDREN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_PARENTS BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_DATA BOOLEAN_CHAR NOT NULL DEFAULT 'F', SPACE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', PROJ_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', EXPE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', CONT_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL, META_DATA JSONB);
 CREATE TABLE SAMPLE_PROPERTIES (ID TECH_ID NOT NULL,SAMP_ID TECH_ID NOT NULL,STPT_ID TECH_ID NOT NULL,VALUE TEXT_VALUE,CVTE_ID TECH_ID,MATE_PROP_ID TECH_ID,SAMP_PROP_ID TECH_ID,PERS_ID_REGISTERER TECH_ID NOT NULL, REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, PERS_ID_AUTHOR TECH_ID NOT NULL, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, SAMP_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL, IS_UNIQUE BOOLEAN_CHAR NOT NULL DEFAULT 'F', INTEGER_ARRAY_VALUE LONG_VALUE[], REAL_ARRAY_VALUE DOUBLE_VALUE[], STRING_ARRAY_VALUE TEXT_VALUE[], TIMESTAMP_ARRAY_VALUE TIME_STAMP[], JSON_VALUE JSONB);
@@ -543,7 +543,6 @@ ALTER TABLE EXPERIMENTS_ALL ADD CONSTRAINT EXPE_IDFRZ_S_UK UNIQUE(ID, FROZEN_FOR
 ALTER TABLE EXPERIMENTS_ALL ADD CONSTRAINT EXPE_IDFRZ_D_UK UNIQUE(ID, FROZEN_FOR_DATA);
 ALTER TABLE EXPERIMENTS_ALL ADD CONSTRAINT EXPE_BK_UK UNIQUE(CODE,PROJ_ID);
 ALTER TABLE EXPERIMENTS_ALL ADD CONSTRAINT EXPE_PI_UK UNIQUE(PERM_ID);
-ALTER TABLE EXPERIMENT_PROPERTIES ADD CONSTRAINT EXPR_BK_UK UNIQUE(EXPE_ID,ETPT_ID);
 ALTER TABLE EXPERIMENT_TYPE_PROPERTY_TYPES ADD CONSTRAINT ETPT_BK_UK UNIQUE(EXTY_ID,PRTY_ID);
 ALTER TABLE EXTERNAL_DATA ADD CONSTRAINT EXDA_BK_UK UNIQUE(LOCATION,LOTY_ID);
 ALTER TABLE FILE_FORMAT_TYPES ADD CONSTRAINT FFTY_BK_UK UNIQUE(CODE);
diff --git a/server-application-server/source/sql/postgresql/migration/migration-192-193.sql b/server-application-server/source/sql/postgresql/migration/migration-192-193.sql
index 2aa7b8de034..4e5efb5ae6e 100644
--- a/server-application-server/source/sql/postgresql/migration/migration-192-193.sql
+++ b/server-application-server/source/sql/postgresql/migration/migration-192-193.sql
@@ -1 +1,6 @@
 -- Migration from 192 to 193
+
+ALTER TABLE EXPERIMENT_PROPERTIES DROP CONSTRAINT EXPR_BK_UK;
+
+ALTER TABLE IF EXISTS PROPERTY_TYPES
+    ADD COLUMN IS_MULTI_VALUE BOOLEAN_CHAR NOT NULL DEFAULT 'F';
-- 
GitLab