diff --git a/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
index 2619f4537a9dfdcc3d5561b50bff63a94eff5d96..687af47485679ffd7553165cb88bf2e794cf0736 100644
--- a/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
+++ b/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
@@ -2,6 +2,7 @@ package at.tuwien.endpoints;
 
 import at.tuwien.api.container.*;
 import at.tuwien.api.error.ApiErrorDto;
+import at.tuwien.api.semantics.OntologyDto;
 import at.tuwien.entities.container.Container;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
@@ -10,6 +11,7 @@ import at.tuwien.service.UserService;
 import at.tuwien.service.impl.ContainerServiceImpl;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -60,7 +62,7 @@ public class ContainerEndpoint {
                     description = "List containers",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = ContainerBriefDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = ContainerBriefDto.class)))}),
     })
     public ResponseEntity<List<ContainerBriefDto>> findAll(Principal principal,
                                                            @RequestParam(required = false) Integer limit) {
diff --git a/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java b/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
index aa854398e960e056ada4920b73ebddcdc2031900..73ca916d7296010878e66fde4bc24437573301be 100644
--- a/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
+++ b/dbrepo-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
@@ -1,5 +1,6 @@
 package at.tuwien.endpoints;
 
+import at.tuwien.api.container.ContainerBriefDto;
 import at.tuwien.api.container.image.ImageBriefDto;
 import at.tuwien.api.container.image.ImageChangeDto;
 import at.tuwien.api.container.image.ImageCreateDto;
@@ -11,6 +12,7 @@ import at.tuwien.mapper.ImageMapper;
 import at.tuwien.service.impl.ImageServiceImpl;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -55,7 +57,7 @@ public class ImageEndpoint {
                     description = "List images",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = ContainerImage[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = ContainerImage.class)))}),
     })
     public ResponseEntity<List<ImageBriefDto>> findAll(@NotNull Principal principal) {
         log.debug("endpoint find all images, principal={}", principal);
@@ -182,7 +184,7 @@ public class ImageEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<?> delete(@NotNull @PathVariable Long imageId,
+    public ResponseEntity<?> delete(@NotNull @PathVariable("id") Long imageId,
                                     @NotNull Principal principal) throws ImageNotFoundException {
         log.debug("endpoint delete image, id={}, principal={}", imageId, principal);
         imageService.find(imageId);
diff --git a/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
index e07a29bf0b2012e4910602939f8b4125f1607976..40a45b0cce76ed6d69c2bbedb344a9fb9ea64493 100644
--- a/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
+++ b/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
@@ -1,5 +1,6 @@
 package at.tuwien.endpoints;
 
+import at.tuwien.api.container.ContainerBriefDto;
 import at.tuwien.api.container.ContainerDto;
 import at.tuwien.api.database.*;
 import at.tuwien.api.error.ApiErrorDto;
@@ -14,6 +15,7 @@ import at.tuwien.service.*;
 import at.tuwien.service.impl.MariaDbServiceImpl;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -72,7 +74,7 @@ public class DatabaseEndpoint {
                     description = "List of databases",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = DatabaseBriefDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = DatabaseBriefDto.class)))}),
     })
     public ResponseEntity<List<DatabaseBriefDto>> list(@NotNull @PathVariable("id") Long containerId,
                                                        @NotNull Principal principal) {
diff --git a/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java b/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java
index 8a2a9e62e49443ae50902a4fe854dff7cf21e511..14105173421f5d044a5ee89a8eee49f156d4e34c 100644
--- a/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java
+++ b/dbrepo-database-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java
@@ -1,5 +1,6 @@
 package at.tuwien.endpoints;
 
+import at.tuwien.api.container.ContainerBriefDto;
 import at.tuwien.api.database.DatabaseBriefDto;
 import at.tuwien.api.database.LicenseDto;
 import at.tuwien.api.error.ApiErrorDto;
@@ -7,6 +8,7 @@ import at.tuwien.mapper.LicenseMapper;
 import at.tuwien.service.LicenseService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -47,7 +49,7 @@ public class LicenseEndpoint {
                     description = "List of licenses",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = DatabaseBriefDto.class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = LicenseDto.class)))}),
     })
     public ResponseEntity<List<LicenseDto>> list(@NotBlank @PathVariable("id") Long containerId) {
         log.debug("endpoint list licenses, containerId={}", containerId);
diff --git a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
index fffddcf42347b7a4d9025bfcdebcf58a947287df..fd0dc378acc18d3336ea00bba073b256409b8c87 100644
--- a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
+++ b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
@@ -1,5 +1,6 @@
 package at.tuwien.endpoints;
 
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.api.identifier.IdentifierCreateDto;
 import at.tuwien.api.identifier.IdentifierDto;
@@ -13,6 +14,7 @@ import at.tuwien.service.IdentifierService;
 import at.tuwien.service.UserService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -62,7 +64,7 @@ public class IdentifierEndpoint {
                     description = "List identifiers",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = IdentifierDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = IdentifierDto.class)))}),
     })
     public ResponseEntity<List<IdentifierDto>> list(@RequestParam(required = false) Long dbid,
                                                     @RequestParam(required = false) Long qid,
diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java
index 8304898a1d836b56c6eb6ab766156cd17537b0a1..857cd13e382cdb233a03b811d6d46ef8f37e5688 100644
--- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java
+++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnConcept.java
@@ -30,10 +30,10 @@ public class TableColumnConcept {
     @Column(nullable = false, columnDefinition = "TEXT")
     private String uri;
 
-    @Column(name = "name")
+    @Column(columnDefinition = "VARCHAR(255)")
     private String name;
 
-    @Column(name = "description")
+    @Column(columnDefinition = "TEXT")
     private String description;
 
     @Column(nullable = false, updatable = false, columnDefinition = "TIMESTAMP")
diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java
index 922f48217f55a78e3becaa28a401da8b34461f07..a5802f853a5dda35a6e2057fd65e44de64b1601f 100644
--- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java
+++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumnUnit.java
@@ -30,10 +30,10 @@ public class TableColumnUnit {
     @Column(nullable = false, columnDefinition = "TEXT")
     private String uri;
 
-    @Column(name = "name")
+    @Column(columnDefinition = "VARCHAR(255)")
     private String name;
 
-    @Column(name = "description")
+    @Column(columnDefinition = "TEXT")
     private String description;
 
     @Column(nullable = false, updatable = false, columnDefinition = "TIMESTAMP")
diff --git a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java
index 6265b027b9757a6b0c311096023478fb0533a957..29951cebd09fc50900586e310634e30d1e0e2a17 100644
--- a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java
+++ b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java
@@ -18,12 +18,16 @@ import at.tuwien.api.database.table.TableCsvDto;
 import at.tuwien.api.database.table.columns.ColumnCreateDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
 import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto;
+import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto;
+import at.tuwien.api.database.table.columns.concepts.UnitSaveDto;
 import at.tuwien.api.database.table.constraints.ConstraintsCreateDto;
 import at.tuwien.api.database.table.constraints.foreignKey.ForeignKeyCreateDto;
 import at.tuwien.api.identifier.*;
 import at.tuwien.api.maintenance.BannerMessageCreateDto;
 import at.tuwien.api.maintenance.BannerMessageTypeDto;
 import at.tuwien.api.maintenance.BannerMessageUpdateDto;
+import at.tuwien.api.semantics.OntologyCreateDto;
+import at.tuwien.api.semantics.OntologyModifyDto;
 import at.tuwien.api.user.*;
 import at.tuwien.entities.container.image.ContainerImageDate;
 import at.tuwien.entities.database.*;
@@ -36,6 +40,7 @@ import at.tuwien.entities.database.table.constraints.unique.Unique;
 import at.tuwien.entities.identifier.*;
 import at.tuwien.entities.maintenance.BannerMessage;
 import at.tuwien.entities.maintenance.BannerMessageType;
+import at.tuwien.entities.semantics.Ontology;
 import at.tuwien.entities.user.Realm;
 import at.tuwien.entities.user.Role;
 import at.tuwien.entities.user.User;
@@ -106,6 +111,13 @@ import static java.time.temporal.ChronoUnit.*;
  */
 public abstract class BaseTest {
 
+    public final static String[] DEFAULT_SEMANTICS_HANDLING = new String[]{"default-semantics-handling",
+            "create-semantic-unit", "execute-semantic-query", "table-semantic-analyse", "create-semantic-concept"};
+
+    public final static String[] ESCALATED_SEMANTICS_HANDLING = new String[]{"escalated-semantics-handling",
+            "update-semantic-concept", "modify-foreign-table-column-semantics", "delete-ontology", "list-ontologies",
+            "update-semantic-unit", "create-ontology", "update-ontology"};
+
     public final static String[] DEFAULT_CONTAINER_HANDLING = new String[]{"default-container-handling",
             "create-container", "list-containers", "modify-container-state", "find-container"};
 
@@ -146,7 +158,7 @@ public abstract class BaseTest {
 
     public final static String[] DEFAULT_RESEARCHER_ROLES = ArrayUtil.merge(List.of(new String[]{"default-researcher-roles"},
             DEFAULT_CONTAINER_HANDLING, DEFAULT_DATABASE_HANDLING, DEFAULT_IDENTIFIER_HANDLING, DEFAULT_QUERY_HANDLING,
-            DEFAULT_TABLE_HANDLING, DEFAULT_USER_HANDLING));
+            DEFAULT_TABLE_HANDLING, DEFAULT_USER_HANDLING, DEFAULT_SEMANTICS_HANDLING));
 
     public final static String[] DEFAULT_DEVELOPER_ROLES = ArrayUtil.merge(List.of(new String[]{"default-developer-roles"},
             DEFAULT_CONTAINER_HANDLING, DEFAULT_DATABASE_HANDLING, DEFAULT_IDENTIFIER_HANDLING, DEFAULT_QUERY_HANDLING,
@@ -155,7 +167,7 @@ public abstract class BaseTest {
             ESCALATED_TABLE_HANDLING));
 
     public final static String[] DEFAULT_DATA_STEWARD_ROLES = ArrayUtil.merge(List.of(new String[]{"default-data-steward-roles"},
-            ESCALATED_IDENTIFIER_HANDLING));
+            ESCALATED_IDENTIFIER_HANDLING, DEFAULT_SEMANTICS_HANDLING, ESCALATED_SEMANTICS_HANDLING));
 
     public final static List<GrantedAuthorityDto> AUTHORITY_DEFAULT_RESEARCHER_ROLES = Arrays.stream(DEFAULT_RESEARCHER_ROLES)
             .map(GrantedAuthorityDto::new)
@@ -1780,26 +1792,165 @@ public abstract class BaseTest {
             }})
             .build();
 
