diff --git a/.config/pmd/ruleset.xml b/.config/pmd/ruleset.xml new file mode 100644 index 00000000..5f881567 --- /dev/null +++ b/.config/pmd/ruleset.xml @@ -0,0 +1,152 @@ + + + + + This ruleset checks the code for discouraged programming constructs. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 68299d5d..041c89bc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -14,9 +14,9 @@ body: attributes: label: "Checklist" options: - - label: "I am able to reproduce the bug with the [latest version](https://github.com/xdev-software/template-placeholder/releases/latest)" + - label: "I am able to reproduce the bug with the [latest version](https://github.com/xdev-software/spring-data-eclipse-store/releases/latest)" required: true - - label: "I made sure that there are *no existing issues* - [open](https://github.com/xdev-software/template-placeholder/issues) or [closed](https://github.com/xdev-software/template-placeholder/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to." + - label: "I made sure that there are *no existing issues* - [open](https://github.com/xdev-software/spring-data-eclipse-store/issues) or [closed](https://github.com/xdev-software/spring-data-eclipse-store/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to." required: true - label: "I have taken the time to fill in all the required details. I understand that the bug report will be dismissed otherwise." required: true diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index 75231293..4ed79471 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -12,7 +12,7 @@ body: attributes: label: "Checklist" options: - - label: "I made sure that there are *no existing issues* - [open](https://github.com/xdev-software/template-placeholder/issues) or [closed](https://github.com/xdev-software/template-placeholder/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to." + - label: "I made sure that there are *no existing issues* - [open](https://github.com/xdev-software/spring-data-eclipse-store/issues) or [closed](https://github.com/xdev-software/spring-data-eclipse-store/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to." required: true - label: "I have taken the time to fill in all the required details. I understand that the feature request will be dismissed otherwise." required: true diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml index 6ecd6ad5..05f66d4b 100644 --- a/.github/ISSUE_TEMPLATE/question.yml +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -12,7 +12,7 @@ body: attributes: label: "Checklist" options: - - label: "I made sure that there are *no existing issues* - [open](https://github.com/xdev-software/template-placeholder/issues) or [closed](https://github.com/xdev-software/template-placeholder/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to." + - label: "I made sure that there are *no existing issues* - [open](https://github.com/xdev-software/spring-data-eclipse-store/issues) or [closed](https://github.com/xdev-software/spring-data-eclipse-store/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to." required: true - label: "I have taken the time to fill in all the required details. I understand that the question will be dismissed otherwise." required: true diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index a1fc53b5..24130cd2 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -70,7 +70,7 @@ jobs: path: ${{ env.DEMO_MAVEN_MODULE }}/target/${{ env.DEMO_MAVEN_MODULE }}.jar if-no-files-found: error - code-style: + checkstyle: runs-on: ubuntu-latest if: ${{ github.event_name != 'pull_request' || !startsWith(github.head_ref, 'renovate/') }} @@ -92,6 +92,43 @@ jobs: - name: Run Checkstyle run: ./mvnw -B checkstyle:check -P checkstyle -T2C + pmd: + runs-on: ubuntu-latest + if: ${{ github.event_name != 'pull_request' || !startsWith(github.head_ref, 'renovate/') }} + + strategy: + matrix: + java: [17] + distribution: [temurin] + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: ${{ matrix.distribution }} + java-version: ${{ matrix.java }} + cache: 'maven' + + - name: Run PMD + run: ./mvnw -B test pmd:aggregate-pmd-no-fork pmd:check -P pmd -DskipTests -T2C + + - name: Run CPD (Copy Paste Detector) + run: ./mvnw -B pmd:aggregate-cpd pmd:cpd-check -P pmd -DskipTests -T2C + + - name: Upload report + if: always() + uses: actions/upload-artifact@v4 + with: + name: pmd-report + if-no-files-found: ignore + path: | + target/site/*.html + target/site/css/** + target/site/images/logos/maven-feather.png + target/site/images/external.png + docs: runs-on: ubuntu-latest @@ -107,4 +144,4 @@ jobs: run: npm i antora @antora/lunr-extension - name: Generate Site - run: npx antora docs/antora-playbook.yml \ No newline at end of file + run: npx antora docs/antora-playbook.yml diff --git a/.github/workflows/update-from-template.yml b/.github/workflows/update-from-template.yml index 33ac9b94..aa33de40 100644 --- a/.github/workflows/update-from-template.yml +++ b/.github/workflows/update-from-template.yml @@ -211,6 +211,8 @@ jobs: echo "Checking if update-branch-merged exists" git fetch if [[ $(git rev-parse origin/${{ env.UPDATE_BRANCH_MERGED }}) ]]; then + echo "Branch still exists; Continuing..." + else echo "Branch origin/${{ env.UPDATE_BRANCH_MERGED }} is missing" exit 0 fi @@ -274,6 +276,8 @@ jobs: echo "Fetching..." git fetch if [[ $(git rev-parse origin/${{ env.UPDATE_BRANCH_MERGED }}) ]]; then + echo "Branch still exists; Continuing..." + else echo "Branch origin/${{ env.UPDATE_BRANCH_MERGED }} is missing" exit 0 fi diff --git a/.gitignore b/.gitignore index fb4acbe5..9ed140af 100644 --- a/.gitignore +++ b/.gitignore @@ -39,11 +39,6 @@ buildNumber.properties # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* - -# bin / compiled stuff -target/ - - # JRebel **/resources/rebel.xml **/resources/rebel-remote.xml diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index f800e78d..e56bc180 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,4 +14,4 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index a3569d9d..377a3011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.0.8 + +* Entities with same ID are replaced on saved and not added +* Updated Spring to version 3.3.1 + # 1.0.7 * QueryByExample now possible diff --git a/README.md b/README.md index 675d9cb7..d1654ce1 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,8 @@ instructions** are in the documentation](https://xdev-software.github.io/spring- |---------------------------|--------|-------------|--------------| | ``<= 1.0.2`` | ``17`` | ``3.2.2`` | ``1.1.0`` | | ``1.0.3/1.0.4`` | ``17`` | ``3.2.3`` | ``1.2.0`` | -| ``>= 1.0.5`` | ``17`` | ``3.2.5`` | ``1.3.2`` | +| ``1.0.5-1.0.7`` | ``17`` | ``3.2.5`` | ``1.3.2`` | +| ``>= 1.0.8`` | ``17`` | ``3.3.1`` | ``1.3.2`` | ## Demo @@ -82,4 +83,5 @@ use [our support](https://xdev.software/en/services/support). See the [contributing guide](./CONTRIBUTING.md) for detailed instructions on how to get started with our project. ## Dependencies and Licenses -View the [license of the current project](LICENSE) or the [summary including all dependencies](https://xdev-software.github.io/spring-data-eclipse-store/dependencies) + +View the [license of the current project](LICENSE). diff --git a/docs/package.json b/docs/package.json index 86a7578d..8b030e18 100644 --- a/docs/package.json +++ b/docs/package.json @@ -3,7 +3,7 @@ "@antora/lunr-extension": "^1.0.0-alpha.8" }, "devDependencies": { - "@antora/cli": "3.1.7", - "@antora/site-generator": "3.1.7" + "@antora/cli": "3.1.8", + "@antora/site-generator": "3.1.8" } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index a2b492b2..c3d84890 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,9 @@ 17 ${javaVersion} + + UTF-8 + UTF-8 @@ -36,7 +39,6 @@ - checkstyle @@ -44,12 +46,74 @@ org.apache.maven.plugins maven-checkstyle-plugin 3.4.0 + + + com.puppycrawl.tools + checkstyle + 10.17.0 + + + + .config/checkstyle/checkstyle.xml + true + + + + + check + + + + + + + + + pmd + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.23.0 - true + true + true + + .config/pmd/ruleset.xml + + + **/benchmark/**/jmh_generated/** + + **/shared/**/Customer* + **/shared/**/Child* + + + + net.sourceforge.pmd + pmd-core + 7.2.0 + + + net.sourceforge.pmd + pmd-java + 7.2.0 + + + + + + + org.apache.maven.plugins + maven-jxr-plugin + 3.4.0 + + + diff --git a/renovate.json5 b/renovate.json5 index 11a77b2a..0c1deaad 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -1,4 +1,14 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "rebaseWhen": "behind-base-branch" + "rebaseWhen": "behind-base-branch", + "packageRules": [ + { + "description": "Ignore project internal dependencies", + "packagePattern": "^software.xdev:spring-data-eclipse-store", + "datasources": [ + "maven" + ], + "enabled": false + } + ] } diff --git a/spring-data-eclipse-store-benchmark/pom.xml b/spring-data-eclipse-store-benchmark/pom.xml index 911fd26b..1ab40c77 100644 --- a/spring-data-eclipse-store-benchmark/pom.xml +++ b/spring-data-eclipse-store-benchmark/pom.xml @@ -1,117 +1,91 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - com.xdev-software - spring-data-eclipse-store-benchmark - 1.0.8-SNAPSHOT - jar + + software.xdev + spring-data-eclipse-store-root + 1.0.8-SNAPSHOT + - 2023 + spring-data-eclipse-store-benchmark + 1.0.8-SNAPSHOT + jar - - XDEV Software - https://xdev.software - + 2023 - - 17 - ${javaVersion} - UTF-8 - UTF-8 + + XDEV Software + https://xdev.software + - 3.2.5 - 1.37 - + + 17 + ${javaVersion} + UTF-8 + UTF-8 - - - - org.springframework.boot - spring-boot-dependencies - ${org.springframework.boot.version} - pom - import - - - + 3.3.1 + 1.37 + - - - software.xdev - spring-data-eclipse-store - ${project.version} - + + + + org.springframework.boot + spring-boot-dependencies + ${org.springframework.boot.version} + pom + import + + + - - org.springframework.boot - spring-boot - - - org.springframework.boot - spring-boot-starter - + + + software.xdev + spring-data-eclipse-store + ${project.version} + - - org.openjdk.jmh - jmh-core - ${jmh.version} - - - org.openjdk.jmh - jmh-generator-annprocess - ${jmh.version} - - + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-starter + - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - ${maven.compiler.release} - - - org.openjdk.jmh - jmh-generator-annprocess - ${jmh.version} - - - - - - - - - checkstyle - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.4.0 - - - com.puppycrawl.tools - checkstyle - 10.17.0 - - - - ../.config/checkstyle/checkstyle.xml - - - - - check - - - - - - - - + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${maven.compiler.release} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + + + + diff --git a/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/AbstractStoringSimpleCustomerBenchmark.java b/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/AbstractStoringSimpleCustomerBenchmark.java new file mode 100644 index 00000000..0931bd64 --- /dev/null +++ b/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/AbstractStoringSimpleCustomerBenchmark.java @@ -0,0 +1,50 @@ +package software.xdev.spring.data.eclipse.store.benchmark.benchmarks.simple.customer; + +import org.openjdk.jmh.annotations.Benchmark; + +import software.xdev.spring.data.eclipse.store.benchmark.SpringState; + + +@SuppressWarnings("checkstyle:MagicNumber") +public abstract class AbstractStoringSimpleCustomerBenchmark +{ + protected abstract void saveCustomerInForEach(final SpringState state, final int entityCount); + + protected abstract void saveCustomerInSaveAll(final SpringState state, final int entityCount); + + @Benchmark + public void save100CustomerInForEach(final SpringState state) + { + this.saveCustomerInForEach(state, 100); + } + + @Benchmark + public void save1000CustomerInForEach(final SpringState state) + { + this.saveCustomerInForEach(state, 1_000); + } + + @Benchmark + public void save100CustomerInSaveAll(final SpringState state) + { + this.saveCustomerInSaveAll(state, 100); + } + + @Benchmark + public void save1000CustomerInSaveAll(final SpringState state) + { + this.saveCustomerInSaveAll(state, 1_000); + } + + @Benchmark + public void save10000CustomerInSaveAll(final SpringState state) + { + this.saveCustomerInSaveAll(state, 10_000); + } + + @Benchmark + public void save100000CustomerInSaveAll(final SpringState state) + { + this.saveCustomerInSaveAll(state, 100_000); + } +} diff --git a/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringAndChangingSimpleCustomerBenchmark.java b/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringAndChangingSimpleCustomerBenchmark.java index aea67d84..83f63914 100644 --- a/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringAndChangingSimpleCustomerBenchmark.java +++ b/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringAndChangingSimpleCustomerBenchmark.java @@ -8,8 +8,7 @@ import software.xdev.spring.data.eclipse.store.repository.config.EclipseStoreClientConfiguration; -@SuppressWarnings("checkstyle:MagicNumber") -public class StoringAndChangingSimpleCustomerBenchmark +public class StoringAndChangingSimpleCustomerBenchmark extends AbstractStoringSimpleCustomerBenchmark { @Benchmark public void saveSingleCustomer(final SpringState state) @@ -29,43 +28,8 @@ public void saveSingleCustomer(final SpringState state) }); } - @Benchmark - public void save100CustomerInForEach(final SpringState state) - { - this.saveCustomerInForEach(state, 100); - } - - @Benchmark - public void save100CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 100); - } - - @Benchmark - public void save1000CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 1_000); - } - - @Benchmark - public void save10000CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 10_000); - } - - @Benchmark - public void save1000CustomerInForEach(final SpringState state) - { - this.saveCustomerInForEach(state, 1_000); - } - - @Benchmark - public void save100000CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 100_000); - } - - private void saveCustomerInForEach(final SpringState state, final int entityCount) + @Override + protected void saveCustomerInForEach(final SpringState state, final int entityCount) { final CustomerRepository customerRepository1 = state.getBean(CustomerRepository.class); IntStream.range(0, entityCount).forEach( @@ -84,7 +48,8 @@ private void saveCustomerInForEach(final SpringState state, final int entityCoun }); } - private void saveCustomerInSaveAll(final SpringState state, final int entityCount) + @Override + protected void saveCustomerInSaveAll(final SpringState state, final int entityCount) { final CustomerRepository customerRepository = state.getBean(CustomerRepository.class); customerRepository.saveAll( diff --git a/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringSimpleCustomerBenchmark.java b/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringSimpleCustomerBenchmark.java index cd14c20f..bf86ccae 100644 --- a/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringSimpleCustomerBenchmark.java +++ b/spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/simple/customer/StoringSimpleCustomerBenchmark.java @@ -8,7 +8,7 @@ @SuppressWarnings("checkstyle:MagicNumber") -public class StoringSimpleCustomerBenchmark +public class StoringSimpleCustomerBenchmark extends AbstractStoringSimpleCustomerBenchmark { @Benchmark public void saveSingleCustomer(final SpringState state) @@ -17,36 +17,6 @@ public void saveSingleCustomer(final SpringState state) customerRepository.save(new Customer("Test", "Test")); } - @Benchmark - public void save100CustomerInForEach(final SpringState state) - { - this.saveCustomerInForEach(state, 100); - } - - @Benchmark - public void save100CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 100); - } - - @Benchmark - public void save1000CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 1_000); - } - - @Benchmark - public void save10000CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 10_000); - } - - @Benchmark - public void save1000CustomerInForEach(final SpringState state) - { - this.saveCustomerInForEach(state, 1_000); - } - @Benchmark public void save10000CustomerInForEach(final SpringState state) { @@ -59,13 +29,8 @@ public void save10000CustomerInForEachParallel(final SpringState state) this.saveCustomerInForEachParallel(state, 10_000); } - @Benchmark - public void save100000CustomerInSaveAll(final SpringState state) - { - this.saveCustomerInSaveAll(state, 100_000); - } - - private void saveCustomerInForEach(final SpringState state, final int entityCount) + @Override + protected void saveCustomerInForEach(final SpringState state, final int entityCount) { final CustomerRepository customerRepository = state.getBean(CustomerRepository.class); IntStream.range(0, entityCount).forEach( @@ -81,7 +46,8 @@ private void saveCustomerInForEachParallel(final SpringState state, final int en ); } - private void saveCustomerInSaveAll(final SpringState state, final int entityCount) + @Override + protected void saveCustomerInSaveAll(final SpringState state, final int entityCount) { final CustomerRepository customerRepository = state.getBean(CustomerRepository.class); customerRepository.saveAll( diff --git a/spring-data-eclipse-store-demo/pom.xml b/spring-data-eclipse-store-demo/pom.xml index 372f2fcc..ee3542ee 100644 --- a/spring-data-eclipse-store-demo/pom.xml +++ b/spring-data-eclipse-store-demo/pom.xml @@ -4,7 +4,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - software.xdev + + software.xdev + spring-data-eclipse-store-root + 1.0.8-SNAPSHOT + + spring-data-eclipse-store-demo 1.0.8-SNAPSHOT jar @@ -23,7 +28,7 @@ software.xdev.spring.data.eclipse.store.demo.complex.ComplexDemoApplication - 3.2.5 + 3.3.1 @@ -88,36 +93,4 @@ - - - checkstyle - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.4.0 - - - com.puppycrawl.tools - checkstyle - 10.17.0 - - - - ../.config/checkstyle/checkstyle.xml - true - - - - - check - - - - - - - - diff --git a/spring-data-eclipse-store-jpa/pom.xml b/spring-data-eclipse-store-jpa/pom.xml index b1ac0322..404a5462 100644 --- a/spring-data-eclipse-store-jpa/pom.xml +++ b/spring-data-eclipse-store-jpa/pom.xml @@ -4,7 +4,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - software.xdev + + software.xdev + spring-data-eclipse-store-root + 1.0.8-SNAPSHOT + + spring-data-eclipse-store-jpa 1.0.8-SNAPSHOT jar @@ -25,7 +30,7 @@ software.xdev.spring.data.eclipse.store.demo.complex.ComplexDemoApplication - 3.2.5 + 3.3.1 @@ -110,35 +115,4 @@ - - - checkstyle - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.4.0 - - - com.puppycrawl.tools - checkstyle - 10.17.0 - - - - ../.config/checkstyle/checkstyle.xml - - - - - check - - - - - - - - diff --git a/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportExplicitTest.java b/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportExplicitTest.java index daaaf988..70e452fb 100644 --- a/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportExplicitTest.java +++ b/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportExplicitTest.java @@ -49,7 +49,7 @@ class JpaImportExplicitTest private EclipseStoreClientConfiguration configuration; @Test - void testEclipseStoreImport_ExplicitNoComponent() + void testEclipseStoreImportExplicitNoComponent() { final PersonToTestInJpa customer = new PersonToTestInJpa("", ""); this.personToTestInJpaRepository.save(customer); diff --git a/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportTest.java b/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportTest.java index aebba495..b1a3bf03 100644 --- a/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportTest.java +++ b/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/JpaImportTest.java @@ -49,7 +49,7 @@ class JpaImportTest @Test void testBasicSaveAndFindSingleRecords() { - final PersonToTestInEclipseStore customer = new PersonToTestInEclipseStore("", ""); + final PersonToTestInEclipseStore customer = new PersonToTestInEclipseStore("1", "", ""); this.personToTestInEclipseStoreRepository.save(customer); final List customers = this.personToTestInEclipseStoreRepository.findAll(); diff --git a/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/repository/PersonToTestInEclipseStore.java b/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/repository/PersonToTestInEclipseStore.java index f57f21e2..ff0b17f2 100644 --- a/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/repository/PersonToTestInEclipseStore.java +++ b/spring-data-eclipse-store-jpa/src/test/java/software/xdev/spring/data/eclipse/store/jpa/integration/repository/PersonToTestInEclipseStore.java @@ -8,13 +8,14 @@ public class PersonToTestInEclipseStore { @Id - private String id; + private final String id; private final String firstName; private final String lastName; - public PersonToTestInEclipseStore(final String firstName, final String lastName) + public PersonToTestInEclipseStore(final String id, final String firstName, final String lastName) { + this.id = id; this.firstName = firstName; this.lastName = lastName; } diff --git a/spring-data-eclipse-store/pom.xml b/spring-data-eclipse-store/pom.xml index 8ebdb428..078c591f 100644 --- a/spring-data-eclipse-store/pom.xml +++ b/spring-data-eclipse-store/pom.xml @@ -50,7 +50,7 @@ UTF-8 - 3.2.5 + 3.3.1 1.3.2 1.3.2 @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.5.0 + 3.6.1 @@ -269,7 +269,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.3.0 --add-opens java.base/java.util=ALL-UNNAMED @@ -389,5 +389,51 @@ + + pmd + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.23.0 + + true + true + + ../.config/pmd/ruleset.xml + + + **/benchmark/**/jmh_generated/** + + **/store/integration/** + + + + + net.sourceforge.pmd + pmd-core + 7.2.0 + + + net.sourceforge.pmd + pmd-java + 7.2.0 + + + + + + + + + + org.apache.maven.plugins + maven-jxr-plugin + 3.4.0 + + + + diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/NotComparableException.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/NotComparableException.java index b5fe9aa1..26611997 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/NotComparableException.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/exceptions/NotComparableException.java @@ -21,4 +21,9 @@ public NotComparableException(final String message) { super(message); } + + public NotComparableException(final String message, final Throwable cause) + { + super(message, cause); + } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/importer/EclipseStoreDataImporter.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/importer/EclipseStoreDataImporter.java index 91134d7c..44322910 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/importer/EclipseStoreDataImporter.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/importer/EclipseStoreDataImporter.java @@ -34,6 +34,7 @@ import software.xdev.spring.data.eclipse.store.repository.SupportedChecker; import software.xdev.spring.data.eclipse.store.repository.config.EclipseStoreClientConfiguration; import software.xdev.spring.data.eclipse.store.repository.support.SimpleEclipseStoreRepository; +import software.xdev.spring.data.eclipse.store.repository.support.copier.id.IdManager; import software.xdev.spring.data.eclipse.store.repository.support.copier.working.RecursiveWorkingCopier; import software.xdev.spring.data.eclipse.store.transactions.EclipseStoreTransactionManager; @@ -254,6 +255,7 @@ private void createRepositoryForType( private SimpleEclipseStoreRepository createEclipseStoreRepo(final Class domainClass) { final EclipseStoreStorage storageInstance = this.configuration.getStorageInstance(); + final IdManager idManager = storageInstance.ensureIdManager(domainClass); return new SimpleEclipseStoreRepository<>( storageInstance, new RecursiveWorkingCopier<>( @@ -265,7 +267,8 @@ private void createRepositoryForType( storageInstance ), domainClass, - new EclipseStoreTransactionManager() + new EclipseStoreTransactionManager(), + idManager ); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/EclipseStoreStorage.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/EclipseStoreStorage.java index 952dd0b1..f05422f6 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/EclipseStoreStorage.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/EclipseStoreStorage.java @@ -38,17 +38,18 @@ import software.xdev.spring.data.eclipse.store.repository.support.SimpleEclipseStoreRepository; import software.xdev.spring.data.eclipse.store.repository.support.concurrency.ReadWriteLock; import software.xdev.spring.data.eclipse.store.repository.support.concurrency.ReentrantJavaReadWriteLock; +import software.xdev.spring.data.eclipse.store.repository.support.copier.id.IdManager; import software.xdev.spring.data.eclipse.store.repository.support.copier.id.IdSetter; import software.xdev.spring.data.eclipse.store.repository.support.reposyncer.RepositorySynchronizer; import software.xdev.spring.data.eclipse.store.repository.support.reposyncer.SimpleRepositorySynchronizer; public class EclipseStoreStorage - implements EntityListProvider, IdSetterProvider, PersistableChecker, ObjectSwizzling + implements EntityListProvider, IdManagerProvider, PersistableChecker, ObjectSwizzling { private static final Logger LOG = LoggerFactory.getLogger(EclipseStoreStorage.class); private final Map, SimpleEclipseStoreRepository> entityClassToRepository = new HashMap<>(); - private final Map, IdSetter> idSetters = new ConcurrentHashMap<>(); + private final Map, IdManager> idManagers = new ConcurrentHashMap<>(); private final EclipseStoreStorageFoundationProvider foundationProvider; private EntitySetCollector entitySetCollector; private PersistableChecker persistenceChecker; @@ -317,7 +318,7 @@ public synchronized void stop() this.storageManager = null; this.root = null; this.registry.reset(); - this.idSetters.clear(); + this.idManagers.clear(); LOG.info("Stopped storage."); } else @@ -330,16 +331,20 @@ public synchronized void stop() @Override @SuppressWarnings("unchecked") - public IdSetter ensureIdSetter(final Class domainClass) + public IdManager ensureIdManager(final Class domainClass) { this.ensureEntitiesInRoot(); - return (IdSetter)this.idSetters.computeIfAbsent( + return (IdManager)this.idManagers.computeIfAbsent( domainClass, clazz -> - IdSetter.createIdSetter( - clazz, - id -> this.setLastId(clazz, id), - () -> this.getLastId(clazz) + new IdManager<>( + domainClass, + (IdSetter)IdSetter.createIdSetter( + clazz, + id -> this.setLastId(clazz, id), + () -> this.getLastId(clazz) + ), + this ) ); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/IdSetterProvider.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/IdManagerProvider.java similarity index 85% rename from spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/IdSetterProvider.java rename to spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/IdManagerProvider.java index 08b9536c..dbe88f61 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/IdSetterProvider.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/IdManagerProvider.java @@ -15,10 +15,10 @@ */ package software.xdev.spring.data.eclipse.store.repository; -import software.xdev.spring.data.eclipse.store.repository.support.copier.id.IdSetter; +import software.xdev.spring.data.eclipse.store.repository.support.copier.id.IdManager; -public interface IdSetterProvider +public interface IdManagerProvider { - IdSetter ensureIdSetter(final Class domainClass); + IdManager ensureIdManager(final Class domainClass); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/lazy/SpringDataEclipseStoreLazy.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/lazy/SpringDataEclipseStoreLazy.java index 2d745474..2d29d410 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/lazy/SpringDataEclipseStoreLazy.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/lazy/SpringDataEclipseStoreLazy.java @@ -64,8 +64,8 @@ static SpringDataEclipseStoreLazy.Default buildWithLazy(final Lazy laz * * @param the type of the lazily referenced element */ - @SuppressWarnings({"java:S2065", "checkstyle:FinalClass"}) - class Default implements SpringDataEclipseStoreLazy + @SuppressWarnings({"java:S2065"}) + final class Default implements SpringDataEclipseStoreLazy { private T objectToBeWrapped; private Lazy wrappedLazy; diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java index 341afd13..8f1ade91 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/EclipseStoreQueryCreator.java @@ -118,6 +118,7 @@ protected QueryExecutor complete(final AbstractCriteriaNode criteria, @Non return QueryExecutorCreator.createQuery(this.typeInformation, this.copier, criteria, sort); } + @SuppressWarnings("PMD.CyclomaticComplexity") private AbstractCriteriaNode from( final Part part, final AbstractCriteriaNode criteria, diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java index e6b4d988..5e45f834 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/criteria/CriteriaByExample.java @@ -19,7 +19,9 @@ import java.util.Collection; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.function.BiPredicate; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -107,78 +109,60 @@ private Predicate createPredicateForSpecif ? example.getMatcher().getDefaultStringMatcher() : specifier.getStringMatcher(); - switch(setOrDefaultMatcher) + return this.createPredicateForStringMatcher( + specifier, + setOrDefaultMatcher, + transformedExampledValue.get(), + transformedValue.orElse(null)); + }; + } + + private boolean createPredicateForStringMatcher( + final ExampleMatcher.PropertySpecifier specifier, + final ExampleMatcher.StringMatcher setOrDefaultMatcher, + final Object transformedExampledValue, // Never null + final Object transformedValue) // Nullable + { + // Check exact matches + if(ExampleMatcher.StringMatcher.DEFAULT.equals(setOrDefaultMatcher) + || ExampleMatcher.StringMatcher.EXACT.equals(setOrDefaultMatcher)) + { + if(transformedExampledValue instanceof String) { - case DEFAULT, EXACT -> - { - if(transformedExampledValue.get() instanceof String) - { - return this.valueToString(transformedValue, specifier).equals(this.valueToString( - transformedExampledValue, - specifier)); - } - return transformedExampledValue.equals(transformedValue); - } - case STARTING -> - { - final Optional valueAsString = this.valueToString(transformedValue, specifier); - if(valueAsString.isEmpty()) - { - return false; - } - return valueAsString.get() - .startsWith(this.valueToString(transformedExampledValue, specifier).get()); - } - case ENDING -> - { - final Optional valueAsString = this.valueToString(transformedValue, specifier); - if(valueAsString.isEmpty()) - { - return false; - } - return valueAsString.get().endsWith(this.valueToString(transformedExampledValue, specifier).get()); - } - case CONTAINING -> - { - final Optional valueAsString = this.valueToString(transformedValue, specifier); - if(valueAsString.isEmpty()) - { - return false; - } - return valueAsString.get().contains(this.valueToString(transformedExampledValue, specifier).get()); - } - case REGEX -> - { - final Optional valueAsString = this.valueToString(transformedValue, specifier); - if(valueAsString.isEmpty()) - { - return false; - } - return Pattern.compile( - this.valueToString(transformedExampledValue, specifier).get() - ) - .matcher(valueAsString.get()).find(); - } - default -> - { - return false; - } + return Objects.equals( + this.valueToString(transformedExampledValue, specifier), + this.valueToString(transformedValue, specifier)); } + return Objects.equals(transformedExampledValue, transformedValue); + } + + // Check comparisons + final BiPredicate compareFunc = switch(setOrDefaultMatcher) + { + case STARTING -> String::startsWith; + case ENDING -> String::endsWith; + case CONTAINING -> String::contains; + case REGEX -> (v, example) -> Pattern.compile(example).matcher(v).find(); + default -> null; }; + + return compareFunc != null + && Optional.ofNullable(this.valueToString(transformedValue, specifier)) + .map(v -> compareFunc.test(v, this.valueToString(transformedExampledValue, specifier))) + .orElse(false); } - private Optional valueToString( - final Optional value, + private String valueToString( + final Object value, final ExampleMatcher.PropertySpecifier specifier) { - if(value.isEmpty()) + if(value == null) { - return Optional.empty(); + return null; } - if(specifier != null && Boolean.TRUE.equals(specifier.getIgnoreCase())) - { - return Optional.of(value.get().toString().toLowerCase(Locale.ROOT)); - } - return Optional.of(value.get().toString()); + + return (specifier != null && Boolean.TRUE.equals(specifier.getIgnoreCase())) + ? value.toString().toLowerCase(Locale.ROOT) + : value.toString(); } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/executors/EntitySorter.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/executors/EntitySorter.java index 48d7e5d9..82968624 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/executors/EntitySorter.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/query/executors/EntitySorter.java @@ -48,9 +48,8 @@ public static Stream sortEntitiesStream(final Class clazz, final Sort } catch(final NoSuchFieldException e) { - throw new NotComparableException(String.format( - "Could not sort entities by property %s", - order.getProperty())); + throw new NotComparableException( + "Could not sort entities by property " + order.getProperty(), e); } } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactory.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactory.java index ec26a18a..ee5b0996 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactory.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactory.java @@ -100,7 +100,8 @@ protected Object getTargetRepository(@Nonnull final RepositoryInformation metada this.storage, this.createWorkingCopier(metadata.getDomainType(), this.storage), metadata.getDomainType(), - this.transactionManager + this.transactionManager, + this.storage.ensureIdManager(metadata.getDomainType()) ); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactoryBean.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactoryBean.java index ecec7407..e98bc944 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactoryBean.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/EclipseStoreRepositoryFactoryBean.java @@ -67,7 +67,7 @@ public void setConfigurationClass(final Class configurationClass) } @Override - public void setBeanFactory(final BeanFactory beanFactory) throws BeansException + public void setBeanFactory(final BeanFactory beanFactory) { super.setBeanFactory(beanFactory); this.beanFactory = beanFactory; diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/SimpleEclipseStoreRepository.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/SimpleEclipseStoreRepository.java index 7e30fc91..799efe90 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/SimpleEclipseStoreRepository.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/SimpleEclipseStoreRepository.java @@ -15,7 +15,6 @@ */ package software.xdev.spring.data.eclipse.store.repository.support; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -23,6 +22,7 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import jakarta.annotation.Nonnull; @@ -34,10 +34,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.FluentQuery; -import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException; -import software.xdev.spring.data.eclipse.store.exceptions.NoIdFieldFoundException; import software.xdev.spring.data.eclipse.store.repository.EclipseStoreStorage; -import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier; import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreCrudRepository; import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreListCrudRepository; import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreListPagingAndSortingRepositoryRepository; @@ -52,6 +49,7 @@ import software.xdev.spring.data.eclipse.store.repository.query.executors.ListQueryExecutor; import software.xdev.spring.data.eclipse.store.repository.query.executors.PageableQueryExecutor; import software.xdev.spring.data.eclipse.store.repository.query.executors.SingleOptionalQueryExecutor; +import software.xdev.spring.data.eclipse.store.repository.support.copier.id.IdManager; import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopier; import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopierResult; import software.xdev.spring.data.eclipse.store.transactions.EclipseStoreTransaction; @@ -72,37 +70,24 @@ public class SimpleEclipseStoreRepository private final Class domainClass; private final WorkingCopier copier; private final EclipseStoreTransactionManager transactionManager; - private Field idField; + private final IdManager idManager; public SimpleEclipseStoreRepository( final EclipseStoreStorage storage, final WorkingCopier copier, final Class domainClass, - final EclipseStoreTransactionManager transactionManager) + final EclipseStoreTransactionManager transactionManager, + final IdManager idManager + ) { this.storage = storage; this.domainClass = domainClass; + this.idManager = idManager; this.storage.registerEntity(domainClass, this); this.copier = copier; this.transactionManager = transactionManager; } - public Field getIdField() - { - if(this.idField == null) - { - final Optional foundIdField = IdFieldFinder.findIdField(this.domainClass); - if(foundIdField.isEmpty()) - { - throw new NoIdFieldFoundException(String.format( - "Could not find id field in class %s", - this.domainClass.getSimpleName())); - } - this.idField = foundIdField.get(); - } - return this.idField; - } - @SuppressWarnings("unchecked") public List saveBulk(final Collection entities) { @@ -119,9 +104,18 @@ private void uncachedStore(final Collection entities) { LOG.debug("Saving {} entities...", entities.size()); } + + this.checkEntityForNull(entities); + this.idManager.checkIds(entities); + + Stream entitiesStream = entities.stream(); + if(this.isMergeParallelizable()) + { + entitiesStream = entities.parallelStream(); + } + final List> results = - this.checkEntityForNull(entities) - .parallelStream() + entitiesStream .map(this.copier::mergeBack) .toList(); final Set nonEntitiesToStore = @@ -145,6 +139,17 @@ private void uncachedStore(final Collection entities) ); } + /** + * If the entities class to merge has an id, it is not possible to parallelize the merge. To search for existing + * ids, we need a read lock. Since we are having a write lock to store the entities, we can not release the lock + * and + * would be stuck in a deadlock. + */ + private boolean isMergeParallelizable() + { + return !this.idManager.hasIdField(); + } + @Override @Nonnull public S save(@Nonnull final S entity) @@ -180,31 +185,7 @@ public List saveAll(@Nonnull final Iterable entities) public Optional findById(@Nonnull final ID id) { return this.storage.getReadWriteLock().read( - () -> this.storage - .getEntityList(this.domainClass) - .parallelStream() - .filter( - entity -> - { - try(final FieldAccessModifier fam = FieldAccessModifier.prepareForField( - this.getIdField(), - entity)) - { - if(id.equals(fam.getValueOfField(entity))) - { - return true; - } - } - catch(final Exception e) - { - throw new FieldAccessReflectionException(String.format( - FieldAccessReflectionException.COULD_NOT_READ_FIELD, - this.getIdField().getName()), e); - } - return false; - } - ) - .findAny() + () -> this.idManager.findById(id) .map(foundEntity -> this.copier.copy(foundEntity)) ); } @@ -212,7 +193,9 @@ public Optional findById(@Nonnull final ID id) @Override public boolean existsById(@Nonnull final ID id) { - return this.findById(id).isPresent(); + return this.storage.getReadWriteLock().read( + () -> this.idManager.findById(id).isPresent() + ); } @Override @@ -233,37 +216,7 @@ public List findAllById(@Nonnull final Iterable idsToFind) // Must get copied as one list to keep same references objects the same. // (Example: If o1 and o2 (both part of the entity list) are referencing o3, // o3 should be the same no matter from where it is referenced. - () -> this.copier.copy( - this.storage - .getEntityList(this.domainClass) - .parallelStream() - .filter( - entity -> - { - try(final FieldAccessModifier fam = FieldAccessModifier.prepareForField( - this.getIdField(), - entity)) - { - final Object idOfEntity = fam.getValueOfField(entity); - for(final ID idToFind : idsToFind) - { - if(idToFind.equals(idOfEntity)) - { - return true; - } - } - } - catch(final Exception e) - { - throw new FieldAccessReflectionException(String.format( - FieldAccessReflectionException.COULD_NOT_READ_FIELD, - this.getIdField().getName()), e); - } - return false; - } - ) - .toList() - ) + () -> this.copier.copy(this.idManager.findAllById(idsToFind)) ); } @@ -276,11 +229,18 @@ public long count() @Override public void deleteById(@Nonnull final ID id) { - this.storage.getReadWriteLock().write( - () -> { - final Optional byId = this.findById(id); - byId.ifPresent(this::delete); - } + final EclipseStoreTransaction transaction = this.transactionManager.getTransaction(); + transaction.addAction(() -> + this.storage.getReadWriteLock().write( + () -> { + this + .idManager + .findById(id) + .ifPresent( + foundEntity -> this.storage.delete(this.domainClass, foundEntity) + ); + } + ) ); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/EntityGetterById.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/EntityGetterById.java new file mode 100644 index 00000000..91e44a5c --- /dev/null +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/EntityGetterById.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.repository.support.copier.id; + +import java.util.Optional; + + +public interface EntityGetterById +{ + Optional findById(ID id); +} diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdGetter.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdGetter.java new file mode 100644 index 00000000..d10b5f06 --- /dev/null +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdGetter.java @@ -0,0 +1,21 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.repository.support.copier.id; + +public interface IdGetter +{ + ID getId(T objectToSetIdIn) throws Exception; +} diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdManager.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdManager.java new file mode 100644 index 00000000..3340fdbb --- /dev/null +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdManager.java @@ -0,0 +1,170 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.repository.support.copier.id; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.StreamSupport; + +import jakarta.annotation.Nonnull; + +import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException; +import software.xdev.spring.data.eclipse.store.exceptions.NoIdFieldFoundException; +import software.xdev.spring.data.eclipse.store.repository.EclipseStoreStorage; +import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier; +import software.xdev.spring.data.eclipse.store.repository.support.IdFieldFinder; + + +public class IdManager implements EntityGetterById, IdGetter +{ + private final Class domainClass; + private final IdSetter idSetter; + private final Optional idField; + private final EclipseStoreStorage storage; + + public IdManager( + final Class domainClass, + final IdSetter idSetter, + final EclipseStoreStorage storage + ) + { + this.domainClass = domainClass; + this.idSetter = idSetter; + this.storage = storage; + this.idField = IdFieldFinder.findIdField(this.domainClass); + } + + public Field ensureIdField() + { + if(this.idField.isEmpty()) + { + throw new NoIdFieldFoundException(String.format( + "Could not find id field in class %s", + this.domainClass.getSimpleName())); + } + return this.idField.get(); + } + + @Override + public Optional findById(@Nonnull final ID id) + { + this.ensureIdField(); + return this.storage.getReadWriteLock().read( + () -> this.storage + .getEntityList(this.domainClass) + .stream() + .filter(entity -> id.equals(this.getId(entity))) + .findAny() + ); + } + + public List findAllById(@Nonnull final Iterable idsToFind) + { + this.ensureIdField(); + return this.storage.getReadWriteLock().read( + () -> this.storage + .getEntityList(this.domainClass) + .stream() + .filter( + entity -> + { + final Object idOfEntity = this.getId(entity); + return StreamSupport + .stream(idsToFind.spliterator(), false) + .anyMatch(idToFind -> idToFind.equals(idOfEntity)); + } + ) + .toList() + ); + } + + public IdSetter getIdSetter() + { + return this.idSetter; + } + + @Override + public ID getId(final T entity) + { + if(this.hasIdField()) + { + try(final FieldAccessModifier fam = FieldAccessModifier.prepareForField( + this.ensureIdField(), + entity)) + { + return (ID)fam.getValueOfField(entity); + } + catch(final Exception e) + { + throw new FieldAccessReflectionException(String.format( + FieldAccessReflectionException.COULD_NOT_READ_FIELD, + this.ensureIdField().getName()), e); + } + } + return null; + } + + public boolean hasIdField() + { + return this.idField.isPresent(); + } + + /** + * This method makes sure, that an id is set for the given object. If it is already set (not null), then nothing is + * done. If it is not set, a new one will be generated and set. + */ + public void ensureId(final T objectToSetIdIn) + { + this.getIdSetter().ensureId(objectToSetIdIn); + } + + public void checkIds(final Collection entities) + { + if(!this.hasIdField()) + { + return; + } + final List ids = entities + .stream() + .map(entity -> this.getId(entity)) + .toList(); + + if(!this.getIdSetter().isAutomaticSetter() && ids.contains(null)) + { + final Optional entityWithNullId = + entities.stream().filter(entity -> this.getId(entity) == null).findAny(); + if(entityWithNullId.isPresent()) + { + throw new IllegalArgumentException( + "Invalid ID (null) for entity " + entityWithNullId + ); + } + } + + final List multipleEqualIds = ids.stream() + .filter(id -> Collections.frequency(ids, id) > 1) + .toList(); + if(!multipleEqualIds.isEmpty()) + { + throw new IllegalArgumentException( + "Same ID %s is set multiple times in one save call ".formatted(multipleEqualIds.get(0)) + ); + } + } +} diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdSetter.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdSetter.java index 9a2f1da0..33d02283 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdSetter.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/IdSetter.java @@ -62,4 +62,6 @@ static IdSetter createIdSetter( * done. If it is not set, a new one will be generated and set. */ void ensureId(T objectToSetIdIn); + + boolean isAutomaticSetter(); } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/NotSettingIdSetter.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/NotSettingIdSetter.java index c299b55d..1ee781f2 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/NotSettingIdSetter.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/NotSettingIdSetter.java @@ -20,6 +20,12 @@ public class NotSettingIdSetter implements IdSetter @Override public void ensureId(final T objectToSetIdIn) { - // Don't need to do anything + // Do nothing because no id generation is needed + } + + @Override + public boolean isAutomaticSetter() + { + return false; } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/SimpleIdSetter.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/SimpleIdSetter.java index e59bc75f..30aa830a 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/SimpleIdSetter.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/SimpleIdSetter.java @@ -60,7 +60,7 @@ public void ensureId(final T objectToSetIdIn) final Object existingId = fam.getValueOfField(objectToSetIdIn); if(existingId == null) { - final ID newId = this.idFinder.findId(); + final ID newId = (ID)this.idFinder.findId(); fam.writeValueOfField(objectToSetIdIn, newId, true); this.lastIdPersister.accept(newId); } @@ -70,4 +70,10 @@ public void ensureId(final T objectToSetIdIn) throw new FieldAccessReflectionException(this.idField, e); } } + + @Override + public boolean isAutomaticSetter() + { + return true; + } } diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/strategy/IdFinder.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/strategy/IdFinder.java index 2618fa2b..88391777 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/strategy/IdFinder.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/id/strategy/IdFinder.java @@ -21,6 +21,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; + import software.xdev.spring.data.eclipse.store.exceptions.IdGeneratorNotSupportedException; import software.xdev.spring.data.eclipse.store.repository.support.copier.id.strategy.auto.AutoIntegerIdFinder; import software.xdev.spring.data.eclipse.store.repository.support.copier.id.strategy.auto.AutoLongIdFinder; @@ -34,7 +35,7 @@ public interface IdFinder { @SuppressWarnings({"java:S1452", "TypeParameterExplicitlyExtendsObject"}) - static IdFinder createIdFinder( + static IdFinder createIdFinder( final Field idField, final GeneratedValue generatedValueAnnotation, final Supplier lastIdGetter) @@ -44,15 +45,15 @@ static IdFinder createIdFinder( { if(Integer.class.isAssignableFrom(idField.getType()) || int.class.isAssignableFrom(idField.getType())) { - return new AutoIntegerIdFinder(lastIdGetter); + return (IdFinder)new AutoIntegerIdFinder(lastIdGetter); } else if(idField.getType().equals(String.class)) { - return new AutoStringIdFinder(lastIdGetter); + return (IdFinder)new AutoStringIdFinder(lastIdGetter); } else if(Long.class.isAssignableFrom(idField.getType()) || long.class.isAssignableFrom(idField.getType())) { - return new AutoLongIdFinder(lastIdGetter); + return (IdFinder)new AutoLongIdFinder(lastIdGetter); } } throw new IdGeneratorNotSupportedException(String.format( diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/working/RecursiveWorkingCopier.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/working/RecursiveWorkingCopier.java index c03949fa..3e40cc09 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/working/RecursiveWorkingCopier.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/working/RecursiveWorkingCopier.java @@ -24,6 +24,7 @@ import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.Objects; +import java.util.Optional; import java.util.TreeMap; import java.util.TreeSet; @@ -33,7 +34,7 @@ import org.slf4j.LoggerFactory; import software.xdev.spring.data.eclipse.store.exceptions.MergeFailedException; -import software.xdev.spring.data.eclipse.store.repository.IdSetterProvider; +import software.xdev.spring.data.eclipse.store.repository.IdManagerProvider; import software.xdev.spring.data.eclipse.store.repository.PersistableChecker; import software.xdev.spring.data.eclipse.store.repository.SupportedChecker; import software.xdev.spring.data.eclipse.store.repository.WorkingCopyRegistry; @@ -41,6 +42,7 @@ import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier; import software.xdev.spring.data.eclipse.store.repository.lazy.SpringDataEclipseStoreLazy; import software.xdev.spring.data.eclipse.store.repository.support.copier.DataTypeUtil; +import software.xdev.spring.data.eclipse.store.repository.support.copier.id.IdManager; import software.xdev.spring.data.eclipse.store.repository.support.copier.registering.RegisteringObjectCopier; import software.xdev.spring.data.eclipse.store.repository.support.copier.registering.RegisteringStorageToWorkingCopyCopier; import software.xdev.spring.data.eclipse.store.repository.support.copier.registering.RegisteringWorkingCopyToStorageCopier; @@ -49,23 +51,25 @@ /** * Creates copies and puts them back. Recognizes already persisted Objects and checks them for changes as well. */ +@SuppressWarnings("PMD.GodClass") public class RecursiveWorkingCopier implements WorkingCopier { private static final Logger LOG = LoggerFactory.getLogger(RecursiveWorkingCopier.class); private final RegisteringObjectCopier workingCopyToStorageCopier; private final RegisteringObjectCopier storageToWorkingCopyCopier; private final WorkingCopyRegistry registry; - private final IdSetterProvider idSetterProvider; + private final IdManagerProvider idManagerProvider; private final Class domainClass; private final PersistableChecker persistableChecker; public RecursiveWorkingCopier( final Class domainClass, final WorkingCopyRegistry registry, - final IdSetterProvider idSetterProvider, + final IdManagerProvider idManagerProvider, final PersistableChecker persistableChecker, final SupportedChecker supportedChecker, - final ObjectSwizzling objectSwizzling) + final ObjectSwizzling objectSwizzling + ) { this.domainClass = domainClass; this.registry = registry; @@ -73,7 +77,7 @@ public RecursiveWorkingCopier( new RegisteringWorkingCopyToStorageCopier(registry, supportedChecker, objectSwizzling, this); this.storageToWorkingCopyCopier = new RegisteringStorageToWorkingCopyCopier(registry, supportedChecker, objectSwizzling, this); - this.idSetterProvider = idSetterProvider; + this.idManagerProvider = idManagerProvider; this.persistableChecker = persistableChecker; } @@ -138,16 +142,38 @@ public E getOrCreateObjectForDatastore( { return null; } - this.idSetterProvider.ensureIdSetter((Class)workingCopy.getClass()).ensureId(workingCopy); + final IdManager idManager = + this.idManagerProvider.ensureIdManager((Class)workingCopy.getClass()); + + idManager.ensureId(workingCopy); final E originalObject = this.registry.getOriginalObjectFromWorkingCopy(workingCopy); if(originalObject != null) { - if(mergeValues) + return this.mergeValueIfNeeded( + workingCopy, + mergeValues, + alreadyMergedTargets, + changedCollector, + originalObject + ); + } + + final Object id = idManager.getId(workingCopy); + if(id != null) + { + // If an id is used, we need to check if an entity with this id already exists and perhaps merge into + // that object. + final Optional existingEntity = idManager.findById(id); + if(existingEntity.isPresent()) { - this.mergeValues(workingCopy, originalObject, alreadyMergedTargets, changedCollector); + return this.mergeValueIfNeeded( + workingCopy, + mergeValues, + alreadyMergedTargets, + changedCollector, + existingEntity.get() + ); } - changedCollector.collectChangedObject(originalObject); - return originalObject; } // The object to merge back is not a working copy, but a originalObject. @@ -160,6 +186,21 @@ public E getOrCreateObjectForDatastore( return objectForDatastore; } + private E mergeValueIfNeeded( + final E workingCopy, + final boolean mergeValues, + final MergedTargetsCollector alreadyMergedTargets, + final ChangedObjectCollector changedCollector, + final E existingEntity) + { + if(mergeValues) + { + this.mergeValues(workingCopy, existingEntity, alreadyMergedTargets, changedCollector); + } + changedCollector.collectChangedObject(existingEntity); + return existingEntity; + } + @Override public T getOriginal(final T workingCopy) { diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/reposyncer/SimpleRepositorySynchronizer.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/reposyncer/SimpleRepositorySynchronizer.java index 7a4ee4ed..81b16a28 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/reposyncer/SimpleRepositorySynchronizer.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/reposyncer/SimpleRepositorySynchronizer.java @@ -51,14 +51,11 @@ public SimpleRepositorySynchronizer(final Root root) } final Class objectInGraphClass = (Class)objectInGraph.getClass(); final IdentitySet entityListForCurrentObject = this.root.getEntityList(objectInGraphClass); - if(entityListForCurrentObject != null) + if(entityListForCurrentObject != null + && !entityListForCurrentObject.contains(objectInGraph)) { - - if(!entityListForCurrentObject.contains(objectInGraph)) - { - entityListForCurrentObject.add(objectInGraph); - this.listsToStore.add(entityListForCurrentObject); - } + entityListForCurrentObject.add(objectInGraph); + this.listsToStore.add(entityListForCurrentObject); } } ).buildObjectGraphTraverser(); diff --git a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/transactions/EclipseStoreTransactionManager.java b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/transactions/EclipseStoreTransactionManager.java index 7e75b44f..44681088 100644 --- a/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/transactions/EclipseStoreTransactionManager.java +++ b/spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/transactions/EclipseStoreTransactionManager.java @@ -16,7 +16,6 @@ package software.xdev.spring.data.eclipse.store.transactions; import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionException; import org.springframework.transaction.support.AbstractPlatformTransactionManager; import org.springframework.transaction.support.DefaultTransactionStatus; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -28,7 +27,7 @@ public class EclipseStoreTransactionManager extends AbstractPlatformTransactionM private static final String TRANSACTION_MANAGER = "ATransactionManagerForThisThread"; @Override - protected Object doGetTransaction() throws TransactionException + protected Object doGetTransaction() { final EclipseStoreExistingTransactionObject transactionObject = (EclipseStoreExistingTransactionObject)TransactionSynchronizationManager.getResource(TRANSACTION_MANAGER); @@ -36,7 +35,7 @@ protected Object doGetTransaction() throws TransactionException } @Override - protected void doBegin(final Object transaction, final TransactionDefinition definition) throws TransactionException + protected void doBegin(final Object transaction, final TransactionDefinition definition) { final EclipseStoreExistingTransactionObject transactionObject = this.extractEclipseStoreTransaction(transaction); @@ -45,13 +44,13 @@ protected void doBegin(final Object transaction, final TransactionDefinition def } @Override - protected void doCommit(final DefaultTransactionStatus status) throws TransactionException + protected void doCommit(final DefaultTransactionStatus status) { this.extractEclipseStoreTransaction(status.getTransaction()).commitTransaction(); } @Override - protected void doRollback(final DefaultTransactionStatus status) throws TransactionException + protected void doRollback(final DefaultTransactionStatus status) { this.extractEclipseStoreTransaction(status.getTransaction()).rollbackTransaction(); } diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/TestConfiguration.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/TestConfiguration.java index 924aa41e..08974a22 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/TestConfiguration.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/TestConfiguration.java @@ -55,7 +55,7 @@ public EmbeddedStorageFoundation createEmbeddedStorageFoundation() } @EventListener - public void handleContextRefresh(final ContextRefreshedEvent event) + public void handleContextRefresh(final ContextRefreshedEvent event) throws IOException { // Init with empty root object this.getStorageInstance().clearData(); diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/IdTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTest.java similarity index 65% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/IdTest.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTest.java index cf6f91b9..d3dcb25a 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/IdTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.tests; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id; import static software.xdev.spring.data.eclipse.store.helper.TestUtil.restartDatastore; @@ -23,33 +23,41 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; import software.xdev.spring.data.eclipse.store.helper.TestData; import software.xdev.spring.data.eclipse.store.helper.TestUtil; -import software.xdev.spring.data.eclipse.store.integration.shared.DefaultTestAnnotations; -import software.xdev.spring.data.eclipse.store.integration.shared.SharedTestConfiguration; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdInt; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdIntRepository; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdInteger; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdIntegerNoAutoGenerate; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdIntegerNoAutoGenerateRepository; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdIntegerRepository; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdLong; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdLongRepository; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdString; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithIdStringRepository; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithPurchase; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.CustomerWithPurchaseRepository; -import software.xdev.spring.data.eclipse.store.integration.shared.repositories.id.Purchase; +import software.xdev.spring.data.eclipse.store.integration.isolated.IsolatedTestAnnotations; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdInt; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdIntRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdInteger; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdIntegerNoAutoGenerate; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdIntegerNoAutoGenerateRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdIntegerRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdLong; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdLongRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdString; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithIdStringRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithPurchase; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.CustomerWithPurchaseRepository; +import software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model.Purchase; import software.xdev.spring.data.eclipse.store.repository.EclipseStoreStorage; @SuppressWarnings("OptionalGetWithoutIsPresent") -@DefaultTestAnnotations +@IsolatedTestAnnotations +@ContextConfiguration(classes = {IdTestConfiguration.class}) class IdTest { + private final IdTestConfiguration configuration; + @Autowired - private SharedTestConfiguration configuration; + public IdTest(final IdTestConfiguration configuration) + { + this.configuration = configuration; + } @Test void testCreateSingleWithAutoIdInteger(@Autowired final CustomerWithIdIntegerRepository customerRepository) @@ -90,7 +98,8 @@ void testSaveSingleWithoutAnyPreviousCall(@Autowired final CustomerWithIdInteger @Test void testCreateSingleWithAutoIdIntegerWorkingCopyIdSet( - @Autowired final CustomerWithIdIntegerRepository customerRepository) + @Autowired final CustomerWithIdIntegerRepository customerRepository + ) { final CustomerWithIdInteger customer1 = new CustomerWithIdInteger(TestData.FIRST_NAME, TestData.LAST_NAME); Assertions.assertNull(customer1.getId()); @@ -120,7 +129,8 @@ void testCreateMultipleWithAutoIdInteger(@Autowired final CustomerWithIdIntegerR @Test void testCreateMultipleWithAutoIdIntegerSingleFinds( - @Autowired final CustomerWithIdIntegerRepository customerRepository) + @Autowired final CustomerWithIdIntegerRepository customerRepository + ) { final CustomerWithIdInteger customer1 = new CustomerWithIdInteger(TestData.FIRST_NAME, TestData.LAST_NAME); customerRepository.save(customer1); @@ -274,7 +284,8 @@ void testCreateMultipleWithNoAutoIdInteger( @Test void testSaveSingleWithAutoIdInteger( - @Autowired final CustomerWithIdIntegerRepository customerRepository) + @Autowired final CustomerWithIdIntegerRepository customerRepository + ) { final CustomerWithIdInteger customer1 = new CustomerWithIdInteger(); @@ -298,17 +309,9 @@ void testSaveSingleWithNoAutoIdInteger( { final CustomerWithIdIntegerNoAutoGenerate customer1 = new CustomerWithIdIntegerNoAutoGenerate(); - customerRepository.save(customer1); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List loadedCustomer = - TestUtil.iterableToList(customerRepository.findAll()); - Assertions.assertEquals(1, loadedCustomer.size()); - Assertions.assertNull(loadedCustomer.get(0).getId()); - Assertions.assertEquals(customer1, loadedCustomer.get(0)); - } + Assertions.assertThrows( + IllegalArgumentException.class, + () -> customerRepository.save(customer1) ); } @@ -412,4 +415,165 @@ void testAutoIdWithTwoSameSubnodesWithSameIdSameNode( } ); } + + @Test + void testReplaceWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) + { + final CustomerWithIdIntegerNoAutoGenerate existingCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); + customerRepository.save(existingCustomer); + + final CustomerWithIdIntegerNoAutoGenerate newCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME_ALTERNATIVE, + TestData.LAST_NAME_ALTERNATIVE); + customerRepository.save(newCustomer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List loadedCustomer = + TestUtil.iterableToList(customerRepository.findAll()); + + Assertions.assertEquals(1, loadedCustomer.size()); + Assertions.assertEquals(TestData.FIRST_NAME_ALTERNATIVE, loadedCustomer.get(0).getFirstName()); + Assertions.assertEquals(TestData.LAST_NAME_ALTERNATIVE, loadedCustomer.get(0).getLastName()); + } + ); + } + + @Test + void testReplaceWithAutoId(@Autowired final CustomerWithIdIntegerRepository customerRepository) + { + final CustomerWithIdInteger existingCustomer = + new CustomerWithIdInteger(TestData.FIRST_NAME, TestData.LAST_NAME); + customerRepository.save(existingCustomer); + final Integer existingId = customerRepository.findAll().iterator().next().getId(); + + final CustomerWithIdInteger newCustomer = new CustomerWithIdInteger( + existingId, + TestData.FIRST_NAME_ALTERNATIVE, + TestData.LAST_NAME_ALTERNATIVE); + customerRepository.save(newCustomer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List loadedCustomer = + TestUtil.iterableToList(customerRepository.findAll()); + + Assertions.assertEquals(1, loadedCustomer.size()); + Assertions.assertEquals(TestData.FIRST_NAME_ALTERNATIVE, loadedCustomer.get(0).getFirstName()); + Assertions.assertEquals(TestData.LAST_NAME_ALTERNATIVE, loadedCustomer.get(0).getLastName()); + } + ); + } + + @Test + void testReplaceWithIdSaveAll(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) + { + final CustomerWithIdIntegerNoAutoGenerate existingCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); + final CustomerWithIdIntegerNoAutoGenerate newCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME_ALTERNATIVE, + TestData.LAST_NAME_ALTERNATIVE); + + Assertions.assertThrows( + IllegalArgumentException.class, + () -> customerRepository.saveAll(List.of(existingCustomer, newCustomer)) + ); + } + + @Test + void testAddTwoWithId(@Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository) + { + final CustomerWithIdIntegerNoAutoGenerate existingCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); + customerRepository.save(existingCustomer); + + final CustomerWithIdIntegerNoAutoGenerate newCustomer = + new CustomerWithIdIntegerNoAutoGenerate(2, TestData.FIRST_NAME_ALTERNATIVE, + TestData.LAST_NAME_ALTERNATIVE); + customerRepository.save(newCustomer); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List loadedCustomer = + TestUtil.iterableToList(customerRepository.findAll()); + + Assertions.assertEquals(2, loadedCustomer.size()); + } + ); + } + + @Test + void testIdsInMultipleTransactions( + @Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository, + @Autowired final PlatformTransactionManager transactionManager + ) + { + new TransactionTemplate(transactionManager).execute( + status -> + { + final CustomerWithIdIntegerNoAutoGenerate existingCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); + customerRepository.save(existingCustomer); + return null; + }); + + new TransactionTemplate(transactionManager).execute( + status -> + { + final CustomerWithIdIntegerNoAutoGenerate newCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME_ALTERNATIVE, + TestData.LAST_NAME_ALTERNATIVE); + customerRepository.save(newCustomer); + return null; + }); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List loadedCustomer = + TestUtil.iterableToList(customerRepository.findAll()); + + Assertions.assertEquals(1, loadedCustomer.size()); + Assertions.assertEquals(TestData.FIRST_NAME_ALTERNATIVE, loadedCustomer.get(0).getFirstName()); + Assertions.assertEquals(TestData.LAST_NAME_ALTERNATIVE, loadedCustomer.get(0).getLastName()); + } + ); + } + + @Test + void testIdsInSingleTransactions( + @Autowired final CustomerWithIdIntegerNoAutoGenerateRepository customerRepository, + @Autowired final PlatformTransactionManager transactionManager + ) + { + new TransactionTemplate(transactionManager).execute( + status -> + { + final CustomerWithIdIntegerNoAutoGenerate existingCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME, TestData.LAST_NAME); + customerRepository.save(existingCustomer); + + final CustomerWithIdIntegerNoAutoGenerate newCustomer = + new CustomerWithIdIntegerNoAutoGenerate(1, TestData.FIRST_NAME_ALTERNATIVE, + TestData.LAST_NAME_ALTERNATIVE); + customerRepository.save(newCustomer); + return null; + }); + + TestUtil.doBeforeAndAfterRestartOfDatastore( + this.configuration, + () -> { + final List loadedCustomer = + TestUtil.iterableToList(customerRepository.findAll()); + + Assertions.assertEquals(1, loadedCustomer.size()); + Assertions.assertEquals(TestData.FIRST_NAME_ALTERNATIVE, loadedCustomer.get(0).getFirstName()); + Assertions.assertEquals(TestData.LAST_NAME_ALTERNATIVE, loadedCustomer.get(0).getLastName()); + } + ); + } } diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTestConfiguration.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTestConfiguration.java new file mode 100644 index 00000000..b70d680c --- /dev/null +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/IdTestConfiguration.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2024 XDEV Software (https://xdev.software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id; + +import org.eclipse.store.integrations.spring.boot.types.configuration.EclipseStoreProperties; +import org.eclipse.store.integrations.spring.boot.types.factories.EmbeddedStorageFoundationFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; + +import software.xdev.spring.data.eclipse.store.integration.TestConfiguration; +import software.xdev.spring.data.eclipse.store.repository.config.EnableEclipseStoreRepositories; + + +@Configuration +@EnableEclipseStoreRepositories +public class IdTestConfiguration extends TestConfiguration +{ + @Autowired + protected IdTestConfiguration( + final EclipseStoreProperties defaultEclipseStoreProperties, + final EmbeddedStorageFoundationFactory defaultEclipseStoreProvider) + { + super(defaultEclipseStoreProperties, defaultEclipseStoreProvider); + } + + @Bean + @Override + public PlatformTransactionManager transactionManager( + final ObjectProvider transactionManagerCustomizers + ) + { + return super.transactionManager(transactionManagerCustomizers); + } +} diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdInt.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdInt.java similarity index 96% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdInt.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdInt.java index 30a8c9ca..944d4bbe 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdInt.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdInt.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import java.util.List; import java.util.Objects; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntRepository.java similarity index 90% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntRepository.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntRepository.java index 8e32578d..27958635 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntRepository.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import org.springframework.data.repository.CrudRepository; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdInteger.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdInteger.java similarity index 89% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdInteger.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdInteger.java index 81cd2f1f..a9d98356 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdInteger.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdInteger.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import java.util.List; import java.util.Objects; @@ -42,6 +42,13 @@ public CustomerWithIdInteger(final String firstName, final String lastName) this.lastName = lastName; } + public CustomerWithIdInteger(final Integer id, final String firstName, final String lastName) + { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + public String getFirstName() { return this.firstName; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerNoAutoGenerate.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerNoAutoGenerate.java similarity index 96% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerNoAutoGenerate.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerNoAutoGenerate.java index c3540763..9ac26318 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerNoAutoGenerate.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerNoAutoGenerate.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import java.util.List; import java.util.Objects; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerNoAutoGenerateRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerNoAutoGenerateRepository.java similarity index 90% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerNoAutoGenerateRepository.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerNoAutoGenerateRepository.java index 55c15fe0..8aff5e66 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerNoAutoGenerateRepository.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerNoAutoGenerateRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import org.springframework.data.repository.CrudRepository; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerRepository.java similarity index 90% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerRepository.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerRepository.java index 8846c83a..6fc66730 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdIntegerRepository.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdIntegerRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import org.springframework.data.repository.CrudRepository; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdLong.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdLong.java similarity index 95% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdLong.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdLong.java index 1fa239b0..7f5cc104 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdLong.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdLong.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import java.util.Objects; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdLongRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdLongRepository.java similarity index 90% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdLongRepository.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdLongRepository.java index d70b9b76..0646719b 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdLongRepository.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdLongRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import org.springframework.data.repository.CrudRepository; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdString.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdString.java similarity index 95% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdString.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdString.java index 4ff3e317..930015d9 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdString.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdString.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import java.util.Objects; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdStringRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdStringRepository.java similarity index 90% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdStringRepository.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdStringRepository.java index 7efb2535..4721e8a0 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithIdStringRepository.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithIdStringRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import org.springframework.data.repository.CrudRepository; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithPurchase.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithPurchase.java similarity index 96% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithPurchase.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithPurchase.java index d55f7503..2f34bf34 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithPurchase.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithPurchase.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import java.util.ArrayList; import java.util.List; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithPurchaseRepository.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithPurchaseRepository.java similarity index 90% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithPurchaseRepository.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithPurchaseRepository.java index 9bc5eae8..cdbc1b30 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/CustomerWithPurchaseRepository.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/CustomerWithPurchaseRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import org.springframework.data.repository.CrudRepository; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/Purchase.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/Purchase.java similarity index 94% rename from spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/Purchase.java rename to spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/Purchase.java index 2a3af61e..a96367b6 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/repositories/id/Purchase.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/id/model/Purchase.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package software.xdev.spring.data.eclipse.store.integration.shared.repositories.id; +package software.xdev.spring.data.eclipse.store.integration.isolated.tests.id.model; import java.util.Objects; diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/lazy/LazyTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/lazy/LazyTest.java index 68c3b613..f2b4d490 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/lazy/LazyTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/lazy/LazyTest.java @@ -188,16 +188,8 @@ void lazyWorkingCopyChangeAfterRestart(@Autowired final ObjectWithLazyRepository @Test void lazyStoreComplexObject(@Autowired final ObjectWithLazyRepository repository) { - final ObjectWithLazy newLazy = new ObjectWithLazy<>(); - final ComplexLazyObject objectToStore = new ComplexLazyObject( - SpringDataEclipseStoreLazy.build(new SimpleObject(TestData.DUMMY_STRING)), - new ArrayList<>(Arrays.asList( - SpringDataEclipseStoreLazy.build(new ArrayList<>(Arrays.asList(TestData.DUMMY_STRING))), - SpringDataEclipseStoreLazy.build(new ArrayList<>(Arrays.asList(TestData.DUMMY_STRING_ALTERNATIVE))) - )) - ); - newLazy.setLazy(SpringDataEclipseStoreLazy.build(objectToStore)); - repository.save(newLazy); + final ComplexLazyObject objectToStore = + prepareLazyComplexObject(repository); TestUtil.doBeforeAndAfterRestartOfDatastore( this.configuration, @@ -222,16 +214,7 @@ void lazyStoreComplexObject(@Autowired final ObjectWithLazyRepository repository) { - final ObjectWithLazy newLazy = new ObjectWithLazy<>(); - final ComplexLazyObject objectToStore = new ComplexLazyObject( - SpringDataEclipseStoreLazy.build(new SimpleObject(TestData.DUMMY_STRING)), - new ArrayList<>(Arrays.asList( - SpringDataEclipseStoreLazy.build(new ArrayList<>(Arrays.asList(TestData.DUMMY_STRING))), - SpringDataEclipseStoreLazy.build(new ArrayList<>(Arrays.asList(TestData.DUMMY_STRING_ALTERNATIVE))) - )) - ); - newLazy.setLazy(SpringDataEclipseStoreLazy.build(objectToStore)); - repository.save(newLazy); + prepareLazyComplexObject(repository); restartDatastore(this.configuration); @@ -243,50 +226,43 @@ void lazyChangeComplexObject(@Autowired final ObjectWithLazyRepository { - final ObjectWithLazy loadedObject = repository.findAll().get(0); - Assertions.assertNotSame(loadedObjectToChange, loadedObject); - Assertions.assertNotSame( - loadedObjectToChange.getLazy().get(), - loadedObject.getLazy().get() - ); - Assertions.assertEquals( - loadedObjectToChange.getLazy().get().getListOfLazyListOfString().size(), - loadedObject.getLazy().get().getListOfLazyListOfString().size() - ); - Assertions.assertEquals( - loadedObjectToChange.getLazy().get().getListOfLazyListOfString().get(0).get().size(), - loadedObject.getLazy().get().getListOfLazyListOfString().get(0).get().size() - ); - Assertions.assertEquals( - loadedObjectToChange.getLazy().get().getListOfLazyListOfString().get(0).get().get(0), - loadedObject.getLazy().get().getListOfLazyListOfString().get(0).get().get(0) - ); - } - ); + this.validateLazyComplexObject(repository, loadedObjectToChange); } @Test void lazyReloadAndRestoreComplexObject(@Autowired final ObjectWithLazyRepository repository) + { + prepareLazyComplexObject(repository); + + restartDatastore(this.configuration); + + final ObjectWithLazy loadedObjectToChange = repository.findAll().get(0); + repository.save(loadedObjectToChange); + + this.validateLazyComplexObject(repository, loadedObjectToChange); + } + + private static ComplexLazyObject prepareLazyComplexObject( + final ObjectWithLazyRepository repository) { final ObjectWithLazy newLazy = new ObjectWithLazy<>(); final ComplexLazyObject objectToStore = new ComplexLazyObject( SpringDataEclipseStoreLazy.build(new SimpleObject(TestData.DUMMY_STRING)), new ArrayList<>(Arrays.asList( - SpringDataEclipseStoreLazy.build(new ArrayList<>(Arrays.asList(TestData.DUMMY_STRING))), - SpringDataEclipseStoreLazy.build(new ArrayList<>(Arrays.asList(TestData.DUMMY_STRING_ALTERNATIVE))) + SpringDataEclipseStoreLazy.build(new ArrayList<>(List.of(TestData.DUMMY_STRING))), + SpringDataEclipseStoreLazy.build(new ArrayList<>(List.of(TestData.DUMMY_STRING_ALTERNATIVE))) )) ); newLazy.setLazy(SpringDataEclipseStoreLazy.build(objectToStore)); repository.save(newLazy); - restartDatastore(this.configuration); - - final ObjectWithLazy loadedObjectToChange = repository.findAll().get(0); - repository.save(loadedObjectToChange); - + return objectToStore; + } + + private void validateLazyComplexObject( + final ObjectWithLazyRepository repository, + final ObjectWithLazy loadedObjectToChange) + { TestUtil.doBeforeAndAfterRestartOfDatastore( this.configuration, () -> { diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/LazyDaoObject.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/LazyDaoObject.java index a58bf983..30c33150 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/LazyDaoObject.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/LazyDaoObject.java @@ -16,8 +16,10 @@ package software.xdev.spring.data.eclipse.store.integration.isolated.tests.special.types; import java.util.Objects; +import java.util.Optional; import org.eclipse.serializer.reference.Lazy; +import org.eclipse.serializer.reference.Referencing; public class LazyDaoObject extends ComplexObject> @@ -30,7 +32,11 @@ public LazyDaoObject(final Integer id, final Lazy value) @Override public int hashCode() { - return super.hashCode(); + return Objects.hash( + this.getId(), + Optional.ofNullable(this.getValue()) + .map(Referencing::get) + .orElse(null)); } @Override @@ -47,8 +53,6 @@ public boolean equals(final Object o) final ComplexObject> that = (ComplexObject>)o; return Objects.equals(this.getId(), that.getId()) && (this.getValue() == null && that.getValue() == null) - || Objects.equals( - this.getValue().get(), - that.getValue().get()); + || Objects.equals(this.getValue().get(), that.getValue().get()); } } diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/TypesData.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/TypesData.java index 0062d6de..1f3b8c18 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/TypesData.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/special/types/TypesData.java @@ -53,6 +53,7 @@ import software.xdev.spring.data.eclipse.store.repository.lazy.SpringDataEclipseStoreLazy; +@SuppressWarnings("PMD.UseArrayListInsteadOfVector") // Vector is required for tests final class TypesData { private TypesData() diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsAnnotationTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsAnnotationTest.java index 0addc20b..d2fdfdcb 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsAnnotationTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsAnnotationTest.java @@ -45,8 +45,8 @@ public TransactionsAnnotationTest(final AccountRepository repository) void accountTransactionUnexpectedErrorAnnotation() { Assertions.assertThrows(RuntimeException.class, () -> { - final Account account1 = new Account(1, BigDecimal.TEN); - final Account account2 = new Account(2, BigDecimal.ZERO); + final Account account1 = new Account(3, BigDecimal.TEN); + final Account account2 = new Account(4, BigDecimal.ZERO); this.repository.saveAll(List.of(account1, account2)); throw new RuntimeException("Unexpected error"); diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsConcurrencyTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsConcurrencyTest.java index d49ad14d..70d3b7b0 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsConcurrencyTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsConcurrencyTest.java @@ -28,6 +28,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.PlatformTransactionManager; @@ -49,51 +51,18 @@ public TransactionsConcurrencyTest(final AccountRepository accountRepository) this.accountRepository = accountRepository; } - @Test - void testSaveConcurrentlyPreviouslyNonExistingAccounts( - @Autowired final PlatformTransactionManager transactionManager - ) - throws InterruptedException - { - final List testAccounts = - IntStream.range(1, 1000).mapToObj((i) -> new Account(i, BigDecimal.TEN)).toList(); - - final ExecutorService service = Executors.newFixedThreadPool(10); - final CountDownLatch latch = new CountDownLatch(testAccounts.size()); - testAccounts.forEach( - account -> - service.execute(() -> - { - new TransactionTemplate(transactionManager).execute( - status -> - { - account.setBalance(account.getBalance().subtract(BigDecimal.ONE)); - this.accountRepository.save(account); - return null; - }); - Assertions.assertEquals( - BigDecimal.valueOf(9), - this.accountRepository.findById(account.getId()).get().getBalance()); - latch.countDown(); - } - ) - ); - - assertTrue(latch.await(5, TimeUnit.SECONDS)); - - final List accounts = TestUtil.iterableToList(this.accountRepository.findAll()); - assertEquals(testAccounts.size(), accounts.size()); - accounts.forEach(account -> Assertions.assertEquals(BigDecimal.valueOf(9), account.getBalance())); - } - - @Test - void testSaveConcurrentlyPreviouslyExistingAccounts( - @Autowired final PlatformTransactionManager transactionManager) - throws InterruptedException + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void saveConcurrently( + final boolean previouslyExisting, + @Autowired final PlatformTransactionManager transactionManager) throws InterruptedException { final List testAccounts = - IntStream.range(1, 100).mapToObj((i) -> new Account(i, BigDecimal.TEN)).toList(); - this.accountRepository.saveAll(testAccounts); + IntStream.range(1, 1000).mapToObj(i -> new Account(i, BigDecimal.TEN)).toList(); + if(previouslyExisting) + { + this.accountRepository.saveAll(testAccounts); + } final ExecutorService service = Executors.newFixedThreadPool(10); final CountDownLatch latch = new CountDownLatch(testAccounts.size()); @@ -101,9 +70,12 @@ void testSaveConcurrentlyPreviouslyExistingAccounts( account -> service.execute(() -> { - Assertions.assertEquals( - BigDecimal.TEN, - this.accountRepository.findById(account.getId()).get().getBalance()); + if(previouslyExisting) + { + Assertions.assertEquals( + BigDecimal.TEN, + this.accountRepository.findById(account.getId()).get().getBalance()); + } new TransactionTemplate(transactionManager).execute( status -> { diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsTest.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsTest.java index 8756e472..6ddae199 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsTest.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsTest.java @@ -113,24 +113,7 @@ void accountTransactionChangeAfterSave(@Autowired final PlatformTransactionManag @Test void accountAndCounterTransactionSequential(@Autowired final PlatformTransactionManager transactionManager) { - new TransactionTemplate(transactionManager).execute( - status -> - { - this.account1.setBalance(this.account1.getBalance().subtract(BigDecimal.ONE)); - this.accountRepository.save(this.account1); - - this.account2.setBalance(this.account2.getBalance().add(BigDecimal.ONE)); - this.accountRepository.save(this.account2); - return null; - } - ); - - Assertions.assertEquals( - BigDecimal.valueOf(9), - this.accountRepository.findById(this.account1.getId()).get().getBalance()); - Assertions.assertEquals( - BigDecimal.ONE, - this.accountRepository.findById(this.account2.getId()).get().getBalance()); + this.accountTransactionWorking(transactionManager); new TransactionTemplate(transactionManager).execute( status -> @@ -254,8 +237,8 @@ void findStoredEntityWithinTransaction(@Autowired final PlatformTransactionManag void accountNoTransactionUnexpectedError() { Assertions.assertThrows(RuntimeException.class, () -> { - final Account account1 = new Account(1, BigDecimal.TEN); - final Account account2 = new Account(2, BigDecimal.ZERO); + final Account account1 = new Account(3, BigDecimal.TEN); + final Account account2 = new Account(4, BigDecimal.ZERO); this.accountRepository.saveAll(List.of(account1, account2)); throw new RuntimeException("Unexpected error"); diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/ChangeRootTests.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/ChangeRootTests.java index 356f6c1a..e229eaa0 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/ChangeRootTests.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/ChangeRootTests.java @@ -29,7 +29,7 @@ @DefaultTestAnnotations -public class ChangeRootTests +class ChangeRootTests { public static final String CHILD_1 = "child1"; public static final String CHILD_2 = "child2"; @@ -37,9 +37,9 @@ public class ChangeRootTests public static final String PARENT_2 = "parent2"; @Autowired - private NodeRepository repository; + NodeRepository repository; @Autowired - private SharedTestConfiguration configuration; + SharedTestConfiguration configuration; //@formatter:off /** @@ -54,16 +54,7 @@ void testSaveParentAndFindAll() final Node parentNode = new Node(PARENT_1, List.of(childNode1, childNode2)); this.repository.save(parentNode); - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List nodes = TestUtil.iterableToList(this.repository.findAll()); - Assertions.assertEquals(3, nodes.size()); - Assertions.assertTrue(Node.getNodeWithName(nodes, PARENT_1).isPresent()); - Assertions.assertTrue(Node.getNodeWithName(nodes, CHILD_1).isPresent()); - Assertions.assertTrue(Node.getNodeWithName(nodes, CHILD_2).isPresent()); - } - ); + this.validate(PARENT_1, CHILD_1, CHILD_2); } //@formatter:off @@ -81,16 +72,7 @@ void testSaveRecursive() childNode1.getChildren().add(parentNode2); this.repository.save(parentNode1); - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List nodes = TestUtil.iterableToList(this.repository.findAll()); - Assertions.assertEquals(3, nodes.size()); - Assertions.assertTrue(Node.getNodeWithName(nodes, PARENT_1).isPresent()); - Assertions.assertTrue(Node.getNodeWithName(nodes, CHILD_1).isPresent()); - Assertions.assertTrue(Node.getNodeWithName(nodes, PARENT_2).isPresent()); - } - ); + this.validate(PARENT_1, CHILD_1, PARENT_2); } //@formatter:off @@ -109,14 +91,21 @@ void testSaveGraph() childNode2.getChildren().add(parentNode); this.repository.save(parentNode); + this.validate(PARENT_1, CHILD_1, CHILD_2); + } + + private void validate(final String... presentNodes) + { TestUtil.doBeforeAndAfterRestartOfDatastore( this.configuration, () -> { final List nodes = TestUtil.iterableToList(this.repository.findAll()); - Assertions.assertEquals(3, nodes.size()); - Assertions.assertTrue(Node.getNodeWithName(nodes, PARENT_1).isPresent()); - Assertions.assertTrue(Node.getNodeWithName(nodes, CHILD_1).isPresent()); - Assertions.assertTrue(Node.getNodeWithName(nodes, CHILD_2).isPresent()); + Assertions.assertEquals(presentNodes.length, nodes.size()); + + for(final String expectedNodeName : presentNodes) + { + Assertions.assertTrue(Node.getNodeWithName(nodes, expectedNodeName).isPresent()); + } } ); } diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/RealLifeTests.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/RealLifeTests.java index 026a3a05..2dd2c883 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/RealLifeTests.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/RealLifeTests.java @@ -40,15 +40,18 @@ @SuppressWarnings("OptionalGetWithoutIsPresent") @DefaultTestAnnotations -public class RealLifeTests +class RealLifeTests { - public static final String STATIONERY = "Stationery"; - public static final String BUILDING_MATERIAL = "Building Material"; - public static final String PEN = "Pen"; - public static final String BRICK = "Brick"; - public static final int PEN_AMOUNT = 5; - public static final int BRICK_AMOUNT = 2; - public static final String WAREHOUSE_WEIDEN = "Weiden"; + static final String STATIONERY = "Stationery"; + static final String BUILDING_MATERIAL = "Building Material"; + static final String PEN = "Pen"; + static final String BRICK = "Brick"; + static final int PEN_AMOUNT = 5; + static final int BRICK_AMOUNT = 2; + static final String WAREHOUSE_WEIDEN = "Weiden"; + static final String SHOE_ARTICLE_NAME = "Shoe"; + static final String SHOES_ARTICLE_GROUP = "Shoes"; + @Autowired InvoiceRepository invoiceRepository; @Autowired @@ -108,33 +111,19 @@ void testReplacePositionWithExistingArticle() @Test void testReplacePositionWithNewArticleThroughInvoiceRepository() { - final String shoeArticleName = "Shoe"; this.buildDefaultModelAndSaveIt(); final Invoice invoice = TestUtil.iterableToList(this.invoiceRepository.findAll()).get(0); final List positions = invoice.getPositions(); positions.remove(1); - final ArticleGroup shoesGroup = new ArticleGroup("Shoes"); - final Article shoe = - new Article(shoeArticleName, shoesGroup, positions.get(0).getArticle().getWarehouses().get(0)); - positions.add(new Position(shoe, 4)); + createAndAddShoeToPosition(positions); this.invoiceRepository.save(invoice); TestUtil.doBeforeAndAfterRestartOfDatastore( this.configuration, () -> { final Invoice loadedInvoice = TestUtil.iterableToList(this.invoiceRepository.findAll()).get(0); - final Optional - positionOfShoe = getPositionWithArticleWithName(loadedInvoice.getPositions(), shoeArticleName); - final Optional positionWithAmount4 = - loadedInvoice.getPositions().stream().filter(position -> position.getAmount() == 4).findFirst(); - Assertions.assertEquals(2, loadedInvoice.getPositions().size()); - Assertions.assertTrue(positionWithAmount4.isPresent()); - Assertions.assertTrue(positionOfShoe.isPresent()); - Assertions.assertEquals("Shoes", positionOfShoe.get().getArticle().getGroup().getName()); - Assertions.assertSame( - loadedInvoice.getPositions().get(0).getArticle().getWarehouses().get(0), - loadedInvoice.getPositions().get(1).getArticle().getWarehouses().get(0)); + this.validatePositions(loadedInvoice.getPositions(), 2); } ); } @@ -142,32 +131,18 @@ void testReplacePositionWithNewArticleThroughInvoiceRepository() @Test void testAddNewArticleWithSameWarehouse() { - final String shoeArticleName = "Shoe"; this.buildDefaultModelAndSaveIt(); final Invoice invoice = TestUtil.iterableToList(this.invoiceRepository.findAll()).get(0); final List positions = invoice.getPositions(); - final ArticleGroup shoesGroup = new ArticleGroup("Shoes"); - final Article shoe = - new Article(shoeArticleName, shoesGroup, positions.get(0).getArticle().getWarehouses().get(0)); - positions.add(new Position(shoe, 4)); + createAndAddShoeToPosition(positions); this.invoiceRepository.save(invoice); TestUtil.doBeforeAndAfterRestartOfDatastore( this.configuration, () -> { final Invoice loadedInvoice = TestUtil.iterableToList(this.invoiceRepository.findAll()).get(0); - final Optional - positionOfShoe = getPositionWithArticleWithName(loadedInvoice.getPositions(), shoeArticleName); - final Optional positionWithAmount4 = - loadedInvoice.getPositions().stream().filter(position -> position.getAmount() == 4).findFirst(); - Assertions.assertEquals(3, loadedInvoice.getPositions().size()); - Assertions.assertTrue(positionWithAmount4.isPresent()); - Assertions.assertTrue(positionOfShoe.isPresent()); - Assertions.assertEquals("Shoes", positionOfShoe.get().getArticle().getGroup().getName()); - Assertions.assertSame( - loadedInvoice.getPositions().get(0).getArticle().getWarehouses().get(0), - loadedInvoice.getPositions().get(1).getArticle().getWarehouses().get(0)); + this.validatePositions(loadedInvoice.getPositions(), 3); Assertions.assertSame( loadedInvoice.getPositions().get(1).getArticle().getWarehouses().get(0), loadedInvoice.getPositions().get(2).getArticle().getWarehouses().get(0)); @@ -178,48 +153,45 @@ void testAddNewArticleWithSameWarehouse() @Test void testReplacePositionWithNewArticleThroughPositionRepository() { - final String shoeArticleName = "Shoe"; this.buildDefaultModelAndSaveIt(); List positions = TestUtil.iterableToList(this.positionRepository.findAll()); this.positionRepository.delete(positions.get(1)); positions = TestUtil.iterableToList(this.positionRepository.findAll()); - final ArticleGroup shoesGroup = new ArticleGroup("Shoes"); - final Article shoe = - new Article(shoeArticleName, shoesGroup, positions.get(0).getArticle().getWarehouses().get(0)); - positions.add(new Position(shoe, 4)); + createAndAddShoeToPosition(positions); this.positionRepository.saveAll(positions); TestUtil.doBeforeAndAfterRestartOfDatastore( this.configuration, () -> { final List loadedPositions = TestUtil.iterableToList(this.positionRepository.findAll()); - final Optional - positionOfShoe = getPositionWithArticleWithName(loadedPositions, shoeArticleName); - final Optional positionWithAmount4 = - loadedPositions.stream().filter(position -> position.getAmount() == 4).findFirst(); - Assertions.assertEquals(2, loadedPositions.size()); - Assertions.assertTrue(positionWithAmount4.isPresent()); - Assertions.assertTrue(positionOfShoe.isPresent()); - Assertions.assertEquals("Shoes", positionOfShoe.get().getArticle().getGroup().getName()); - Assertions.assertSame( - loadedPositions.get(0).getArticle().getWarehouses().get(0), - loadedPositions.get(1).getArticle().getWarehouses().get(0)); + this.validatePositions(loadedPositions, 2); } ); } + private void validatePositions(final List positions, final int expectedSize) + { + final Optional + positionOfShoe = getPositionWithArticleWithName(positions, SHOE_ARTICLE_NAME); + final Optional positionWithAmount4 = + positions.stream().filter(position -> position.getAmount() == 4).findFirst(); + Assertions.assertEquals(expectedSize, positions.size()); + Assertions.assertTrue(positionWithAmount4.isPresent()); + Assertions.assertTrue(positionOfShoe.isPresent()); + Assertions.assertEquals(SHOES_ARTICLE_GROUP, positionOfShoe.get().getArticle().getGroup().getName()); + Assertions.assertSame( + positions.get(0).getArticle().getWarehouses().get(0), + positions.get(1).getArticle().getWarehouses().get(0)); + } + @Test void testReplacePositionWithNewArticleAndUseImmutableList() { - final String shoeArticleName = "Shoe"; this.buildDefaultModelAndSaveIt(); final List positions = TestUtil.iterableToList(this.positionRepository.findAll()); - final ArticleGroup shoesGroup = new ArticleGroup("Shoes"); - final Article shoe = - new Article(shoeArticleName, shoesGroup, List.of(positions.get(0).getArticle().getWarehouses().get(0))); - positions.add(new Position(shoe, 4)); + createAndAddShoeToPosition(positions); this.positionRepository.saveAll(positions); TestUtil.doBeforeAndAfterRestartOfDatastore( @@ -233,6 +205,14 @@ void testReplacePositionWithNewArticleAndUseImmutableList() ); } + private static void createAndAddShoeToPosition(final List positions) + { + final ArticleGroup shoesGroup = new ArticleGroup(SHOES_ARTICLE_GROUP); + final Article shoe = + new Article(SHOE_ARTICLE_NAME, shoesGroup, List.of(positions.get(0).getArticle().getWarehouses().get(0))); + positions.add(new Position(shoe, 4)); + } + @Test void testSaveTwoObjectsWithSameReferenceThroughPositionRepository() { diff --git a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/WorkingCopyTests.java b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/WorkingCopyTests.java index 84de03cb..30edd01a 100644 --- a/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/WorkingCopyTests.java +++ b/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/shared/tests/WorkingCopyTests.java @@ -20,6 +20,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import software.xdev.spring.data.eclipse.store.helper.TestData; @@ -96,12 +98,6 @@ void testBasicDoubleCopy() Assertions.assertNotSame(customers1.get(0), customers2.get(0)); } - //@formatter:off - /** - * □-□ - * □-┘ - **/ - //@formatter:on @Test void testStoreGraphWithCircularRelation() { @@ -136,53 +132,9 @@ void testStoreGraphWithCircularRelation() ); } - //@formatter:off - /** - * □-□ - * □-┘ - **/ - //@formatter:on - @Test - void testStoreGraphWithCircularRelationWithDoubleSave() - { - final Node childNode = new Node(CHILD_NAME_1); - final Node parentNode1 = new Node(PARENT_NAME_1, List.of(childNode)); - final Node parentNode2 = new Node(PARENT_NAME_2); - childNode.getChildren().add(parentNode2); - - this.nodeRepository.save(parentNode1); - this.nodeRepository.save(parentNode2); - - TestUtil.doBeforeAndAfterRestartOfDatastore( - this.configuration, - () -> { - final List loadedNodes = TestUtil.iterableToList(this.nodeRepository.findAll()); - Assertions.assertEquals(3, loadedNodes.size()); - - final Node loadedChildNode = - loadedNodes.stream().filter(node -> node.getName().equals(CHILD_NAME_1)).findFirst().get(); - Assertions.assertEquals(CHILD_NAME_1, loadedChildNode.getName()); - final Node loadedParentNode1 = - loadedNodes.stream().filter(node -> node.getName().equals(PARENT_NAME_1)).findFirst().get(); - Assertions.assertEquals(PARENT_NAME_1, loadedParentNode1.getName()); - final Node loadedParentNode2 = - loadedNodes.stream().filter(node -> node.getName().equals(PARENT_NAME_2)).findFirst().get(); - Assertions.assertEquals(PARENT_NAME_2, loadedParentNode2.getName()); - - Assertions.assertSame(loadedParentNode1.getChildren().get(0), loadedChildNode); - Assertions.assertSame(loadedChildNode.getChildren().get(0), loadedParentNode2); - } - ); - } - - //@formatter:off - /** - * □-□ - * □-┘ - **/ - //@formatter:on - @Test - void testStoreGraphWithCircularRelationWithTrippleSave() + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void storeGraphWithCircularRelationWithMultiSave(final boolean saveChildNode) { final Node childNode = new Node(CHILD_NAME_1); final Node parentNode1 = new Node(PARENT_NAME_1, List.of(childNode)); @@ -191,7 +143,10 @@ void testStoreGraphWithCircularRelationWithTrippleSave() this.nodeRepository.save(parentNode1); this.nodeRepository.save(parentNode2); - this.nodeRepository.save(childNode); + if(saveChildNode) + { + this.nodeRepository.save(childNode); + } TestUtil.doBeforeAndAfterRestartOfDatastore( this.configuration, @@ -215,12 +170,6 @@ void testStoreGraphWithCircularRelationWithTrippleSave() ); } - //@formatter:off - /** - * □-□ - * □-┘ - **/ - //@formatter:on @Test void testModifyGraph() {