Remove Lambda & Restructure File 26/137026/2
authorseanbeirne <sean.beirne@est.tech>
Wed, 17 Jan 2024 15:18:10 +0000 (15:18 +0000)
committerseanbeirne <sean.beirne@est.tech>
Fri, 19 Jan 2024 13:47:50 +0000 (13:47 +0000)
-Removed Lambda from CompositeStateUtils
-Restructured CpsDataPersistenceServiceImpl to more logical layout

Issue-ID: CPS-1238
Change-Id: I09096dab474230d04b15c204cbea95a2f1285366
Signed-off-by: seanbeirne <sean.beirne@est.tech>
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java

index a31332f..0ed95ad 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -164,12 +164,12 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
     }
 
     private void setInitialStates(final YangModelCmHandle yangModelCmHandle) {
-        CompositeStateUtils.setInitialDataStoreSyncState().accept(yangModelCmHandle.getCompositeState());
-        CompositeStateUtils.setCompositeState(READY).accept(yangModelCmHandle.getCompositeState());
+        CompositeStateUtils.setInitialDataStoreSyncState(yangModelCmHandle.getCompositeState());
+        CompositeStateUtils.setCompositeState(READYyangModelCmHandle.getCompositeState());
     }
 
     private void retryCmHandle(final YangModelCmHandle yangModelCmHandle) {
-        CompositeStateUtils.setCompositeStateForRetry().accept(yangModelCmHandle.getCompositeState());
+        CompositeStateUtils.setCompositeStateForRetry(yangModelCmHandle.getCompositeState());
     }
 
     private void registerNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
