Use native query to delete data nodes 84/132984/5
authorsourabh_sourabh <sourabh.sourabh@est.tech>
Mon, 23 Jan 2023 12:51:15 +0000 (12:51 +0000)
committersourabh_sourabh <sourabh.sourabh@est.tech>
Mon, 23 Jan 2023 16:33:54 +0000 (16:33 +0000)
- Used hashcode to remove child fragment based on it's xpath
- Used native query to delete fragment by altering fragment_parent_id_fkey CONSTRAINT.

Issue-ID: CPS-1439

Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
Change-Id: If19c449818e18f8fd666503b7346704eeb4a95d0
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java
cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java [new file with mode: 0644]
cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java [new file with mode: 0644]
cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy

index 2fdfa05..2ffbb4a 100755 (executable)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * Copyright (C) 2020 Nordix Foundation.
+ * Copyright (C) 2020-2023 Nordix Foundation.
  * Modifications Copyright (C) 2021 Pantheon.tech
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,6 +40,7 @@ import javax.validation.constraints.NotNull;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
@@ -58,6 +59,7 @@ import org.hibernate.annotations.TypeDef;
 @Entity
 @Table(name = "fragment")
 @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
 public class FragmentEntity implements Serializable {
 
     private static final long serialVersionUID = 7737669789097119667L;
@@ -68,6 +70,7 @@ public class FragmentEntity implements Serializable {
 
     @NotNull
     @Column(columnDefinition = "text")
+    @EqualsAndHashCode.Include
     private String xpath;
 
     @Column(name = "parent_id")
index 06ee8ec..b85b30d 100644 (file)
@@ -61,6 +61,7 @@ import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.DataNodeBuilder;
 import org.onap.cps.spi.repository.AnchorRepository;
 import org.onap.cps.spi.repository.DataspaceRepository;
+import org.onap.cps.spi.repository.FragmentNativeRepository;
 import org.onap.cps.spi.repository.FragmentQueryBuilder;
 import org.onap.cps.spi.repository.FragmentRepository;
 import org.onap.cps.spi.utils.SessionManager;
@@ -78,6 +79,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     private final FragmentRepository fragmentRepository;
     private final JsonObjectMapper jsonObjectMapper;
     private final SessionManager sessionManager;
+    private final FragmentNativeRepository fragmentNativeRepositoryImpl;
 
     private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
 
@@ -645,7 +647,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
     private boolean deleteDataNode(final FragmentEntity parentFragmentEntity, final String targetXpath) {
         final String normalizedTargetXpath = CpsPathUtil.getNormalizedXpath(targetXpath);
         if (parentFragmentEntity.getXpath().equals(normalizedTargetXpath)) {
-            fragmentRepository.delete(parentFragmentEntity);
+            fragmentNativeRepositoryImpl.deleteFragmentEntity(parentFragmentEntity.getId());
             return true;
         }
         if (parentFragmentEntity.getChildFragments()
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java
new file mode 100644 (file)
index 0000000..4cfd79d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.spi.repository;
+
+/**
+ * This interface is used in delete fragment entity by id with child using native sql queries.
+ */
+public interface FragmentNativeRepository {
+    void deleteFragmentEntity(long fragmentEntityId);
+}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java
new file mode 100644 (file)
index 0000000..57dca56
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.spi.repository;
+
+import java.sql.PreparedStatement;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import org.hibernate.Session;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class FragmentNativeRepositoryImpl implements FragmentNativeRepository {
+
+    private static final String DROP_FRAGMENT_CONSTRAINT
+            = "ALTER TABLE fragment DROP CONSTRAINT fragment_parent_id_fkey;";
+    private static final String ADD_FRAGMENT_CONSTRAINT_WITH_CASCADE
+            = "ALTER TABLE fragment ADD CONSTRAINT fragment_parent_id_fkey FOREIGN KEY (parent_id) "
+            + "REFERENCES fragment (id) ON DELETE CASCADE;";
+    private static final String DELETE_FRAGMENT = "DELETE FROM fragment WHERE id =?;";
+    private static final String ADD_ORIGINAL_FRAGMENT_CONSTRAINT
+            = "ALTER TABLE fragment ADD CONSTRAINT fragment_parent_id_fkey FOREIGN KEY (parent_id) "
+            + "REFERENCES fragment (id) ON DELETE NO ACTION;";
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    @Override
+    public void deleteFragmentEntity(final long fragmentEntityId) {
+        final Session session = entityManager.unwrap(Session.class);
+        session.doWork(connection -> {
+            try (PreparedStatement preparedStatement = connection.prepareStatement(
+                    DROP_FRAGMENT_CONSTRAINT
+                            + ADD_FRAGMENT_CONSTRAINT_WITH_CASCADE
+                            + DELETE_FRAGMENT
+                            + DROP_FRAGMENT_CONSTRAINT
+                            + ADD_ORIGINAL_FRAGMENT_CONSTRAINT)) {
+                preparedStatement.setLong(1, fragmentEntityId);
+                preparedStatement.executeUpdate();
+            }
+        });
+    }
+}
+
index 87e59c6..5dab87e 100644 (file)
@@ -32,6 +32,7 @@ import org.onap.cps.spi.model.DataNode
 import org.onap.cps.spi.model.DataNodeBuilder
 import org.onap.cps.spi.repository.AnchorRepository
 import org.onap.cps.spi.repository.DataspaceRepository
+import org.onap.cps.spi.repository.FragmentNativeRepository
 import org.onap.cps.spi.repository.FragmentRepository
 import org.onap.cps.spi.utils.SessionManager
 import org.onap.cps.utils.JsonObjectMapper
@@ -45,8 +46,10 @@ class CpsDataPersistenceServiceSpec extends Specification {
     def mockFragmentRepository = Mock(FragmentRepository)
     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
     def mockSessionManager = Mock(SessionManager)
+    def stubFragmentNativeRepository = Stub(FragmentNativeRepository)
 
-    def objectUnderTest = Spy(new CpsDataPersistenceServiceImpl(mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper, mockSessionManager))
+    def objectUnderTest = Spy(new CpsDataPersistenceServiceImpl(mockDataspaceRepository, mockAnchorRepository,
+            mockFragmentRepository, jsonObjectMapper, mockSessionManager, stubFragmentNativeRepository))
 
     def 'Storing data nodes individually when batch operation fails'(){
         given: 'two data nodes and supporting repository mock behavior'
index 5aae285..4dd4823 100644 (file)
@@ -61,8 +61,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
             }
             stopWatch.stop()
             def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
-        then: 'delete duration is under 6000 milliseconds'
-            assert deleteDurationInMillis < 6000
+        then: 'delete duration is under 300 milliseconds'
+            assert deleteDurationInMillis < 300
     }
 
     def 'Delete 50 grandchildren (that have no descendants)'() {
@@ -74,8 +74,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
             }
             stopWatch.stop()
             def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
-        then: 'delete duration is under 500 milliseconds'
-            assert deleteDurationInMillis < 500
+        then: 'delete duration is under 350 milliseconds'
+            assert deleteDurationInMillis < 350
     }
 
     def 'Delete 1 large data node with many descendants'() {
@@ -84,8 +84,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
             objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, PERF_TEST_PARENT)
             stopWatch.stop()
             def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
-        then: 'delete duration is under 2500 milliseconds'
-            assert deleteDurationInMillis < 2500
+        then: 'delete duration is under 250 milliseconds'
+            assert deleteDurationInMillis < 250
     }
 
     @Sql([CLEAR_DATA, PERF_TEST_DATA])
@@ -108,8 +108,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
             }
             stopWatch.stop()
             def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
-        then: 'delete duration is under 4000 milliseconds'
-            assert deleteDurationInMillis < 4000
+        then: 'delete duration is under 1000 milliseconds'
+            assert deleteDurationInMillis < 1000
     }
 
     def 'Delete 10 list elements with keys'() {
@@ -122,8 +122,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
             }
             stopWatch.stop()
             def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
-        then: 'delete duration is under 6000 milliseconds'
-            assert deleteDurationInMillis < 6000
+        then: 'delete duration is under 1200 milliseconds'
+            assert deleteDurationInMillis < 1200
     }
 
     @Sql([CLEAR_DATA, PERF_TEST_DATA])