+    public final static Long ONTOLOGY_1_ID = 1L;
+    public final static String ONTOLOGY_1_PREFIX = "om2";
+    public final static String ONTOLOGY_1_NEW_PREFIX = "om-2";
+    public final static String ONTOLOGY_1_URI = "http://www.ontology-of-units-of-measure.org/resource/om-2/";
+    public final static String ONTOLOGY_1_SPARQL_ENDPOINT = null;
+    public final static UUID ONTOLOGY_1_CREATED_BY = USER_1_ID;
+
+    public final static Ontology ONTOLOGY_1 = Ontology.builder()
+            .id(ONTOLOGY_1_ID)
+            .prefix(ONTOLOGY_1_PREFIX)
+            .uri(ONTOLOGY_1_URI)
+            .sparqlEndpoint(ONTOLOGY_1_SPARQL_ENDPOINT)
+            .createdBy(ONTOLOGY_1_CREATED_BY)
+            .build();
+
+    public final static OntologyCreateDto ONTOLOGY_1_CREATE_DTO = OntologyCreateDto.builder()
+            .prefix(ONTOLOGY_1_PREFIX)
+            .uri(ONTOLOGY_1_URI)
+            .sparqlEndpoint(ONTOLOGY_1_SPARQL_ENDPOINT)
+            .build();
+
+    public final static OntologyModifyDto ONTOLOGY_1_MODIFY_DTO = OntologyModifyDto.builder()
+            .prefix(ONTOLOGY_1_NEW_PREFIX)
+            .uri(ONTOLOGY_1_URI)
+            .sparqlEndpoint(ONTOLOGY_1_SPARQL_ENDPOINT)
+            .build();
+
+    public final static Long ONTOLOGY_2_ID = 2L;
+    public final static String ONTOLOGY_2_PREFIX = "wd";
+    public final static String ONTOLOGY_2_URI = "http://www.wikidata.org/";
+    public final static String ONTOLOGY_2_SPARQL_ENDPOINT = "https://query.wikidata.org/sparql";
+    public final static UUID ONTOLOGY_2_CREATED_BY = USER_1_ID;
+
+    public final static Ontology ONTOLOGY_2 = Ontology.builder()
+            .id(ONTOLOGY_2_ID)
+            .prefix(ONTOLOGY_2_PREFIX)
+            .uri(ONTOLOGY_2_URI)
+            .sparqlEndpoint(ONTOLOGY_2_SPARQL_ENDPOINT)
+            .createdBy(ONTOLOGY_2_CREATED_BY)
+            .build();
+
+    public final static OntologyCreateDto ONTOLOGY_2_CREATE_DTO = OntologyCreateDto.builder()
+            .prefix(ONTOLOGY_2_PREFIX)
+            .uri(ONTOLOGY_2_URI)
+            .sparqlEndpoint(ONTOLOGY_2_SPARQL_ENDPOINT)
+            .build();
+
+    public final static Long ONTOLOGY_3_ID = 3L;
+    public final static String ONTOLOGY_3_PREFIX = "rdfs";
+    public final static String ONTOLOGY_3_URI = "http://www.w3.org/2000/01/rdf-schema#";
+    public final static String ONTOLOGY_3_SPARQL_ENDPOINT = null;
+    public final static UUID ONTOLOGY_3_CREATED_BY = USER_1_ID;
+
+    public final static Ontology ONTOLOGY_3 = Ontology.builder()
+            .id(ONTOLOGY_3_ID)
+            .prefix(ONTOLOGY_3_PREFIX)
+            .uri(ONTOLOGY_3_URI)
+            .sparqlEndpoint(ONTOLOGY_3_SPARQL_ENDPOINT)
+            .createdBy(ONTOLOGY_3_CREATED_BY)
+            .build();
+
+    public final static OntologyCreateDto ONTOLOGY_3_CREATE_DTO = OntologyCreateDto.builder()
+            .prefix(ONTOLOGY_3_PREFIX)
+            .uri(ONTOLOGY_3_URI)
+            .sparqlEndpoint(ONTOLOGY_3_SPARQL_ENDPOINT)
+            .build();
+
+    public final static Long ONTOLOGY_4_ID = 4L;
+    public final static String ONTOLOGY_4_PREFIX = "schema";
+    public final static String ONTOLOGY_4_URI = "http://schema.org/";
+    public final static String ONTOLOGY_4_SPARQL_ENDPOINT = null;
+    public final static UUID ONTOLOGY_4_CREATED_BY = USER_1_ID;
+
+    public final static Ontology ONTOLOGY_4 = Ontology.builder()
+            .id(ONTOLOGY_4_ID)
+            .prefix(ONTOLOGY_4_PREFIX)
+            .uri(ONTOLOGY_4_URI)
+            .sparqlEndpoint(ONTOLOGY_4_SPARQL_ENDPOINT)
+            .createdBy(ONTOLOGY_4_CREATED_BY)
+            .build();
+
+    public final static OntologyCreateDto ONTOLOGY_4_CREATE_DTO = OntologyCreateDto.builder()
+            .prefix(ONTOLOGY_4_PREFIX)
+            .uri(ONTOLOGY_4_URI)
+            .sparqlEndpoint(ONTOLOGY_4_SPARQL_ENDPOINT)
+            .build();
+
     public final static String COLUMN_CONCEPT_TEMPERATURE_NAME = "temperature";