@@ -178,7 +178,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState
     }
 
     private void setCmHandleState(final YangModelCmHandle yangModelCmHandle, final CmHandleState targetCmHandleState) {
-        CompositeStateUtils.setCompositeState(targetCmHandleState).accept(yangModelCmHandle.getCompositeState());
+        CompositeStateUtils.setCompositeState(targetCmHandleStateyangModelCmHandle.getCompositeState());
     }
 
     private boolean isNew(final CompositeState existingCompositeState) {
index 99cca8c..35ad54f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2022-2023 Nordix Foundation
+ * Copyright (C) 2022-2024 Nordix Foundation
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@
 
 package org.onap.cps.ncmp.api.impl.inventory;
 
-import java.util.function.Consumer;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -34,31 +33,23 @@ public class CompositeStateUtils {
 
     /**
      * Sets the cmHandleState to the provided state and updates the timestamp.
-     *
-     * @return Updated CompositeState
      */
-    public static Consumer<CompositeState> setCompositeState(final CmHandleState cmHandleState) {
-        return compositeState -> {
-            compositeState.setCmHandleState(cmHandleState);
-            compositeState.setLastUpdateTimeNow();
-        };
+    public static void setCompositeState(final CmHandleState cmHandleState,
+                                                   final CompositeState compositeState) {
+        compositeState.setCmHandleState(cmHandleState);
+        compositeState.setLastUpdateTimeNow();
     }
 
     /**
      * Set the Operational datastore sync state based on the global flag.
-     *
-     * @return Updated CompositeState
      */
-    public static Consumer<CompositeState> setInitialDataStoreSyncState() {
-
-        return compositeState -> {
-            compositeState.setDataSyncEnabled(false);
-            final CompositeState.Operational operational =
-                    getInitialDataStoreSyncState(compositeState.getDataSyncEnabled());
-            final CompositeState.DataStores dataStores =
-                    CompositeState.DataStores.builder().operationalDataStore(operational).build();
-            compositeState.setDataStores(dataStores);
-        };
+    public static void setInitialDataStoreSyncState(final CompositeState compositeState) {
+        compositeState.setDataSyncEnabled(false);
+        final CompositeState.Operational operational =
+            getInitialDataStoreSyncState(compositeState.getDataSyncEnabled());
+        final CompositeState.DataStores dataStores =
+            CompositeState.DataStores.builder().operationalDataStore(operational).build();
+        compositeState.setDataStores(dataStores);
     }
 
     /**
@@ -91,19 +82,15 @@ public class CompositeStateUtils {
 
     /**
      * Sets the cmHandleState to ADVISED and retain the lock details. Used in retry scenarios.
-     *
-     * @return Updated CompositeState
      */
-    public static Consumer<CompositeState> setCompositeStateForRetry() {
-        return compositeState -> {
-            compositeState.setCmHandleState(CmHandleState.ADVISED);
-            compositeState.setLastUpdateTimeNow();
-            final String oldLockReasonDetails = compositeState.getLockReason().getDetails();
-            final CompositeState.LockReason lockReason =
-                    CompositeState.LockReason.builder()
-                            .lockReasonCategory(compositeState.getLockReason().getLockReasonCategory())
-                            .details(oldLockReasonDetails).build();
-            compositeState.setLockReason(lockReason);
-        };
+    public static void setCompositeStateForRetry(final CompositeState compositeState) {
+        compositeState.setCmHandleState(CmHandleState.ADVISED);
+        compositeState.setLastUpdateTimeNow();
+        final String oldLockReasonDetails = compositeState.getLockReason().getDetails();
+        final CompositeState.LockReason lockReason =
+            CompositeState.LockReason.builder()
+                .lockReasonCategory(compositeState.getLockReason().getLockReasonCategory())
+                .details(oldLockReasonDetails).build();
+        compositeState.setLockReason(lockReason);
     }
 }
index 1cfe21d..fd47793 100644 (file)
@@ -83,67 +83,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
     private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@.+?])?)";
 
-    @Override
-    public void addChildDataNodes(final String dataspaceName, final String anchorName,
-                                  final String parentNodeXpath, final Collection<DataNode> dataNodes) {
-        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
-        addChildrenDataNodes(anchorEntity, parentNodeXpath, dataNodes);
-    }
-
-    @Override
-    public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
-                                final Collection<DataNode> newListElements) {
-        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
-        addChildrenDataNodes(anchorEntity, parentNodeXpath, newListElements);
-    }
-
-    private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath,
-                                     final DataNode newChild) {
-        final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath);
-        final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild);
-        newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
-        try {
-            fragmentRepository.save(newChildAsFragmentEntity);
-        } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
-            throw AlreadyDefinedException.forDataNodes(Collections.singletonList(newChild.getXpath()),
-                    anchorEntity.getName());
-        }
-    }
-
-    private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath,
-                                      final Collection<DataNode> newChildren) {
-        final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath);
-        final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size());
-        try {
-            for (final DataNode newChildAsDataNode : newChildren) {
-                final FragmentEntity newChildAsFragmentEntity =
-                    convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode);
-                newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
-                fragmentEntities.add(newChildAsFragmentEntity);
-            }
-            fragmentRepository.saveAll(fragmentEntities);
-        } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
-            log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations",
-                    dataIntegrityViolationException, fragmentEntities.size());
-            retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren);
-        }
-    }
-
-    private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath,
-                                                  final Collection<DataNode> newChildren) {
-        final Collection<String> failedXpaths = new HashSet<>();
-        for (final DataNode newChild : newChildren) {
-            try {
-                addNewChildDataNode(anchorEntity, parentNodeXpath, newChild);
-            } catch (final AlreadyDefinedException alreadyDefinedException) {
-                failedXpaths.add(newChild.getXpath());
-            }
-        }
-        if (!failedXpaths.isEmpty()) {
-            throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName());
-        }
-    }
-
     @Override
     public void storeDataNodes(final String dataspaceName, final String anchorName,
                                final Collection<DataNode> dataNodes) {
@@ -157,7 +96,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             fragmentRepository.saveAll(fragmentEntities);
         } catch (final DataIntegrityViolationException exception) {
             log.warn("Exception occurred : {} , While saving : {} data nodes, Retrying saving data nodes individually",
-                    exception, dataNodes.size());
+                exception, dataNodes.size());
             storeDataNodesIndividually(anchorEntity, dataNodes);
         }
     }
