Skip to content

Commit 522804b

Browse files
Merge pull request #134 from xdev-software/composite-primary-keys
Composite primary keys
2 parents d994a2d + 1acd1b0 commit 522804b

File tree

30 files changed

+1068
-53
lines changed

30 files changed

+1068
-53
lines changed

.gitignore

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,12 @@ hs_err_pid*
6262
.tern-project
6363

6464
# EclipseStore
65-
storage
66-
storage-person
67-
storage-invoice
68-
storage-complex
69-
storage-eclipsestore
65+
spring-data-eclipse-store/storage
66+
spring-data-eclipse-store-demo/storage
67+
spring-data-eclipse-store-demo/storage-person
68+
spring-data-eclipse-store-demo/storage-invoice
69+
spring-data-eclipse-store-demo/storage-complex
70+
spring-data-eclipse-store-jpa/storage-eclipsestore
7071
spring-data-eclipse-store-jpa/storage-h2.mv.db
7172
spring-data-eclipse-store-jpa/storage-h2.trace.db
7273

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 2.1.0
2+
3+
* Implemented auto-id-generation for UUIDs.
4+
* Implemented composite primary keys.
5+
16
# 2.0.1
27

38
* Fix for Issue [#131](https://github.com/xdev-software/spring-data-eclipse-store/issues/131)

docs/antora.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
name: ROOT
22
title: Spring-Data-Eclipse-Store
33
version: master
4-
display_version: '2.0.1'
4+
display_version: '2.1.0'
55
start_page: index.adoc
66
nav:
77
- modules/ROOT/nav.adoc
88
asciidoc:
99
attributes:
1010
product-name: 'Spring-Data-Eclipse-Store'
11-
display-version: '2.0.1'
12-
maven-version: '2.0.1'
11+
display-version: '2.1.0'
12+
maven-version: '2.1.0'
1313
page-editable: false
1414
page-out-of-support: false
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
= IDs
2+
3+
{product-name} supports the following types with auto generating (``GenerationType.AUTO``) values:
4+
5+
* ``int`` / ``Integer``
6+
* ``long`` / ``Long``
7+
* ``String``
8+
* ``UUID``
9+
10+
Other generation types are currently not supported.
11+
12+
== Composite keys
13+
14+
It is possible to use **any class as https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/id[``@Id``]** but without any auto generation.
15+
Most importantly the used class **must have a valid ``hashCode``** since a ``HashMap`` is used to store and manage entities.
16+
17+
{product-name} can also handle https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/embeddedid[``@EmbeddedId``] which results in the same behavior as ``@Id`` but the id-class must then implement ``Serializable``.
18+
19+
Multiple Ids for a single entity and https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/idclass[``@IdClass``] are **not** supported.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>software.xdev</groupId>
88
<artifactId>spring-data-eclipse-store-root</artifactId>
9-
<version>2.0.2-SNAPSHOT</version>
9+
<version>2.1.0-SNAPSHOT</version>
1010
<packaging>pom</packaging>
1111

1212
<organization>

spring-data-eclipse-store-benchmark/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>software.xdev</groupId>
77
<artifactId>spring-data-eclipse-store-root</artifactId>
8-
<version>2.0.2-SNAPSHOT</version>
8+
<version>2.1.0-SNAPSHOT</version>
99
</parent>
1010

1111
<artifactId>spring-data-eclipse-store-benchmark</artifactId>

spring-data-eclipse-store-demo/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>software.xdev</groupId>
99
<artifactId>spring-data-eclipse-store-root</artifactId>
10-
<version>2.0.2-SNAPSHOT</version>
10+
<version>2.1.0-SNAPSHOT</version>
1111
</parent>
1212

1313
<artifactId>spring-data-eclipse-store-demo</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package software.xdev.spring.data.eclipse.store.demo.dual.storage;
2+
3+
import java.io.File;
4+
5+
import org.junit.jupiter.api.BeforeAll;
6+
import org.junit.jupiter.api.Test;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.boot.test.context.SpringBootTest;
9+
10+
import software.xdev.spring.data.eclipse.store.demo.TestUtil;
11+
import software.xdev.spring.data.eclipse.store.demo.dual.storage.invoice.PersistenceInvoiceConfiguration;
12+
import software.xdev.spring.data.eclipse.store.demo.dual.storage.person.PersistencePersonConfiguration;
13+
14+
15+
@SpringBootTest(classes = DualStorageDemoApplication.class)
16+
class DualStorageDemoApplicationTest
17+
{
18+
private final PersistenceInvoiceConfiguration invoiceConfiguration;
19+
private final PersistencePersonConfiguration personConfiguration;
20+
21+
@Autowired
22+
public DualStorageDemoApplicationTest(
23+
final PersistenceInvoiceConfiguration invoiceConfiguration,
24+
final PersistencePersonConfiguration personConfiguration)
25+
{
26+
this.invoiceConfiguration = invoiceConfiguration;
27+
this.personConfiguration = personConfiguration;
28+
}
29+
30+
@BeforeAll
31+
static void clearPreviousData()
32+
{
33+
TestUtil.deleteDirectory(new File("./" + PersistenceInvoiceConfiguration.STORAGE_PATH));
34+
TestUtil.deleteDirectory(new File("./" + PersistencePersonConfiguration.STORAGE_PATH));
35+
}
36+
37+
@Test
38+
void checkPossibilityToSimplyStartAndRestartApplication()
39+
{
40+
this.invoiceConfiguration.getStorageInstance().stop();
41+
this.personConfiguration.getStorageInstance().stop();
42+
DualStorageDemoApplication.main(new String[]{});
43+
}
44+
}

spring-data-eclipse-store-jpa/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>software.xdev</groupId>
99
<artifactId>spring-data-eclipse-store-root</artifactId>
10-
<version>2.0.2-SNAPSHOT</version>
10+
<version>2.1.0-SNAPSHOT</version>
1111
</parent>
1212

1313
<artifactId>spring-data-eclipse-store-jpa</artifactId>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
*/
1616
package software.xdev.spring.data.eclipse.store.exceptions;
1717

18-
public class IdFieldFinalException extends RuntimeException
18+
public class IdFieldException extends RuntimeException
1919
{
20-
public IdFieldFinalException(final String message)
20+
public IdFieldException(final String message)
2121
{
2222
super(message);
2323
}

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/AnnotatedFieldFinder.java

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,17 @@
1717

1818
import java.lang.annotation.Annotation;
1919
import java.lang.reflect.Field;
20+
import java.util.ArrayList;
2021
import java.util.Collection;
2122
import java.util.List;
2223
import java.util.Optional;
2324

25+
import jakarta.persistence.EmbeddedId;
26+
import jakarta.persistence.Id;
27+
import jakarta.persistence.Version;
28+
29+
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException;
30+
import software.xdev.spring.data.eclipse.store.exceptions.InvalidVersionException;
2431
import software.xdev.spring.data.eclipse.store.repository.access.AccessHelper;
2532

2633

@@ -31,17 +38,34 @@ private AnnotatedFieldFinder()
3138
}
3239

3340
/**
34-
* Finds any field in a class with an ID-Annotation ({@link jakarta.persistence.Id} or
35-
* {@link org.springframework.data.annotation.Id}). Finds this field recursively in the Hierarchy-tree.
41+
* Finds any field in a class with an ID-Annotation ({@link jakarta.persistence.Id},
42+
* {@link org.springframework.data.annotation.Id} or {@link jakarta.persistence.EmbeddedId}). Finds this field
43+
* recursively in the Hierarchy-tree.
3644
*
3745
* @return field with ID-Annotation. Is {@link Optional#empty()} if no field was found.
3846
*/
3947
public static Optional<Field> findIdField(final Class<?> domainClass)
4048
{
41-
return findAnnotatedField(
49+
final List<Field> idFields = findAnnotatedFields(
4250
domainClass,
43-
List.of(jakarta.persistence.Id.class, org.springframework.data.annotation.Id.class)
51+
List.of(
52+
Id.class,
53+
org.springframework.data.annotation.Id.class,
54+
EmbeddedId.class)
4455
);
56+
57+
if(idFields.isEmpty())
58+
{
59+
return Optional.empty();
60+
}
61+
else
62+
{
63+
if(idFields.size() > 1)
64+
{
65+
throw new IdFieldException("Only one id field is allowed");
66+
}
67+
return Optional.of(idFields.get(0));
68+
}
4569
}
4670

4771
/**
@@ -52,32 +76,46 @@ public static Optional<Field> findIdField(final Class<?> domainClass)
5276
*/
5377
public static Optional<Field> findVersionField(final Class<?> domainClass)
5478
{
55-
return findAnnotatedField(
79+
final List<Field> versionFields = findAnnotatedFields(
5680
domainClass,
57-
List.of(jakarta.persistence.Version.class, org.springframework.data.annotation.Version.class)
81+
List.of(Version.class, org.springframework.data.annotation.Version.class)
5882
);
83+
84+
if(versionFields.isEmpty())
85+
{
86+
return Optional.empty();
87+
}
88+
else
89+
{
90+
if(versionFields.size() > 1)
91+
{
92+
throw new InvalidVersionException("Only one version field is allowed");
93+
}
94+
return Optional.of(versionFields.get(0));
95+
}
5996
}
6097

6198
/**
6299
* Finds any field in a class with specified annotations. Finds this field recursively in the Hierarchy-tree.
63100
*
64-
* @return field with annotation. Is {@link Optional#empty()} if no field was found.
101+
* @return fields with annotation.
65102
*/
66-
public static Optional<Field> findAnnotatedField(
103+
public static List<Field> findAnnotatedFields(
67104
final Class<?> domainClass,
68105
final Collection<Class<? extends Annotation>> annotations)
69106
{
107+
final ArrayList<Field> foundFields = new ArrayList<>();
70108
final Collection<Field> classFields = AccessHelper.getInheritedPrivateFieldsByName(domainClass).values();
71109
for(final Field currentField : classFields)
72110
{
73111
for(final Class<? extends Annotation> annotation : annotations)
74112
{
75113
if(currentField.getAnnotationsByType(annotation).length > 0)
76114
{
77-
return Optional.of(currentField);
115+
foundFields.add(currentField);
78116
}
79117
}
80118
}
81-
return Optional.empty();
119+
return foundFields;
82120
}
83121
}

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/copier/version/SimpleEntityVersionIncrementer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.lang.reflect.Modifier;
2020

2121
import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException;
22-
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldFinalException;
22+
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException;
2323
import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier;
2424
import software.xdev.spring.data.eclipse.store.repository.support.copier.version.incrementer.VersionIncrementer;
2525

@@ -43,7 +43,7 @@ private void checkIfVersionFieldIsFinal()
4343
final int fieldModifiers = this.versionField.getModifiers();
4444
if(Modifier.isFinal(fieldModifiers))
4545
{
46-
throw new IdFieldFinalException(String.format(
46+
throw new IdFieldException(String.format(
4747
"Field %s is final and cannot be modified. Version fields must not be final.",
4848
this.versionField.getName()));
4949
}

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/SimpleIdSetter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.util.function.Consumer;
2121

2222
import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException;
23-
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldFinalException;
23+
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException;
2424
import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier;
2525
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.IdFinder;
2626

@@ -45,7 +45,7 @@ private void checkIfIdFieldIsFinal()
4545
final int fieldModifiers = this.idField.getModifiers();
4646
if(Modifier.isFinal(fieldModifiers))
4747
{
48-
throw new IdFieldFinalException(String.format(
48+
throw new IdFieldException(String.format(
4949
"Field %s is final and cannot be modified. ID fields must not be final.",
5050
this.idField.getName()));
5151
}

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/support/id/strategy/IdFinder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.lang.reflect.Field;
1919
import java.util.Objects;
20+
import java.util.UUID;
2021
import java.util.function.Supplier;
2122

2223
import jakarta.persistence.GeneratedValue;
@@ -26,6 +27,7 @@
2627
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoIntegerIdFinder;
2728
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoLongIdFinder;
2829
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoStringIdFinder;
30+
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoUUIDIdFinder;
2931

3032

3133
/**
@@ -56,6 +58,10 @@ else if(Long.class.isAssignableFrom(idField.getType()) || long.class.isAssignabl
5658
{
5759
return (IdFinder<ID>)new AutoLongIdFinder(lastIdGetter);
5860
}
61+
else if(idField.getType().equals(UUID.class))
62+
{
63+
return (IdFinder<ID>)new AutoUUIDIdFinder(lastIdGetter);
64+
}
5965
}
6066
throw new IdGeneratorNotSupportedException(String.format(
6167
"Id generator with strategy %s for type %s is not supported.",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto;
17+
18+
import java.util.UUID;
19+
import java.util.function.Supplier;
20+
21+
22+
public class AutoUUIDIdFinder extends AbstractAutoIdFinder<UUID>
23+
{
24+
public AutoUUIDIdFinder(final Supplier<Object> idGetter)
25+
{
26+
super(() -> (UUID)idGetter.get());
27+
}
28+
29+
@Override
30+
protected UUID getNext(final UUID oldId)
31+
{
32+
return UUID.randomUUID();
33+
}
34+
35+
@Override
36+
public UUID getDefaultValue()
37+
{
38+
return null;
39+
}
40+
}

0 commit comments

Comments
 (0)