-    public final static String COLUMN_CONCEPT_TEMPERATURE_URI = "https://www.wikidata.org/entity/Q11466";
+    public final static String COLUMN_CONCEPT_TEMPERATURE_URI = "http://www.wikidata.org/entity/Q11466";
+    public final static String COLUMN_CONCEPT_TEMPERATURE_DESCRIPTION = "physical property of matter that quantitatively expresses the common notions of hot and cold";
     public final static Instant COLUMN_CONCEPT_TEMPERATURE_CREATED = Instant.now();
 
-    public final static TableColumnConcept COLUMN_CONCEPT_TEMPERATURE = TableColumnConcept.builder()
+    public final static ConceptSaveDto COLUMN_CONCEPT_TEMPERATURE_SAVE_DTO = ConceptSaveDto.builder()
+            .uri(COLUMN_CONCEPT_TEMPERATURE_URI)
             .name(COLUMN_CONCEPT_TEMPERATURE_NAME)
+            .description(COLUMN_CONCEPT_TEMPERATURE_DESCRIPTION)
+            .build();
+
+    public final static TableColumnConcept COLUMN_CONCEPT_TEMPERATURE = TableColumnConcept.builder()
             .uri(COLUMN_CONCEPT_TEMPERATURE_URI)
+            .name(COLUMN_CONCEPT_TEMPERATURE_NAME)
+            .description(COLUMN_CONCEPT_TEMPERATURE_DESCRIPTION)
             .created(COLUMN_CONCEPT_TEMPERATURE_CREATED)
             .build();
 
+    public final static String COLUMN_CONCEPT_FAIR_DATA_NAME = "FAIR data";
+    public final static String COLUMN_CONCEPT_FAIR_DATA_URI = "http://www.wikidata.org/entity/Q29032648";
+    public final static String COLUMN_CONCEPT_FAIR_DATA_DESCRIPTION = "data compliant with the terms of the FAIR Data Principles";
+    public final static Instant COLUMN_CONCEPT_FAIR_DATA_CREATED = Instant.now();
+
+    public final static ConceptSaveDto COLUMN_CONCEPT_FAIR_DATA_SAVE_DTO = ConceptSaveDto.builder()
+            .uri(COLUMN_CONCEPT_FAIR_DATA_URI)
+            .name(COLUMN_CONCEPT_FAIR_DATA_NAME)
+            .description(COLUMN_CONCEPT_FAIR_DATA_DESCRIPTION)
+            .build();
+
+    public final static TableColumnConcept COLUMN_CONCEPT_FAIR_DATA = TableColumnConcept.builder()
+            .uri(COLUMN_CONCEPT_FAIR_DATA_URI)
+            .name(COLUMN_CONCEPT_FAIR_DATA_NAME)
+            .description(COLUMN_CONCEPT_FAIR_DATA_DESCRIPTION)
+            .created(COLUMN_CONCEPT_FAIR_DATA_CREATED)
+            .build();
+
     public final static String COLUMN_UNIT_DEGREES_CELSIUS_NAME = "Degrees Celsius";
     public final static String COLUMN_UNIT_DEGREES_CELSIUS_URI = "http://www.ontology-of-units-of-measure.org/resource/om-2/degreeCelsius";
+    public final static String COLUMN_UNIT_DEGREES_CELSIUS_DESCRIPTION = "The degree Celsius is a unit of temperature defined as 1 kelvin.";
     public final static Instant COLUMN_UNIT_DEGREES_CELSIUS_CREATED = Instant.now();
 
-    public final static TableColumnUnit COLUMN_UNIT_DEGREES_CELSIUS = TableColumnUnit.builder()
+    public final static UnitSaveDto COLUMN_UNIT_DEGREES_CELSIUS_SAVE_DTO = UnitSaveDto.builder()
+            .uri(COLUMN_UNIT_DEGREES_CELSIUS_URI)
             .name(COLUMN_UNIT_DEGREES_CELSIUS_NAME)
+            .description(COLUMN_UNIT_DEGREES_CELSIUS_DESCRIPTION)
+            .build();
+
+    public final static TableColumnUnit COLUMN_UNIT_DEGREES_CELSIUS = TableColumnUnit.builder()
             .uri(COLUMN_UNIT_DEGREES_CELSIUS_URI)
+            .name(COLUMN_UNIT_DEGREES_CELSIUS_NAME)
+            .description(COLUMN_UNIT_DEGREES_CELSIUS_DESCRIPTION)
             .created(COLUMN_CONCEPT_TEMPERATURE_CREATED)
             .build();
 
+    public final static String COLUMN_UNIT_TON_NAME = "tonne";
+    public final static String COLUMN_UNIT_TON_URI = "http://www.ontology-of-units-of-measure.org/resource/om-2/tonne";
+    public final static String COLUMN_UNIT_TON_DESCRIPTION = "The tonne is a unit of mass defined as 1000 kilogram.";
+    public final static Instant COLUMN_UNIT_TON_CREATED = Instant.now();
+
+    public final static UnitSaveDto COLUMN_UNIT_TON_SAVE_DTO = UnitSaveDto.builder()
+            .uri(COLUMN_UNIT_TON_URI)
+            .name(COLUMN_UNIT_TON_NAME)
+            .description(COLUMN_UNIT_TON_DESCRIPTION)
+            .build();
+
+    public final static TableColumnUnit COLUMN_UNIT_TON = TableColumnUnit.builder()
+            .uri(COLUMN_UNIT_TON_URI)
+            .name(COLUMN_UNIT_TON_NAME)
+            .description(COLUMN_UNIT_TON_DESCRIPTION)
+            .created(COLUMN_UNIT_TON_CREATED)
+            .build();
+
     public final static Long COLUMN_1_1_ID = 1L;
     public final static Integer COLUMN_1_1_ORDINALPOS = 0;
     public final static Boolean COLUMN_1_1_PRIMARY = true;