@@ -197,79 +136,153 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         return parentFragment;
     }
 
-    private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) {
-        return FragmentEntity.builder()
-                .anchor(anchorEntity)
-                .xpath(dataNode.getXpath())
-                .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves()))
-                .build();
+    @Override
+    public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+                                final Collection<DataNode> newListElements) {
+        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+        addChildrenDataNodes(anchorEntity, parentNodeXpath, newListElements);
     }
 
     @Override
-    @Timed(value = "cps.data.persistence.service.datanode.get",
-            description = "Time taken to get a data node")
-    public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
-                                             final String xpath,
-                                             final FetchDescendantsOption fetchDescendantsOption) {
-        final String targetXpath = getNormalizedXpath(xpath);
-        final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName,
-                Collections.singletonList(targetXpath), fetchDescendantsOption);
-        if (dataNodes.isEmpty()) {
-            throw new DataNodeNotFoundException(dataspaceName, anchorName, xpath);
+    public void addChildDataNodes(final String dataspaceName, final String anchorName,
+                                  final String parentNodeXpath, final Collection<DataNode> dataNodes) {
+        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+        addChildrenDataNodes(anchorEntity, parentNodeXpath, dataNodes);
+    }
+
+    private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath,
+                                      final Collection<DataNode> newChildren) {
+        final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath);
+        final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size());
+        try {
+            for (final DataNode newChildAsDataNode : newChildren) {
+                final FragmentEntity newChildAsFragmentEntity =
+                    convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode);
+                newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
+                fragmentEntities.add(newChildAsFragmentEntity);
+            }
+            fragmentRepository.saveAll(fragmentEntities);
+        } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
+            log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations",
+                dataIntegrityViolationException, fragmentEntities.size());
+            retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren);
         }
-        return dataNodes;
     }
 
-    @Override
-    @Timed(value = "cps.data.persistence.service.datanode.batch.get",
-            description = "Time taken to get data nodes")
-    public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName,
-                                                              final Collection<String> xpaths,
-                                                              final FetchDescendantsOption fetchDescendantsOption) {
-        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
-        Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpaths);
-        fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption,
-                fragmentEntities);
-        return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities);
+    private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath,
+                                     final DataNode newChild) {
+        final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath);
+        final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild);
+        newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
+        try {
+            fragmentRepository.save(newChildAsFragmentEntity);
+        } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
+            throw AlreadyDefinedException.forDataNodes(Collections.singletonList(newChild.getXpath()),
+                anchorEntity.getName());
+        }
     }
 
-    private Collection<FragmentEntity> getFragmentEntities(final AnchorEntity anchorEntity,
-                                                           final Collection<String> xpaths) {
-        final Collection<String> normalizedXpaths = getNormalizedXpaths(xpaths);
+    private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath,
+                                                  final Collection<DataNode> newChildren) {
+        final Collection<String> failedXpaths = new HashSet<>();
+        for (final DataNode newChild : newChildren) {
+            try {
+                addNewChildDataNode(anchorEntity, parentNodeXpath, newChild);
+            } catch (final AlreadyDefinedException alreadyDefinedException) {
+                failedXpaths.add(newChild.getXpath());
+            }
+        }
+        if (!failedXpaths.isEmpty()) {
+            throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName());
+        }
+    }
 
