Skip to content

Composite primary keys #134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ hs_err_pid*
.tern-project

# EclipseStore
storage
storage-person
storage-invoice
storage-complex
storage-eclipsestore
spring-data-eclipse-store/storage
spring-data-eclipse-store-demo/storage
spring-data-eclipse-store-demo/storage-person
spring-data-eclipse-store-demo/storage-invoice
spring-data-eclipse-store-demo/storage-complex
spring-data-eclipse-store-jpa/storage-eclipsestore
spring-data-eclipse-store-jpa/storage-h2.mv.db
spring-data-eclipse-store-jpa/storage-h2.trace.db

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 2.1.0

* Implemented auto-id-generation for UUIDs.
* Implemented composite primary keys.

# 2.0.1

* Fix for Issue [#131](https://github.com/xdev-software/spring-data-eclipse-store/issues/131)
Expand Down
6 changes: 3 additions & 3 deletions docs/antora.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: ROOT
title: Spring-Data-Eclipse-Store
version: master
display_version: '2.0.1'
display_version: '2.1.0'
start_page: index.adoc
nav:
- modules/ROOT/nav.adoc
asciidoc:
attributes:
product-name: 'Spring-Data-Eclipse-Store'
display-version: '2.0.1'
maven-version: '2.0.1'
display-version: '2.1.0'
maven-version: '2.1.0'
page-editable: false
page-out-of-support: false
19 changes: 19 additions & 0 deletions docs/modules/ROOT/pages/features/ids.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
= IDs

{product-name} supports the following types with auto generating (``GenerationType.AUTO``) values:

* ``int`` / ``Integer``
* ``long`` / ``Long``
* ``String``
* ``UUID``

Other generation types are currently not supported.

== Composite keys

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.
Most importantly the used class **must have a valid ``hashCode``** since a ``HashMap`` is used to store and manage entities.

{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``.

Multiple Ids for a single entity and https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/idclass[``@IdClass``] are **not** supported.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>software.xdev</groupId>
<artifactId>spring-data-eclipse-store-root</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<organization>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-eclipse-store-benchmark/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>software.xdev</groupId>
<artifactId>spring-data-eclipse-store-root</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
</parent>

<artifactId>spring-data-eclipse-store-benchmark</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-eclipse-store-demo/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>software.xdev</groupId>
<artifactId>spring-data-eclipse-store-root</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
</parent>

<artifactId>spring-data-eclipse-store-demo</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package software.xdev.spring.data.eclipse.store.demo.dual.storage;

import java.io.File;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import software.xdev.spring.data.eclipse.store.demo.TestUtil;
import software.xdev.spring.data.eclipse.store.demo.dual.storage.invoice.PersistenceInvoiceConfiguration;
import software.xdev.spring.data.eclipse.store.demo.dual.storage.person.PersistencePersonConfiguration;


@SpringBootTest(classes = DualStorageDemoApplication.class)
class DualStorageDemoApplicationTest
{
private final PersistenceInvoiceConfiguration invoiceConfiguration;
private final PersistencePersonConfiguration personConfiguration;

@Autowired
public DualStorageDemoApplicationTest(
final PersistenceInvoiceConfiguration invoiceConfiguration,
final PersistencePersonConfiguration personConfiguration)
{
this.invoiceConfiguration = invoiceConfiguration;
this.personConfiguration = personConfiguration;
}

@BeforeAll
static void clearPreviousData()
{
TestUtil.deleteDirectory(new File("./" + PersistenceInvoiceConfiguration.STORAGE_PATH));
TestUtil.deleteDirectory(new File("./" + PersistencePersonConfiguration.STORAGE_PATH));
}

@Test
void checkPossibilityToSimplyStartAndRestartApplication()
{
this.invoiceConfiguration.getStorageInstance().stop();
this.personConfiguration.getStorageInstance().stop();
DualStorageDemoApplication.main(new String[]{});
}
}
2 changes: 1 addition & 1 deletion spring-data-eclipse-store-jpa/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>software.xdev</groupId>
<artifactId>spring-data-eclipse-store-root</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
</parent>

<artifactId>spring-data-eclipse-store-jpa</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/
package software.xdev.spring.data.eclipse.store.exceptions;

public class IdFieldFinalException extends RuntimeException
public class IdFieldException extends RuntimeException
{
public IdFieldFinalException(final String message)
public IdFieldException(final String message)
{
super(message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Id;
import jakarta.persistence.Version;

import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException;
import software.xdev.spring.data.eclipse.store.exceptions.InvalidVersionException;
import software.xdev.spring.data.eclipse.store.repository.access.AccessHelper;


Expand All @@ -31,17 +38,34 @@ private AnnotatedFieldFinder()
}

/**
* Finds any field in a class with an ID-Annotation ({@link jakarta.persistence.Id} or
* {@link org.springframework.data.annotation.Id}). Finds this field recursively in the Hierarchy-tree.
* Finds any field in a class with an ID-Annotation ({@link jakarta.persistence.Id},
* {@link org.springframework.data.annotation.Id} or {@link jakarta.persistence.EmbeddedId}). Finds this field
* recursively in the Hierarchy-tree.
*
* @return field with ID-Annotation. Is {@link Optional#empty()} if no field was found.
*/
public static Optional<Field> findIdField(final Class<?> domainClass)
{
return findAnnotatedField(
final List<Field> idFields = findAnnotatedFields(
domainClass,
List.of(jakarta.persistence.Id.class, org.springframework.data.annotation.Id.class)
List.of(
Id.class,
org.springframework.data.annotation.Id.class,
EmbeddedId.class)
);

if(idFields.isEmpty())
{
return Optional.empty();
}
else
{
if(idFields.size() > 1)
{
throw new IdFieldException("Only one id field is allowed");
}
return Optional.of(idFields.get(0));
}
}

/**
Expand All @@ -52,32 +76,46 @@ public static Optional<Field> findIdField(final Class<?> domainClass)
*/
public static Optional<Field> findVersionField(final Class<?> domainClass)
{
return findAnnotatedField(
final List<Field> versionFields = findAnnotatedFields(
domainClass,
List.of(jakarta.persistence.Version.class, org.springframework.data.annotation.Version.class)
List.of(Version.class, org.springframework.data.annotation.Version.class)
);

if(versionFields.isEmpty())
{
return Optional.empty();
}
else
{
if(versionFields.size() > 1)
{
throw new InvalidVersionException("Only one version field is allowed");
}
return Optional.of(versionFields.get(0));
}
}

/**
* Finds any field in a class with specified annotations. Finds this field recursively in the Hierarchy-tree.
*
* @return field with annotation. Is {@link Optional#empty()} if no field was found.
* @return fields with annotation.
*/
public static Optional<Field> findAnnotatedField(
public static List<Field> findAnnotatedFields(
final Class<?> domainClass,
final Collection<Class<? extends Annotation>> annotations)
{
final ArrayList<Field> foundFields = new ArrayList<>();
final Collection<Field> classFields = AccessHelper.getInheritedPrivateFieldsByName(domainClass).values();
for(final Field currentField : classFields)
{
for(final Class<? extends Annotation> annotation : annotations)
{
if(currentField.getAnnotationsByType(annotation).length > 0)
{
return Optional.of(currentField);
foundFields.add(currentField);
}
}
}
return Optional.empty();
return foundFields;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import java.lang.reflect.Modifier;

import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException;
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldFinalException;
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException;
import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier;
import software.xdev.spring.data.eclipse.store.repository.support.copier.version.incrementer.VersionIncrementer;

Expand All @@ -43,7 +43,7 @@ private void checkIfVersionFieldIsFinal()
final int fieldModifiers = this.versionField.getModifiers();
if(Modifier.isFinal(fieldModifiers))
{
throw new IdFieldFinalException(String.format(
throw new IdFieldException(String.format(
"Field %s is final and cannot be modified. Version fields must not be final.",
this.versionField.getName()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import java.util.function.Consumer;

import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException;
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldFinalException;
import software.xdev.spring.data.eclipse.store.exceptions.IdFieldException;
import software.xdev.spring.data.eclipse.store.repository.access.modifier.FieldAccessModifier;
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.IdFinder;

Expand All @@ -45,7 +45,7 @@ private void checkIfIdFieldIsFinal()
final int fieldModifiers = this.idField.getModifiers();
if(Modifier.isFinal(fieldModifiers))
{
throw new IdFieldFinalException(String.format(
throw new IdFieldException(String.format(
"Field %s is final and cannot be modified. ID fields must not be final.",
this.idField.getName()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.lang.reflect.Field;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Supplier;

import jakarta.persistence.GeneratedValue;
Expand All @@ -26,6 +27,7 @@
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoIntegerIdFinder;
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoLongIdFinder;
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoStringIdFinder;
import software.xdev.spring.data.eclipse.store.repository.support.id.strategy.auto.AutoUUIDIdFinder;


/**
Expand Down Expand Up @@ -56,6 +58,10 @@ else if(Long.class.isAssignableFrom(idField.getType()) || long.class.isAssignabl
{
return (IdFinder<ID>)new AutoLongIdFinder(lastIdGetter);
}
else if(idField.getType().equals(UUID.class))
{
return (IdFinder<ID>)new AutoUUIDIdFinder(lastIdGetter);
}
}
throw new IdGeneratorNotSupportedException(String.format(
"Id generator with strategy %s for type %s is not supported.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.id.strategy.auto;

import java.util.UUID;
import java.util.function.Supplier;


public class AutoUUIDIdFinder extends AbstractAutoIdFinder<UUID>
{
public AutoUUIDIdFinder(final Supplier<Object> idGetter)
{
super(() -> (UUID)idGetter.get());
}

@Override
protected UUID getNext(final UUID oldId)
{
return UUID.randomUUID();
}

@Override
public UUID getDefaultValue()
{
return null;
}
}
Loading