@@ -5539,7 +5690,7 @@ public abstract class BaseTest {
             .displayStart(BANNER_MESSAGE_1_START)
             .displayEnd(BANNER_MESSAGE_1_END)
             .build();
-    
+
     public final static BannerMessageCreateDto BANNER_MESSAGE_1_CREATE_DTO = BannerMessageCreateDto.builder()
             .message(BANNER_MESSAGE_1_MESSAGE)
             .type(BANNER_MESSAGE_1_TYPE_DTO)
diff --git a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java
index c5d30f6dacf6093dde4310cfd1e01db417020679..6224be726b5b0b8a9cfd110857652d4780d32a12 100644
--- a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java
+++ b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java
@@ -50,7 +50,7 @@ public class QueryEndpoint {
     @PostMapping
     @Transactional(readOnly = true)
     @Timed(value = "query.execute", description = "Time needed to execute a query")
-    @PreAuthorize("isAuthenticated()")
+    @PreAuthorize("hasAuthority('execute-query')")
     @Operation(summary = "Execute query", security = @SecurityRequirement(name = "bearerAuth"))
     public ResponseEntity<QueryResultDto> execute(@NotNull @PathVariable("id") Long containerId,
                                                   @NotNull @PathVariable("databaseId") Long databaseId,
@@ -179,7 +179,7 @@ public class QueryEndpoint {
             }
         }
         final Query query = storeService.findOne(containerId, databaseId, queryId, principal);
-        log.trace("querystore returned query {}", query);
+        log.trace("query store returned query {}", query);
         final ExportResource resource = queryService.findOne(containerId, databaseId, queryId, principal);
         if (accept == null || accept.equals("text/csv")) {
             final HttpHeaders headers = new HttpHeaders();
diff --git a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java
index 14e3548dd428760a3ed6f4245e48411370e4d957..9b043f3ea97bb51a9f17319450340ac2f26eaf45 100644
--- a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java
+++ b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/StoreEndpoint.java
@@ -2,6 +2,7 @@ package at.tuwien.endpoint;
 
 import at.tuwien.api.database.query.QueryBriefDto;
 import at.tuwien.api.database.query.QueryDto;
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.entities.identifier.Identifier;
 import at.tuwien.entities.identifier.IdentifierType;
@@ -15,6 +16,7 @@ import at.tuwien.service.*;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -71,7 +73,7 @@ public class StoreEndpoint {
                     description = "List queries",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = QueryBriefDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = QueryBriefDto.class)))}),
             @ApiResponse(responseCode = "404",
                     description = "Database, container or user could not be found",
                     content = {@Content(
diff --git a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableHistoryEndpoint.java b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableHistoryEndpoint.java
index 0933749a0ecea839380686e6cefde6441284c92b..f0d05319d346b3e3664f609fd5763d93bdfbd388 100644
--- a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableHistoryEndpoint.java
+++ b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableHistoryEndpoint.java
@@ -1,11 +1,13 @@
 package at.tuwien.endpoint;
 
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.database.table.TableHistoryDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.exception.*;
 import at.tuwien.service.*;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -43,7 +45,7 @@ public class TableHistoryEndpoint {
                     description = "Find table history successfully",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = TableHistoryDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = TableHistoryDto.class)))}),
             @ApiResponse(responseCode = "400",
                     description = "Table history query is malformed",
                     content = {@Content(
diff --git a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java
index 8a7001f019277444da77e89286974e270bbc9632..a1149174e4c7df954d0c154523a6347d94960053 100644
--- a/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java
+++ b/dbrepo-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java
@@ -4,6 +4,7 @@ import at.tuwien.api.database.ViewBriefDto;
 import at.tuwien.api.database.ViewCreateDto;
 import at.tuwien.api.database.ViewDto;
 import at.tuwien.api.database.query.QueryResultDto;
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.View;
@@ -14,6 +15,7 @@ import at.tuwien.service.*;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -64,7 +66,7 @@ public class ViewEndpoint {
                     description = "Find views successfully",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = ViewBriefDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = ViewBriefDto.class)))}),
             @ApiResponse(responseCode = "404",
                     description = "Database or user could not be found",
                     content = {@Content(
diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
index 7f2d6f2a91b7c97de6673fc22fe691427b0d8f81..74433460a69c41526af16f95d01cf3811ce80945 100644
--- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
+++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
@@ -1,15 +1,13 @@
 package at.tuwien.endpoints;
 
 import at.tuwien.api.error.ApiErrorDto;
-import at.tuwien.api.semantics.OntologyBriefDto;
-import at.tuwien.api.semantics.OntologyCreateDto;
-import at.tuwien.api.semantics.OntologyDto;
-import at.tuwien.api.semantics.OntologyModifyDto;
+import at.tuwien.api.semantics.*;
 import at.tuwien.exception.OntologyNotFoundException;
 import at.tuwien.mapper.OntologyMapper;
 import at.tuwien.service.OntologyService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -51,7 +49,7 @@ public class OntologyEndpoint {
                     description = "List all ontologies",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = OntologyDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = OntologyDto.class)))}),
     })
     public ResponseEntity<List<OntologyBriefDto>> findAll() {
         log.debug("endpoint find all ontologies");
diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java
index f28f49dc4c2cbc3ead2e1cf8f7f4786779b197fa..9d1ed742fbf29dc6434f17767c4b77fa8378bf3a 100644
--- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java
+++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java
@@ -11,6 +11,7 @@ import at.tuwien.service.OntologyService;
 import at.tuwien.service.QueryService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -49,7 +50,7 @@ public class QueryEndpoint {
                     description = "Found entities",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = EntityDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = EntityDto.class)))}),
             @ApiResponse(responseCode = "400",
                     description = "Filter params are invalid",
                     content = {@Content(
diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java
index 5bcf6abdf01ab634d14b18733a4bb793ab6dd2d1..ce2f64c63807dfdcfdeab16a0f874f6514b8f158 100644
--- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java
+++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java
@@ -6,6 +6,7 @@ import at.tuwien.mapper.SemanticMapper;
 import at.tuwien.service.SemanticService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -15,6 +16,7 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
@@ -48,7 +50,7 @@ public class SemanticsEndpoint {
                     description = "Find all semantic concepts",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = ConceptDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = ConceptDto.class)))}),
     })
     public ResponseEntity<List<ConceptDto>> findAllConcepts() {
         log.debug("endpoint list concepts");
@@ -73,11 +75,11 @@ public class SemanticsEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ConceptDto.class))}),
     })
-    public ResponseEntity<ConceptDto> saveConcept(@NotNull @Valid @RequestBody ConceptSaveDto data) {
+    public ResponseEntity<ConceptDto> saveUnit(@NotNull @Valid @RequestBody ConceptSaveDto data) {
         log.debug("endpoint save concept, data={}", data);
         final ConceptDto dto = ontologyMapper.tableColumnConceptToConceptDto(semanticService.saveConcept(data));
         log.trace("save concept resulted in dto {}", dto);
-        return ResponseEntity.ok()
+        return ResponseEntity.accepted()
                 .body(dto);
     }
 
@@ -90,7 +92,7 @@ public class SemanticsEndpoint {
                     description = "Find all semantic units",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = UnitDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = UnitDto.class)))}),
     })
     public ResponseEntity<List<UnitDto>> findAllUnits() {
         log.debug("endpoint list units");
@@ -115,7 +117,7 @@ public class SemanticsEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = UnitDto.class))}),
     })