-        final boolean haveRootXpath = normalizedXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath);
+    @Override
+    public void batchUpdateDataLeaves(final String dataspaceName, final String anchorName,
+                                      final Map<String, Map<String, Serializable>> updatedLeavesPerXPath) {
+        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
 
-        final List<FragmentEntity> fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity,
-                normalizedXpaths);
+        final Collection<String> xpathsOfUpdatedLeaves = updatedLeavesPerXPath.keySet();
+        final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpathsOfUpdatedLeaves);
 
         for (final FragmentEntity fragmentEntity : fragmentEntities) {
-            normalizedXpaths.remove(fragmentEntity.getXpath());
+            final Map<String, Serializable> updatedLeaves = updatedLeavesPerXPath.get(fragmentEntity.getXpath());
+            final String mergedLeaves = mergeLeaves(updatedLeaves, fragmentEntity.getAttributes());
+            fragmentEntity.setAttributes(mergedLeaves);
         }
 
-        for (final String xpath : normalizedXpaths) {
-            if (!CpsPathUtil.isPathToListElement(xpath)) {
-                fragmentEntities.addAll(fragmentRepository.findListByAnchorAndXpath(anchorEntity, xpath));
-            }
+        try {
+            fragmentRepository.saveAll(fragmentEntities);
+        } catch (final StaleStateException staleStateException) {
+            retryUpdateDataNodesIndividually(anchorEntity, fragmentEntities);
         }
+    }
 
-        if (haveRootXpath) {
-            fragmentEntities.addAll(fragmentRepository.findRootsByAnchorId(anchorEntity.getId()));
+    @Override
+    public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
+                                              final Collection<DataNode> updatedDataNodes) {
+        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+
+        final Map<String, DataNode> xpathToUpdatedDataNode = updatedDataNodes.stream()
+            .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode));
+
+        final Collection<String> xpaths = xpathToUpdatedDataNode.keySet();
+        Collection<FragmentEntity> existingFragmentEntities = getFragmentEntities(anchorEntity, xpaths);
+        existingFragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(
+            FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS, existingFragmentEntities);
+
+        for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) {
+            final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath());
+            updateFragmentEntityAndDescendantsWithDataNode(existingFragmentEntity, updatedDataNode);
         }
 
-        return fragmentEntities;
+        try {
+            fragmentRepository.saveAll(existingFragmentEntities);
+        } catch (final StaleStateException staleStateException) {
+            retryUpdateDataNodesIndividually(anchorEntity, existingFragmentEntities);
+        }
     }
 
-    private FragmentEntity getFragmentEntity(final AnchorEntity anchorEntity, final String xpath) {
-        final FragmentEntity fragmentEntity;
-        if (isRootXpath(xpath)) {
-            fragmentEntity = fragmentRepository.findOneByAnchorId(anchorEntity.getId()).orElse(null);
-        } else {
-            fragmentEntity = fragmentRepository.getByAnchorAndXpath(anchorEntity, getNormalizedXpath(xpath));
+    private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity,
+                                                  final Collection<FragmentEntity> fragmentEntities) {
+        final Collection<String> failedXpaths = new HashSet<>();
+        for (final FragmentEntity dataNodeFragment : fragmentEntities) {
+            try {
+                fragmentRepository.save(dataNodeFragment);
+            } catch (final StaleStateException staleStateException) {
+                failedXpaths.add(dataNodeFragment.getXpath());
+            }
         }
-        if (fragmentEntity == null) {
-            throw new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath);
+        if (!failedXpaths.isEmpty()) {
+            final String failedXpathsConcatenated = String.join(",", failedXpaths);
+            throw new ConcurrencyException("Concurrent Transactions", String.format(
+                "DataNodes : %s in Dataspace :'%s' with Anchor : '%s'  are updated by another transaction.",
+                failedXpathsConcatenated, anchorEntity.getDataspace().getName(), anchorEntity.getName()));
         }
-        return fragmentEntity;
+    }
+
+    private void updateFragmentEntityAndDescendantsWithDataNode(final FragmentEntity existingFragmentEntity,
+                                                                final DataNode newDataNode) {
+        copyAttributesFromNewDataNode(existingFragmentEntity, newDataNode);
+
+        final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream()
+            .collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity));
+
+        final Collection<FragmentEntity> updatedChildFragments = new HashSet<>();
+        for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) {
+            final FragmentEntity childFragment;
+            if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) {
+                childFragment = convertToFragmentWithAllDescendants(existingFragmentEntity.getAnchor(),
+                    newDataNodeChild);
+            } else {
+                childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath());
+                updateFragmentEntityAndDescendantsWithDataNode(childFragment, newDataNodeChild);
+            }
+            updatedChildFragments.add(childFragment);
+        }
+
+        existingFragmentEntity.getChildFragments().clear();
+        existingFragmentEntity.getChildFragments().addAll(updatedChildFragments);
     }
 
     @Override
@@ -338,11 +351,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities);
     }
 
-    private List<Long> getAnchorIdsForPagination(final DataspaceEntity dataspaceEntity, final CpsPathQuery cpsPathQuery,
-                                                 final PaginationOption paginationOption) {
-        return fragmentRepository.findAnchorIdsForPagination(dataspaceEntity, cpsPathQuery, paginationOption);
-    }
-
     private List<DataNode> createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption,
                                                                final Collection<FragmentEntity> fragmentEntities) {
         final List<DataNode> dataNodes = new ArrayList<>(fragmentEntities.size());
@@ -352,29 +360,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         return Collections.unmodifiableList(dataNodes);
     }
 
-    private static String getNormalizedXpath(final String xpathSource) {
-        if (isRootXpath(xpathSource)) {
-            return xpathSource;
-        }
-        try {
-            return CpsPathUtil.getNormalizedXpath(xpathSource);
-        } catch (final PathParsingException pathParsingException) {
-            throw new CpsPathException(pathParsingException.getMessage());
-        }
-    }
-
-    private static Collection<String> getNormalizedXpaths(final Collection<String> xpaths) {
-        final Collection<String> normalizedXpaths = new HashSet<>(xpaths.size());
-        for (final String xpath : xpaths) {
-            try {
-                normalizedXpaths.add(getNormalizedXpath(xpath));
-            } catch (final CpsPathException cpsPathException) {
-                log.warn("Error parsing xpath \"{}\": {}", xpath, cpsPathException.getMessage());
-            }
-        }
-        return normalizedXpaths;
-    }
-
     @Override
     public String startSession() {
         return sessionManager.startSession();
@@ -404,21 +389,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         return anchorIdList.size();
     }
 
-    private static Set<String> processAncestorXpath(final Collection<FragmentEntity> fragmentEntities,
-                                                    final CpsPathQuery cpsPathQuery) {
-        final Set<String> ancestorXpath = new HashSet<>();
-        final Pattern pattern =
-                Pattern.compile("(.*/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier())
-                        + REG_EX_FOR_OPTIONAL_LIST_INDEX + "/.*");
-        for (final FragmentEntity fragmentEntity : fragmentEntities) {
-            final Matcher matcher = pattern.matcher(fragmentEntity.getXpath());
-            if (matcher.matches()) {
-                ancestorXpath.add(matcher.group(1));
-            }
-        }
-        return ancestorXpath;
-    }
-
     private DataNode toDataNode(final FragmentEntity fragmentEntity,
                                 final FetchDescendantsOption fetchDescendantsOption) {
         final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption);
@@ -434,103 +404,15 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
                 .withChildDataNodes(childDataNodes).build();
     }
 