-    public ResponseEntity<UnitDto> saveConcept(@NotNull @Valid @RequestBody UnitSaveDto data) {
+    public ResponseEntity<UnitDto> saveUnit(@NotNull @Valid @RequestBody UnitSaveDto data) {
         log.debug("endpoint save or update unit, data={}", data);
         final UnitDto dto = ontologyMapper.tableColumnUnitToUnitDto(semanticService.saveUnit(data));
         log.trace("save unit resulted in dto {}", dto);
diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index cc3689fdedb5e002351315bfc3d6c11b1032cf56..3ce65ac4519e811467857b25c841a867dc38315c 100644
--- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -9,6 +9,7 @@ import at.tuwien.exception.TableNotFoundException;
 import at.tuwien.service.TableService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -47,7 +48,7 @@ public class TableEndpoint {
                     description = "Suggested table semantics successfully",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = TableColumnEntityDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = TableColumnEntityDto.class)))}),
             @ApiResponse(responseCode = "404",
                     description = "Could not find the table",
                     content = {@Content(
@@ -79,7 +80,7 @@ public class TableEndpoint {
                     description = "Suggested table column semantics successfully",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = TableColumnEntityDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = TableColumnEntityDto.class)))}),
             @ApiResponse(responseCode = "404",
                     description = "Could not find the table column",
                     content = {@Content(
diff --git a/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/OntologyEndpointUnitTest.java b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/OntologyEndpointUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a412b6c965b80dd7f89af1cc64db20d9d1accab2
--- /dev/null
+++ b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/OntologyEndpointUnitTest.java
@@ -0,0 +1,290 @@
+
+package at.tuwien.endpoint;
+
+import at.tuwien.BaseUnitTest;
+import at.tuwien.api.semantics.OntologyBriefDto;
+import at.tuwien.api.semantics.OntologyCreateDto;
+import at.tuwien.api.semantics.OntologyDto;
+import at.tuwien.api.semantics.OntologyModifyDto;
+import at.tuwien.endpoints.OntologyEndpoint;
+import at.tuwien.entities.semantics.Ontology;
+import at.tuwien.exception.OntologyNotFoundException;
+import at.tuwien.service.OntologyService;
+import lombok.extern.log4j.Log4j2;
+import org.apache.jena.sys.JenaSystem;
+import org.hibernate.HibernateException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.test.context.support.WithAnonymousUser;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.security.Principal;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@Log4j2
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+public class OntologyEndpointUnitTest extends BaseUnitTest {
+
+    @Autowired
+    private OntologyEndpoint ontologyEndpoint;
+
+    @MockBean
+    private OntologyService ontologyService;
+
+    @BeforeAll
+    public static void beforeAll() {
+        JenaSystem.init();
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findAll_anonymous_succeeds() {
+
+        /* test */
+        findAll_generic();
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void findAll_noRole_succeeds() {
+
+        /* test */
+        findAll_generic();
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void find_anonymous_succeeds() throws OntologyNotFoundException {
+
+        /* test */
+        find_generic(ONTOLOGY_1_ID, ONTOLOGY_1);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void find_notFound_fails() {
+
+        /* test */
+        assertThrows(OntologyNotFoundException.class, () -> {
+            find_generic(99999L, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void find_noRole_succeeds() throws OntologyNotFoundException {
+
+        /* test */
+        find_generic(ONTOLOGY_1_ID, ONTOLOGY_1);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void create_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            create_generic(ONTOLOGY_1_CREATE_DTO, null, ONTOLOGY_1);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void create_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            create_generic(ONTOLOGY_1_CREATE_DTO, USER_4_PRINCIPAL, ONTOLOGY_1);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"create-ontology"})
+    public void create_hasRole_succeeds() {
+
+        /* test */
+        create_generic(ONTOLOGY_1_CREATE_DTO, USER_3_PRINCIPAL, ONTOLOGY_1);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void update_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            update_generic(ONTOLOGY_1_ID, ONTOLOGY_1_MODIFY_DTO, null, ONTOLOGY_1);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void update_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            update_generic(ONTOLOGY_1_ID, ONTOLOGY_1_MODIFY_DTO, USER_4_PRINCIPAL, ONTOLOGY_1);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"update-ontology"})
+    public void update_hasRoleNotFound_fails() {
+
+        /* test */
+        assertThrows(OntologyNotFoundException.class, () -> {
+            update_generic(ONTOLOGY_1_ID, ONTOLOGY_1_MODIFY_DTO, USER_3_PRINCIPAL, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"update-ontology"})
+    public void update_hasRole_succeeds() throws OntologyNotFoundException {
+
+        /* test */
+        update_generic(ONTOLOGY_1_ID, ONTOLOGY_1_MODIFY_DTO, USER_3_PRINCIPAL, ONTOLOGY_1);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void delete_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            delete_generic(ONTOLOGY_1_ID, ONTOLOGY_1);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void delete_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            delete_generic(ONTOLOGY_1_ID, ONTOLOGY_1);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"delete-ontology"})
+    public void delete_hasRoleNotFound_fails() {
+
+        /* test */
+        assertThrows(OntologyNotFoundException.class, () -> {
+            delete_generic(ONTOLOGY_1_ID, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"delete-ontology"})
+    public void delete_hasRole_succeeds() throws OntologyNotFoundException {
+
+        /* test */
+        delete_generic(ONTOLOGY_1_ID, ONTOLOGY_1);
+    }
+
+    /* ################################################################################################### */
+    /* ## GENERIC TEST CASES                                                                            ## */
+    /* ################################################################################################### */
+
+    public void findAll_generic() {
+
+        /* mock */
+        when(ontologyService.findAll())
+                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4));
+
+        /* test */
+        final ResponseEntity<List<OntologyBriefDto>> response = ontologyEndpoint.findAll();
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final List<OntologyBriefDto> body = response.getBody();
+        assertNotNull(body);
+        assertEquals(4, body.size());
+    }
+
+    public void find_generic(Long ontologyId, Ontology ontology) throws OntologyNotFoundException {
+
+        /* mock */
+        if (ontology != null) {
+            when(ontologyService.find(ontologyId))
+                    .thenReturn(ontology);
+        } else {
+            doThrow(OntologyNotFoundException.class)
+                    .when(ontologyService)
+                    .find(ontologyId);
+        }
+
+        /* test */
+        final ResponseEntity<OntologyDto> response = ontologyEndpoint.find(ontologyId);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final OntologyDto body = response.getBody();
+        assertNotNull(body);
+    }
+
+    public void create_generic(OntologyCreateDto createDto, Principal principal, Ontology ontology) {
+
+        /* mock */
+        if (ontology != null) {
+            when(ontologyService.create(createDto))
+                    .thenReturn(ontology);
+        } else {
+            doThrow(HibernateException.class)
+                    .when(ontologyService)
+                    .create(createDto);
+        }
+
+        /* test */
+        final ResponseEntity<OntologyDto> response = ontologyEndpoint.create(createDto, principal);
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        final OntologyDto body = response.getBody();
+        assertNotNull(body);
+    }
+
+    public void update_generic(Long ontologyId, OntologyModifyDto modifyDto, Principal principal, Ontology ontology)
+            throws OntologyNotFoundException {
+
+        /* mock */
+        if (ontology != null) {
+            when(ontologyService.update(ontologyId, modifyDto))
+                    .thenReturn(ontology);
+        } else {
+            doThrow(OntologyNotFoundException.class)
+                    .when(ontologyService)
+                    .update(ontologyId, modifyDto);
+        }
+
+        /* test */
+        final ResponseEntity<OntologyDto> response = ontologyEndpoint.update(ontologyId, modifyDto, principal);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        final OntologyDto body = response.getBody();
+        assertNotNull(body);
+    }
+
+    public void delete_generic(Long ontologyId, Ontology ontology) throws OntologyNotFoundException {
+
+        /* mock */
+        if (ontology != null) {
+            doNothing()
+                    .when(ontologyService)
+                    .delete(ontologyId);
+        } else {
+            doThrow(OntologyNotFoundException.class)
+                    .when(ontologyService)
+                    .delete(ontologyId);
+        }
+
+        /* test */
+        final ResponseEntity<?> response = ontologyEndpoint.delete(ontologyId);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+    }
+}
diff --git a/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/QueryEndpointUnitTest.java b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/QueryEndpointUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..66e2c1cba53d52dc6fd9ec1045451115d7b4ea62
--- /dev/null
+++ b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/QueryEndpointUnitTest.java
@@ -0,0 +1,164 @@
+
+package at.tuwien.endpoint;
+
+import at.tuwien.BaseUnitTest;
+import at.tuwien.api.semantics.*;
+import at.tuwien.endpoints.OntologyEndpoint;
+import at.tuwien.endpoints.QueryEndpoint;
+import at.tuwien.entities.semantics.Ontology;
+import at.tuwien.exception.FilterBadRequestException;
+import at.tuwien.exception.OntologyNotFoundException;
+import at.tuwien.exception.QueryMalformedException;
+import at.tuwien.exception.UriMalformedException;
+import at.tuwien.service.OntologyService;
+import at.tuwien.service.QueryService;
+import lombok.extern.log4j.Log4j2;
+import org.apache.jena.sys.JenaSystem;
+import org.hibernate.HibernateException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.test.context.support.WithAnonymousUser;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.swing.text.html.parser.Entity;
+import java.security.Principal;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@Log4j2
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+public class QueryEndpointUnitTest extends BaseUnitTest {
+
+    @Autowired
+    private QueryEndpoint queryEndpoint;
+
+    @MockBean
+    private QueryService queryService;
+
+    @MockBean
+    private OntologyService ontologyService;
+
+    @BeforeAll
+    public static void beforeAll() {
+        JenaSystem.init();
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void find_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            find_generic(ONTOLOGY_2_ID, "Apache Jena", null, ONTOLOGY_2, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {})
+    public void find_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            find_generic(ONTOLOGY_2_ID, "Apache Jena", null, ONTOLOGY_2, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
+    public void find_hasRoleInvalidParams_succeeds() {
+
+        /* test */
+        assertThrows(FilterBadRequestException.class, () -> {
+            find_generic(ONTOLOGY_2_ID, "Apache Jena", "http://www.wikidata.org/entity/Q1686799", ONTOLOGY_2, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
+    public void find_hasRoleNotOntologyUri_succeeds() {
+
+        /* test */
+        assertThrows(UriMalformedException.class, () -> {
+            find_generic(ONTOLOGY_2_ID, null, "https://wikidata.org/entity/Q1686799", ONTOLOGY_2, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
+    public void find_hasRoleLabel_succeeds() throws UriMalformedException, QueryMalformedException,
+            OntologyNotFoundException, FilterBadRequestException {
+        final EntityDto entityDto = EntityDto.builder()
+                .label("Apache Jena")
+                .uri("http://www.wikidata.org/entity/Q1686799")
+                .build();
+
+        /* test */
+        final List<EntityDto> response = find_generic(ONTOLOGY_2_ID, "Apache Jena", null, ONTOLOGY_2, entityDto);
+        final EntityDto entity0 = response.get(0);
+        assertEquals("Apache Jena", entity0.getLabel());
+        assertEquals("http://www.wikidata.org/entity/Q1686799", entity0.getUri());
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
+    public void find_hasRoleUri_succeeds() throws UriMalformedException, QueryMalformedException,
+            OntologyNotFoundException, FilterBadRequestException {
+        final EntityDto entityDto = EntityDto.builder()
+                .label("Apache Jena")
+                .uri("http://www.wikidata.org/entity/Q1686799")
+                .build();
+
+        /* test */
+        final List<EntityDto> response = find_generic(ONTOLOGY_2_ID, null, "http://www.wikidata.org/entity/Q1686799", ONTOLOGY_2, entityDto);
+        final EntityDto entity0 = response.get(0);
+        assertEquals("Apache Jena", entity0.getLabel());
+        assertEquals("http://www.wikidata.org/entity/Q1686799", entity0.getUri());
+    }
+
+    /* ################################################################################################### */
+    /* ## GENERIC TEST CASES                                                                            ## */
+    /* ################################################################################################### */
+
+    public List<EntityDto> find_generic(Long ontologyId, String label, String uri, Ontology ontology, EntityDto entityDto)
+            throws OntologyNotFoundException, QueryMalformedException, UriMalformedException, FilterBadRequestException {
+
+        /* mock */
+        if (ontology != null) {
+            when(ontologyService.find(ontologyId))
+                    .thenReturn(ontology);
+        } else {
+            doThrow(OntologyNotFoundException.class)
+                    .when(ontologyService)
+                    .find(ontologyId);
+        }
+        if (entityDto != null) {
+            when(queryService.findByLabel(ontology, label))
+                    .thenReturn(List.of(entityDto));
+            when(queryService.findByUri(ontology, uri))
+                    .thenReturn(List.of(entityDto));
+        } else {
+            when(queryService.findByLabel(ontology, label))
+                    .thenReturn(List.of());
+            when(queryService.findByUri(ontology, uri))
+                    .thenReturn(List.of());
+        }
+
+        /* test */
+        final ResponseEntity<List<EntityDto>> response = queryEndpoint.find(ontologyId, label, uri);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final List<EntityDto> body = response.getBody();
+        assertNotNull(body);
+        return body;
+    }
+}
diff --git a/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/SemanticsEndpointUnitTest.java b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/SemanticsEndpointUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d140d7fb1a79905e9ff0142f1924196725c30fe9
--- /dev/null
+++ b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/endpoint/SemanticsEndpointUnitTest.java
@@ -0,0 +1,210 @@
+
+package at.tuwien.endpoint;
+
+import at.tuwien.BaseUnitTest;
+import at.tuwien.api.database.table.columns.concepts.ConceptDto;
+import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto;
+import at.tuwien.api.database.table.columns.concepts.UnitDto;
+import at.tuwien.api.database.table.columns.concepts.UnitSaveDto;
+import at.tuwien.endpoints.SemanticsEndpoint;
+import at.tuwien.entities.database.table.columns.TableColumnConcept;
+import at.tuwien.entities.database.table.columns.TableColumnUnit;
+import at.tuwien.service.SemanticService;
+import lombok.extern.log4j.Log4j2;
+import org.apache.jena.sys.JenaSystem;
+import org.hibernate.HibernateException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.test.context.support.WithAnonymousUser;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+
+@Log4j2
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+public class SemanticsEndpointUnitTest extends BaseUnitTest {
+
+    @Autowired
+    private SemanticsEndpoint semanticsEndpoint;
+
+    @MockBean
+    private SemanticService semanticService;
+
+    @BeforeAll
+    public static void beforeAll() {
+        JenaSystem.init();
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findAllConcepts_anonymous_succeeds() {
+
+        /* test */
+        findAllConcepts_generic();
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {})
+    public void findAllConcepts_noRole_succeeds() {
+
+        /* test */
+        findAllConcepts_generic();
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findAllUnits_anonymous_succeeds() {
+
+        /* test */
+        findAllUnits_generic();
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {})
+    public void findAllUnits_noRole_succeeds() {
+
+        /* test */
+        findAllUnits_generic();
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void saveConcept_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            saveConcept_generic(COLUMN_CONCEPT_TEMPERATURE_SAVE_DTO, COLUMN_CONCEPT_TEMPERATURE);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {})
+    public void saveConcept_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            saveConcept_generic(COLUMN_CONCEPT_TEMPERATURE_SAVE_DTO, COLUMN_CONCEPT_TEMPERATURE);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"create-semantic-concept"})
+    public void saveConcept_hasRole_succeeds() {
+
+        /* test */
+        saveConcept_generic(COLUMN_CONCEPT_TEMPERATURE_SAVE_DTO, COLUMN_CONCEPT_TEMPERATURE);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void saveUnit_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            saveUnit_generic(COLUMN_UNIT_DEGREES_CELSIUS_SAVE_DTO, COLUMN_UNIT_DEGREES_CELSIUS);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {})
+    public void saveUnit_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            saveUnit_generic(COLUMN_UNIT_DEGREES_CELSIUS_SAVE_DTO, COLUMN_UNIT_DEGREES_CELSIUS);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"create-semantic-concept"})
+    public void saveUnit_hasRole_succeeds() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            saveUnit_generic(COLUMN_UNIT_DEGREES_CELSIUS_SAVE_DTO, COLUMN_UNIT_DEGREES_CELSIUS);
+        });
+    }
+
+    /* ################################################################################################### */
+    /* ## GENERIC TEST CASES                                                                            ## */
+    /* ################################################################################################### */
+
+    public void findAllConcepts_generic() {
+
+        /* mock */
+        when(semanticService.findAllConcepts())
+                .thenReturn(List.of(COLUMN_CONCEPT_TEMPERATURE, COLUMN_CONCEPT_FAIR_DATA));
+
+        /* test */
+        final ResponseEntity<List<ConceptDto>> response = semanticsEndpoint.findAllConcepts();
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final List<ConceptDto> body = response.getBody();
+        assertNotNull(body);
+        assertEquals(2, body.size());
+    }
+
+    public void findAllUnits_generic() {
+
+        /* mock */
+        when(semanticService.findAllUnits())
+                .thenReturn(List.of(COLUMN_UNIT_TON, COLUMN_UNIT_DEGREES_CELSIUS));
+
+        /* test */
+        final ResponseEntity<List<UnitDto>> response = semanticsEndpoint.findAllUnits();
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final List<UnitDto> body = response.getBody();
+        assertNotNull(body);
+        assertEquals(2, body.size());
+    }
+
+    public void saveConcept_generic(ConceptSaveDto saveDto, TableColumnConcept concept) {
+
+        /* mock */
+        if (concept != null) {
+            when(semanticService.saveConcept(saveDto))
+                    .thenReturn(concept);
+        } else {
+            doThrow(HibernateException.class)
+                    .when(semanticService)
+                    .saveConcept(saveDto);
+        }
+
+        /* test */
+        final ResponseEntity<ConceptDto> response = semanticsEndpoint.saveUnit(saveDto);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        final ConceptDto body = response.getBody();
+        assertNotNull(body);
+    }
+
+    public void saveUnit_generic(UnitSaveDto saveDto, TableColumnUnit unit) {
+
+        /* mock */
+        if (unit != null) {
+            when(semanticService.saveUnit(saveDto))
+                    .thenReturn(unit);
+        } else {
+            doThrow(HibernateException.class)
+                    .when(semanticService)
+                    .saveUnit(saveDto);
+        }
+
+        /* test */
+        final ResponseEntity<UnitDto> response = semanticsEndpoint.saveUnit(saveDto);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        final UnitDto body = response.getBody();
+        assertNotNull(body);
+    }
+}
diff --git a/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/service/QueryServiceUnitTest.java b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/service/QueryServiceUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a7ee7eb9144bb4f0bcdee3f8b08ea7d1c030851
--- /dev/null
+++ b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/service/QueryServiceUnitTest.java
@@ -0,0 +1,110 @@
+package at.tuwien.service;
+
+import at.tuwien.BaseUnitTest;
+import at.tuwien.api.semantics.EntityDto;
+import at.tuwien.exception.QueryMalformedException;
+import at.tuwien.repository.jpa.OntologyRepository;
+import at.tuwien.repository.jpa.RealmRepository;
+import at.tuwien.repository.jpa.UserRepository;
+import lombok.extern.log4j.Log4j2;
+import org.apache.jena.sys.JenaSystem;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@Log4j2
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+public class QueryServiceUnitTest extends BaseUnitTest {
+
+    @Autowired
+    private RealmRepository realmRepository;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private OntologyRepository ontologyRepository;
+
+    @Autowired
+    private QueryService queryService;
+
+    @BeforeAll
+    public static void beforeAll() {
+        JenaSystem.init();
+    }
+
+    @BeforeEach
+    public void beforeEach() {
+        realmRepository.save(REALM_DBREPO);
+        userRepository.save(USER_1);
+        ontologyRepository.save(ONTOLOGY_1);
+        ontologyRepository.save(ONTOLOGY_2);
+        ontologyRepository.save(ONTOLOGY_3);
+        ontologyRepository.save(ONTOLOGY_4);
+    }
+
+    @Test
+    public void findByLabel_wikidata_succeeds() throws QueryMalformedException {
+
+        /* test */
+        final List<EntityDto> response = queryService.findByLabel(ONTOLOGY_2, "Apache Jena");
+        assertEquals(1, response.size());
+        final EntityDto entity0 = response.get(0);
+        assertEquals("Apache Jena", entity0.getLabel());
+        assertNotNull(entity0.getDescription()) /* user provided */;
+    }
+
+    @Test
+    public void findByLabel_measurements_succeeds() throws QueryMalformedException {
+
+        /* test */
+        final List<EntityDto> response = queryService.findByLabel(ONTOLOGY_1, "tonne");
+        assertEquals(1, response.size());
+        final EntityDto entity0 = response.get(0);
+        assertEquals(COLUMN_UNIT_TON_NAME, entity0.getLabel());
+        assertEquals(COLUMN_UNIT_TON_URI, entity0.getUri());
+        assertEquals(COLUMN_UNIT_TON_DESCRIPTION, entity0.getDescription());
+    }
+
+    @Test
+    public void findByLabel_fails() throws QueryMalformedException {
+
+        /* test */
+        final List<EntityDto> response = queryService.findByLabel(ONTOLOGY_2, "apache jena");
+        assertEquals(0, response.size());
+    }
+
+    @Test
+    public void findByUri_wikidata_succeeds() throws QueryMalformedException {
+
+        /* test */
+        final List<EntityDto> response = queryService.findByUri(ONTOLOGY_2, COLUMN_CONCEPT_TEMPERATURE_URI);
+        assertEquals(1, response.size());
+        final EntityDto entity0 = response.get(0);
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_URI, entity0.getUri());
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_NAME, entity0.getLabel());
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_DESCRIPTION, entity0.getDescription());
+    }
+
+    @Test
+    public void findByUri_measurements_succeeds() throws QueryMalformedException {
+
+        /* test */
+        final List<EntityDto> response = queryService.findByUri(ONTOLOGY_1, COLUMN_UNIT_TON_URI);
+        assertEquals(1, response.size());
+        final EntityDto entity0 = response.get(0);
+        assertEquals(COLUMN_UNIT_TON_URI, entity0.getUri());
+        assertEquals(COLUMN_UNIT_TON_NAME, entity0.getLabel());
+        assertEquals(COLUMN_UNIT_TON_DESCRIPTION, entity0.getDescription());
+    }
+}
diff --git a/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/service/SemanticServiceUnitTest.java b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/service/SemanticServiceUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b997fd6b7d2f65d1c4f68cd03778fde4b2ec67a
--- /dev/null
+++ b/dbrepo-semantics-service/rest-service/src/test/java/at/tuwien/service/SemanticServiceUnitTest.java
@@ -0,0 +1,130 @@
+package at.tuwien.service;
+
+import at.tuwien.BaseUnitTest;
+import at.tuwien.entities.database.table.columns.TableColumnConcept;
+import at.tuwien.entities.database.table.columns.TableColumnUnit;
+import at.tuwien.repository.jpa.*;
+import lombok.extern.log4j.Log4j2;
+import org.apache.jena.sys.JenaSystem;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentMatchers;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+@Log4j2
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+public class SemanticServiceUnitTest extends BaseUnitTest {
+
+    @MockBean
+    private TableColumnConceptRepository tableColumnConceptRepository;
+
+    @MockBean
+    private TableColumnUnitRepository tableColumnUnitRepository;
+
+    @Autowired
+    private SemanticService semanticService;
+
+    @BeforeAll
+    public static void beforeAll() {
+        JenaSystem.init();
+    }
+
+    @Test
+    public void findAllConcepts_succeeds() {
+
+        /* mock */
+        when(tableColumnConceptRepository.findAll())
+                .thenReturn(List.of(COLUMN_CONCEPT_TEMPERATURE));
+
+        /* test */
+        final List<TableColumnConcept> response = semanticService.findAllConcepts();
+        assertEquals(1, response.size());
+        final TableColumnConcept concept0 = response.get(0);
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_URI, concept0.getUri());
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_NAME, concept0.getName());
+    }
+
+    @Test
+    public void findAllUnit_succeeds() {
+
+        /* mock */
+        when(tableColumnUnitRepository.findAll())
+                .thenReturn(List.of(COLUMN_UNIT_DEGREES_CELSIUS));
+
+        /* test */
+        final List<TableColumnUnit> response = semanticService.findAllUnits();
+        assertEquals(1, response.size());
+        final TableColumnUnit unit0 = response.get(0);
+        assertEquals(COLUMN_UNIT_DEGREES_CELSIUS_URI, unit0.getUri());
+        assertEquals(COLUMN_UNIT_DEGREES_CELSIUS_NAME, unit0.getName());
+        assertEquals(COLUMN_UNIT_DEGREES_CELSIUS_DESCRIPTION, unit0.getDescription());
+    }
+
+    @Test
+    public void saveUnit_exists_succeeds() {
+
+        /* mock */
+        when(tableColumnUnitRepository.save(any(TableColumnUnit.class)))
+                .thenReturn(COLUMN_UNIT_DEGREES_CELSIUS);
+
+        /* test */
+        final TableColumnUnit response = semanticService.saveUnit(COLUMN_UNIT_DEGREES_CELSIUS_SAVE_DTO);
+        assertEquals(COLUMN_UNIT_DEGREES_CELSIUS_URI, response.getUri());
+        assertEquals(COLUMN_UNIT_DEGREES_CELSIUS_NAME, response.getName());
+        assertEquals(COLUMN_UNIT_DEGREES_CELSIUS_DESCRIPTION, response.getDescription());
+    }
+
+    @Test
+    public void saveUnit_succeeds() {
+
+        /* mock */
+        when(tableColumnUnitRepository.save(any(TableColumnUnit.class)))
+                .thenReturn(COLUMN_UNIT_TON);
+
+        /* test */
+        final TableColumnUnit response = semanticService.saveUnit(COLUMN_UNIT_TON_SAVE_DTO);
+        assertEquals(COLUMN_UNIT_TON_URI, response.getUri());
+        assertEquals(COLUMN_UNIT_TON_NAME, response.getName());
+        assertEquals(COLUMN_UNIT_TON_DESCRIPTION, response.getDescription());
+    }
+
+    @Test
+    public void saveConcept_exists_succeeds() {
+
+        /* mock */
+        when(tableColumnConceptRepository.save(any(TableColumnConcept.class)))
+                .thenReturn(COLUMN_CONCEPT_TEMPERATURE);
+
+        /* test */
+        final TableColumnConcept response = semanticService.saveConcept(COLUMN_CONCEPT_TEMPERATURE_SAVE_DTO);
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_URI, response.getUri());
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_NAME, response.getName());
+        assertEquals(COLUMN_CONCEPT_TEMPERATURE_DESCRIPTION, response.getDescription());
+    }
+
+    @Test
+    public void saveConcept_succeeds() {
+
+        /* mock */
+        when(tableColumnConceptRepository.save(any(TableColumnConcept.class)))
+                .thenReturn(COLUMN_CONCEPT_FAIR_DATA);
+
+        /* test */
+        final TableColumnConcept response = semanticService.saveConcept(COLUMN_CONCEPT_FAIR_DATA_SAVE_DTO);
+        assertEquals(COLUMN_CONCEPT_FAIR_DATA_URI, response.getUri());
+        assertEquals(COLUMN_CONCEPT_FAIR_DATA_NAME, response.getName());
+        assertEquals(COLUMN_CONCEPT_FAIR_DATA_DESCRIPTION, response.getDescription());
+    }
+}
diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java
index aeec69284c213f542dc76005ac906df7a6fe14c9..4d36f77a6765c6142b6883974eebbc504da092fa 100644
--- a/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java
+++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/mapper/OntologyMapper.java
@@ -56,10 +56,9 @@ public interface OntologyMapper {
                     "SELECT * {",
                     "  SERVICE <" + ontology.getSparqlEndpoint() + "> {",
                     "    SELECT ?o ?label ?description {",
-                    "      OPTIONAL {",
-                    "        ?o rdfs:label ?label",
-                    "        FILTER (LANG(?label) = 'en')",
-                    "      }",
+                    "      ?o rdfs:label \"" + label.replace("\"", "") + "\"@en .",
+                    "      ?o rdfs:label ?label .",
+                    "      FILTER (LANG(?label) = 'en')",
                     "      OPTIONAL {",
                     "        ?o schema:description ?description",
                     "        FILTER (LANG(?description) = 'en')",
diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/ContainerRepository.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/ContainerRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..39566b1cc0c471f71212f9319e1497d0e3dcac66
--- /dev/null
+++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/ContainerRepository.java
@@ -0,0 +1,10 @@
+package at.tuwien.repository.jpa;
+
+import at.tuwien.entities.container.Container;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ContainerRepository extends JpaRepository<Container, Long> {
+
+}
diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/DatabaseRepository.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/DatabaseRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..688993ab3ca2e32edeb59b6c66dc838f58333b02
--- /dev/null
+++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/DatabaseRepository.java
@@ -0,0 +1,10 @@
+package at.tuwien.repository.jpa;
+
+import at.tuwien.entities.database.Database;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface DatabaseRepository extends JpaRepository<Database, Long> {
+
+}
diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/ImageRepository.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/ImageRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..247ce97770d636831aef83e8cc1a471ed67019a9
--- /dev/null
+++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/ImageRepository.java
@@ -0,0 +1,10 @@
+package at.tuwien.repository.jpa;
+
+import at.tuwien.entities.container.image.ContainerImage;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ImageRepository extends JpaRepository<ContainerImage, Long> {
+
+}
diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b5855dec88d3e3ef8d032369a7d6af6956a702e
--- /dev/null
+++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java
@@ -0,0 +1,12 @@
+package at.tuwien.repository.jpa;
+
+import at.tuwien.entities.user.Realm;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.UUID;
+
+@Repository
+public interface RealmRepository extends JpaRepository<Realm, UUID> {
+
+}
diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java
index 94a55ee511b4edb2cdfb2d36a5a2cf25a1e1eba8..34e38612fb8527969226d695b876e6f97d0adeaf 100644
--- a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java
+++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java
@@ -19,16 +19,16 @@ import java.util.List;
 @Service
 public class SemanticServiceImpl implements SemanticService {
 
-    private final TableColumnConceptRepository tableColumnConceptRepository;
-    private final TableColumnUnitRepository tableColumnUnitRepository;
     private final OntologyMapper ontologyMapper;
+    private final TableColumnUnitRepository tableColumnUnitRepository;
+    private final TableColumnConceptRepository tableColumnConceptRepository;
 
     @Autowired
     public SemanticServiceImpl(TableColumnConceptRepository tableColumnConceptRepository,
                                TableColumnUnitRepository tableColumnUnitRepository, OntologyMapper ontologyMapper) {
-        this.tableColumnConceptRepository = tableColumnConceptRepository;
-        this.tableColumnUnitRepository = tableColumnUnitRepository;
         this.ontologyMapper = ontologyMapper;
+        this.tableColumnUnitRepository = tableColumnUnitRepository;
+        this.tableColumnConceptRepository = tableColumnConceptRepository;
     }
 
     @Override
diff --git a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index ab10207e973b73ece4339ab2072fbe77a60029ae..a5d7bf648c6df653f428dd6414a7957f9d3a3a90 100644
--- a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -1,5 +1,6 @@
 package at.tuwien.endpoints;
 
+import at.tuwien.api.container.ContainerBriefDto;
 import at.tuwien.api.database.table.*;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.entities.database.table.Table;
@@ -10,6 +11,7 @@ import at.tuwien.service.TableService;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -58,7 +60,7 @@ public class TableEndpoint {
                     description = "List tables",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = TableBriefDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = TableBriefDto.class)))}),
             @ApiResponse(responseCode = "404",
                     description = "Database could not be found",
                     content = {@Content(
diff --git a/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/MaintenanceEndpoint.java b/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/MaintenanceEndpoint.java
index 18fa41da61db5dc80d8870cb5bb2099ca5dbab81..49fdfc37f472476403edfd3f7555fa244fe69fe6 100644
--- a/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/MaintenanceEndpoint.java
+++ b/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/MaintenanceEndpoint.java
@@ -1,5 +1,6 @@
 package at.tuwien.endpoint;
 
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.maintenance.BannerMessageBriefDto;
 import at.tuwien.api.maintenance.BannerMessageCreateDto;
 import at.tuwien.api.maintenance.BannerMessageDto;
@@ -9,6 +10,7 @@ import at.tuwien.exception.BannerMessageNotFoundException;
 import at.tuwien.mapper.BannerMessageMapper;
 import at.tuwien.service.BannerMessageService;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -46,7 +48,7 @@ public class MaintenanceEndpoint {
                     description = "List messages",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = BannerMessageDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = BannerMessageDto.class)))}),
     })
     public ResponseEntity<List<BannerMessageDto>> list() {
         log.debug("endpoint list active maintenance messages");
@@ -82,7 +84,7 @@ public class MaintenanceEndpoint {
                     description = "List messages",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = BannerMessageBriefDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = BannerMessageBriefDto.class)))}),
     })
     public ResponseEntity<List<BannerMessageBriefDto>> active() {
         log.debug("endpoint list active maintenance messages");
diff --git a/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java b/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
index f83b023b7d1bb1eff0bcad07cf4642fb781a60c6..93bf67e068cafcdf4709161d2afb5addc4bf0ae2 100644
--- a/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
+++ b/dbrepo-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
@@ -1,6 +1,7 @@
 package at.tuwien.endpoint;
 
 import at.tuwien.api.auth.SignupRequestDto;
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.api.user.*;
 import at.tuwien.config.AuthenticationConfig;
@@ -14,6 +15,7 @@ import at.tuwien.service.RoleService;
 import at.tuwien.service.UserService;
 import io.micrometer.core.annotation.Timed;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -65,7 +67,7 @@ public class UserEndpoint {
                     description = "List users",
                     content = {@Content(
                             mediaType = "application/json",
-                            schema = @Schema(implementation = UserBriefDto[].class))}),
+                            array = @ArraySchema(schema = @Schema(implementation = UserBriefDto.class)))}),
     })
     public ResponseEntity<List<UserBriefDto>> findAll() {
         log.debug("endpoint find all users");