-    private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
-                                             final FetchDescendantsOption fetchDescendantsOption) {
-        if (fetchDescendantsOption.hasNext()) {
-            return fragmentEntity.getChildFragments().stream()
-                    .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption.next()))
-                    .collect(Collectors.toList());
-        }
-        return Collections.emptyList();
-    }
-
-    @Override
-    public void batchUpdateDataLeaves(final String dataspaceName, final String anchorName,
-                                        final Map<String, Map<String, Serializable>> updatedLeavesPerXPath) {
-        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
-
-        final Collection<String> xpathsOfUpdatedLeaves = updatedLeavesPerXPath.keySet();
-        final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpathsOfUpdatedLeaves);
-
-        for (final FragmentEntity fragmentEntity : fragmentEntities) {
-            final Map<String, Serializable> updatedLeaves = updatedLeavesPerXPath.get(fragmentEntity.getXpath());
-            final String mergedLeaves = mergeLeaves(updatedLeaves, fragmentEntity.getAttributes());
-            fragmentEntity.setAttributes(mergedLeaves);
-        }
-
-        try {
-            fragmentRepository.saveAll(fragmentEntities);
-        } catch (final StaleStateException staleStateException) {
-            retryUpdateDataNodesIndividually(anchorEntity, fragmentEntities);
-        }
-    }
-
-    @Override
-    public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
-                                              final Collection<DataNode> updatedDataNodes) {
-        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
-
-        final Map<String, DataNode> xpathToUpdatedDataNode = updatedDataNodes.stream()
-            .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode));
-
-        final Collection<String> xpaths = xpathToUpdatedDataNode.keySet();
-        Collection<FragmentEntity> existingFragmentEntities = getFragmentEntities(anchorEntity, xpaths);
-        existingFragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(
-                FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS, existingFragmentEntities);
-
-        for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) {
-            final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath());
-            updateFragmentEntityAndDescendantsWithDataNode(existingFragmentEntity, updatedDataNode);
-        }
-
-        try {
-            fragmentRepository.saveAll(existingFragmentEntities);
-        } catch (final StaleStateException staleStateException) {
-            retryUpdateDataNodesIndividually(anchorEntity, existingFragmentEntities);
-        }
-    }
-
-    private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity,
-                                                  final Collection<FragmentEntity> fragmentEntities) {
-        final Collection<String> failedXpaths = new HashSet<>();
-        for (final FragmentEntity dataNodeFragment : fragmentEntities) {
-            try {
-                fragmentRepository.save(dataNodeFragment);
-            } catch (final StaleStateException staleStateException) {
-                failedXpaths.add(dataNodeFragment.getXpath());
-            }
-        }
-        if (!failedXpaths.isEmpty()) {
-            final String failedXpathsConcatenated = String.join(",", failedXpaths);
-            throw new ConcurrencyException("Concurrent Transactions", String.format(
-                    "DataNodes : %s in Dataspace :'%s' with Anchor : '%s'  are updated by another transaction.",
-                    failedXpathsConcatenated, anchorEntity.getDataspace().getName(), anchorEntity.getName()));
-        }
+    private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) {
+        return FragmentEntity.builder()
+            .anchor(anchorEntity)
+            .xpath(dataNode.getXpath())
+            .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves()))
+            .build();
     }
 
-    private void updateFragmentEntityAndDescendantsWithDataNode(final FragmentEntity existingFragmentEntity,
-                                                                final DataNode newDataNode) {
-        copyAttributesFromNewDataNode(existingFragmentEntity, newDataNode);
-
-        final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream()
-                .collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity));
-
-        final Collection<FragmentEntity> updatedChildFragments = new HashSet<>();
-        for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) {
-            final FragmentEntity childFragment;
-            if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) {
-                childFragment = convertToFragmentWithAllDescendants(existingFragmentEntity.getAnchor(),
-                    newDataNodeChild);
-            } else {
-                childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath());
-                updateFragmentEntityAndDescendantsWithDataNode(childFragment, newDataNodeChild);
-            }
-            updatedChildFragments.add(childFragment);
-        }
 
-        existingFragmentEntity.getChildFragments().clear();
-        existingFragmentEntity.getChildFragments().addAll(updatedChildFragments);
-    }
 
     @Override
     @Transactional
@@ -636,6 +518,116 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         }
     }
 
+    @Override
+    @Timed(value = "cps.data.persistence.service.datanode.get",
+        description = "Time taken to get a data node")
+    public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
+                                             final String xpath,
+                                             final FetchDescendantsOption fetchDescendantsOption) {
+        final String targetXpath = getNormalizedXpath(xpath);
+        final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName,
+            Collections.singletonList(targetXpath), fetchDescendantsOption);
+        if (dataNodes.isEmpty()) {
+            throw new DataNodeNotFoundException(dataspaceName, anchorName, xpath);
+        }
+        return dataNodes;
+    }
+
+    @Override
+    @Timed(value = "cps.data.persistence.service.datanode.batch.get",
+        description = "Time taken to get data nodes")
+    public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName,
+                                                              final Collection<String> xpaths,
+                                                              final FetchDescendantsOption fetchDescendantsOption) {
+        final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+        Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpaths);
+        fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption,
+            fragmentEntities);
+        return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities);
+    }
+
+    private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
+                                             final FetchDescendantsOption fetchDescendantsOption) {
+        if (fetchDescendantsOption.hasNext()) {
+            return fragmentEntity.getChildFragments().stream()
+                .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption.next()))
+                .collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+
+    private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) {
+        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+        return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+    }
+
+    private List<Long> getAnchorIdsForPagination(final DataspaceEntity dataspaceEntity, final CpsPathQuery cpsPathQuery,
+                                                 final PaginationOption paginationOption) {
+        return fragmentRepository.findAnchorIdsForPagination(dataspaceEntity, cpsPathQuery, paginationOption);
+    }
+
+    private static String getNormalizedXpath(final String xpathSource) {
+        if (isRootXpath(xpathSource)) {
+            return xpathSource;
+        }
+        try {
+            return CpsPathUtil.getNormalizedXpath(xpathSource);
+        } catch (final PathParsingException pathParsingException) {
+            throw new CpsPathException(pathParsingException.getMessage());
+        }
+    }
+
+    private static Collection<String> getNormalizedXpaths(final Collection<String> xpaths) {
+        final Collection<String> normalizedXpaths = new HashSet<>(xpaths.size());
+        for (final String xpath : xpaths) {
+            try {
+                normalizedXpaths.add(getNormalizedXpath(xpath));
+            } catch (final CpsPathException cpsPathException) {
+                log.warn("Error parsing xpath \"{}\": {}", xpath, cpsPathException.getMessage());
+            }
+        }
+        return normalizedXpaths;
+    }
+
+    private FragmentEntity getFragmentEntity(final AnchorEntity anchorEntity, final String xpath) {
+        final FragmentEntity fragmentEntity;
+        if (isRootXpath(xpath)) {
+            fragmentEntity = fragmentRepository.findOneByAnchorId(anchorEntity.getId()).orElse(null);
+        } else {
+            fragmentEntity = fragmentRepository.getByAnchorAndXpath(anchorEntity, getNormalizedXpath(xpath));
+        }
+        if (fragmentEntity == null) {
+            throw new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath);
+        }
+        return fragmentEntity;
+    }
+
+    private Collection<FragmentEntity> getFragmentEntities(final AnchorEntity anchorEntity,
+                                                           final Collection<String> xpaths) {
+        final Collection<String> normalizedXpaths = getNormalizedXpaths(xpaths);
+
+        final boolean haveRootXpath = normalizedXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath);
+
+        final List<FragmentEntity> fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity,
+            normalizedXpaths);
+
+        for (final FragmentEntity fragmentEntity : fragmentEntities) {
+            normalizedXpaths.remove(fragmentEntity.getXpath());
+        }
+
+        for (final String xpath : normalizedXpaths) {
+            if (!CpsPathUtil.isPathToListElement(xpath)) {
+                fragmentEntities.addAll(fragmentRepository.findListByAnchorAndXpath(anchorEntity, xpath));
+            }
+        }
+
+        if (haveRootXpath) {
+            fragmentEntities.addAll(fragmentRepository.findRootsByAnchorId(anchorEntity.getId()));
+        }
+
+        return fragmentEntities;
+    }
+
     private static String getListElementXpathPrefix(final Collection<DataNode> newListElements) {
         if (newListElements.isEmpty()) {
             throw new CpsAdminException("Invalid list replacement",
@@ -660,20 +652,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         return existingListElementEntity;
     }
 
-    private static boolean isNewDataNode(final DataNode replacementDataNode,
-                                         final Map<String, FragmentEntity> existingListElementsByXpath) {
-        return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath());
-    }
-
-    private void copyAttributesFromNewDataNode(final FragmentEntity existingFragmentEntity,
-                                               final DataNode newDataNode) {
-        final String oldOrderedLeavesAsJson = getOrderedLeavesAsJson(existingFragmentEntity.getAttributes());
-        final String newOrderedLeavesAsJson = getOrderedLeavesAsJson(newDataNode.getLeaves());
-        if (!oldOrderedLeavesAsJson.equals(newOrderedLeavesAsJson)) {
-            existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves()));
-        }
-    }
-
     private String getOrderedLeavesAsJson(final Map<String, Serializable> currentLeaves) {
         final Map<String, Serializable> sortedLeaves = new TreeMap<>(String::compareTo);
         sortedLeaves.putAll(currentLeaves);
@@ -685,7 +663,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
             return "{}";
         }
         final Map<String, Serializable> sortedLeaves = jsonObjectMapper.convertJsonString(currentLeavesAsString,
-                TreeMap.class);
+            TreeMap.class);
         return jsonObjectMapper.asJsonString(sortedLeaves);
     }
 
@@ -696,10 +674,39 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
                 .collect(Collectors.toMap(FragmentEntity::getXpath, fragmentEntity -> fragmentEntity));
     }
 
+    private static Set<String> processAncestorXpath(final Collection<FragmentEntity> fragmentEntities,
+                                                    final CpsPathQuery cpsPathQuery) {
+        final Set<String> ancestorXpath = new HashSet<>();
+        final Pattern pattern =
+            Pattern.compile("(.*/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier())
+                + REG_EX_FOR_OPTIONAL_LIST_INDEX + "/.*");
+        for (final FragmentEntity fragmentEntity : fragmentEntities) {
+            final Matcher matcher = pattern.matcher(fragmentEntity.getXpath());
+            if (matcher.matches()) {
+                ancestorXpath.add(matcher.group(1));
+            }
+        }
+        return ancestorXpath;
+    }
+
     private static boolean isRootXpath(final String xpath) {
         return "/".equals(xpath) || "".equals(xpath);
     }
 
+    private static boolean isNewDataNode(final DataNode replacementDataNode,
+                                         final Map<String, FragmentEntity> existingListElementsByXpath) {
+        return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath());
+    }
+
+    private void copyAttributesFromNewDataNode(final FragmentEntity existingFragmentEntity,
+                                               final DataNode newDataNode) {
+        final String oldOrderedLeavesAsJson = getOrderedLeavesAsJson(existingFragmentEntity.getAttributes());
+        final String newOrderedLeavesAsJson = getOrderedLeavesAsJson(newDataNode.getLeaves());
+        if (!oldOrderedLeavesAsJson.equals(newOrderedLeavesAsJson)) {
+            existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves()));
+        }
+    }
+
     private String mergeLeaves(final Map<String, Serializable> updateLeaves, final String currentLeavesAsString) {
         Map<String, Serializable> currentLeavesAsMap = new HashMap<>();
         if (currentLeavesAsString != null) {
@@ -712,9 +719,4 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         }
         return jsonObjectMapper.asJsonString(currentLeavesAsMap);
     }
-
-    private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) {
-        final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
-        return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
-    }
 }