Skip to content

Commit

Permalink
Merge pull request #219 from xdev-software/micro-migration-for-developer
Browse files Browse the repository at this point in the history
Micro migration for developer
  • Loading branch information
JohannesRabauer authored Jan 8, 2025
2 parents 851ddbf + 6db8d8c commit 0e80868
Show file tree
Hide file tree
Showing 44 changed files with 1,345 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 2.4.2

* Updated org.springframework.boot.version to v3.4.1
* Added support for the [micro-migration-Framework](https://github.com/xdev-software/micro-migration)

# 2.4.1

Expand Down
3 changes: 2 additions & 1 deletion docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
** xref:features/queries.adoc[Queries]
** xref:features/transactions.adoc[Transactions]
** xref:features/versions.adoc[Versions]
** xref:features/versioned-migration.adoc[Versioned Migration]
** xref:features/rest-api.adoc[REST Interface]
** xref:features/validation-constraints.adoc[Validation Constraints]
* xref:migration.adoc[Migration from JPA]
* xref:migration-from-jpa.adoc[Migration from JPA]
* xref:known-issues.adoc[Known issues]
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/features/features.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
* xref:features/queries.adoc[Queries]
* xref:features/transactions.adoc[Transactions]
* xref:features/versions.adoc[Versions]
* xref:features/versioned-migration.adoc[Versioned Migration]
* xref:features/rest-api.adoc[REST Interface]
* xref:features/validation-constraints.adoc[Validation Constraints]
116 changes: 116 additions & 0 deletions docs/modules/ROOT/pages/features/versioned-migration.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
= Versioned Migration

To keep the data in the store up-to-date, {product-name} utilizes https://github.com/xdev-software/micro-migration[XDEV's Micro-Migration].
This means the user can use versioning for the stored data and only apply changes for certain versions of data.
This can be very useful specifically with build-pipelines. https://github.com/xdev-software/micro-migration#intro[More info at Micro-Migration...]

== Implementation

This can be easily achieved by either of these 3 methods:

=== 1. Reflective Scripts

Simply implement a new component with a specific pattern of naming, that extends the ``ReflectiveDataMigrationScript``.

[source,java,title="https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/migration/v1_0_0_Init.java[Reflective example from complex demo]"]
----
package software.xdev.spring.data.eclipse.store.demo.complex.migration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//...
import software.xdev.spring.data.eclipse.store.repository.root.data.version.ReflectiveDataMigrationScript;
@Component
public class v1_0_0_Init extends ReflectiveDataMigrationScript
{
private final OwnerService service;
@Autowired
public v1_0_0_Init(final OwnerService service)
{
this.service = service;
}
@Override
public void migrate(final Context<VersionedRoot, MigrationEmbeddedStorageManager> context)
{
this.service.createNewOwnerAndVisit("Mick", "Fleetwood", "Isabella");
}
}
----

Here the version number on which the data is updated on execution is derived from the class name.

The ``MigrationVersion`` is stored in the root object in the data store.
Therefore, the storage always knows on which version the current data is and the ``DataMigrater`` will only execute the newer scripts.

The scripts are automatically registered by declaring them as ``@Component``s.
That means that they can be anywhere as long as they are discovered by Spring as a component.

=== 2. Custom Scripts

Implementing a script without special naming is possible by implementing the
``DataMigrationScript``.

[source,java,title="https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/migration/CustomNameScript.java[Custom script example from complex demo]"]
----
package software.xdev.spring.data.eclipse.store.demo.complex.migration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//...
import software.xdev.spring.data.eclipse.store.repository.root.data.version.DataMigrationScript;
@Component
public class CustomNameScriptAddOwner implements DataMigrationScript
{
private final OwnerService service;
public CustomNameScriptAddOwner(@Autowired final OwnerService service)
{
this.service = service;
}
@Override
public MigrationVersion getTargetVersion()
{
return new MigrationVersion(1, 1, 0);
}
@Override
public void migrate(final Context<VersionedRoot, MigrationEmbeddedStorageManager> context)
{
this.service.createNewOwnerAndVisit("John", "McVie", "Ivan");
}
}
----

The version number must be returned explicitly in the ``#getTargetVersion``-method.

=== 3. Custom Migrater

If more customization is needed it is also possible to replace the ``DataMigrater`` completely and implement your own ``MicroMigrater``.
This should only be used if necessary since it adds a lot of complexity to the code.

[source,java,title="https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/data/migration/with/migrater/CustomMigrater.java[Custom migrater from tests]"]
----
package software.xdev.spring.data.eclipse.store.integration.isolated.tests.data.migration.with.migrater;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import software.xdev.micromigration.migrater.ExplicitMigrater;
import software.xdev.micromigration.migrater.MicroMigrater;
//...
@Component
public class CustomMigrater implements MicroMigrater
{
private final ExplicitMigrater explicitMigrater;
@Autowired
public CustomMigrater(final PersistedEntityRepository repository)
{
this.explicitMigrater = new ExplicitMigrater(new v1_0_0_Init(repository));
}
----
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ public void run(final String... args)
*/
private void ownerCalls()
{
this.ownerService.logOwners();
this.ownerService.deleteAll();
this.ownerService.logOwners();
this.ownerService.createNewOwnerAndVisit();
this.ownerService.createNewOwnerAndVisit("Stevie", "Nicks", "Peter");
this.ownerService.logOwnersAndVisits();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,16 @@ public void logOwnersAndVisits()
/**
* Transactional
*/
public void createNewOwnerAndVisit()
public void createNewOwnerAndVisit(final String ownerFirstName, final String ownerLastName, final String petName)
{
new TransactionTemplate(this.transactionManager).execute(
status ->
{
final Owner owner = this.createOwner();
final Owner owner = this.createOwner(ownerFirstName, ownerLastName, petName);
this.ownerRepository.save(owner);

final Visit visit = this.createVisit();
owner.addVisit("Peter", visit);
owner.addVisit(petName, visit);
this.ownerRepository.save(owner);
LOG.info("----Stored new owner and visit----");
return null;
Expand All @@ -113,14 +113,14 @@ private Visit createVisit()
}

@SuppressWarnings("checkstyle:MagicNumber")
private Owner createOwner()
private Owner createOwner(final String ownerFirstName, final String ownerLastName, final String petName)
{
final Owner owner = new Owner();
owner.setFirstName("Stevie");
owner.setLastName("Nicks");
owner.setFirstName(ownerFirstName);
owner.setLastName(ownerLastName);
final Pet pet = new Pet();
pet.setBirthDate(LocalDate.now().minusWeeks(6));
pet.setName("Peter");
pet.setName(petName);
final PetType petType = new PetType();
petType.setName("Dog");
pet.setType(petType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.demo.complex.migration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import software.xdev.micromigration.eclipsestore.MigrationEmbeddedStorageManager;
import software.xdev.micromigration.scripts.Context;
import software.xdev.micromigration.version.MigrationVersion;
import software.xdev.spring.data.eclipse.store.demo.complex.OwnerService;
import software.xdev.spring.data.eclipse.store.repository.root.VersionedRoot;
import software.xdev.spring.data.eclipse.store.repository.root.data.version.DataMigrationScript;


/**
* This is automatically called by the
* {@link software.xdev.spring.data.eclipse.store.repository.root.data.version.DataMigrater} through dependency
* injection.
* <p>
* In contrast to {@link v1_0_0_Init} the version of this script is defined in the method {@link #getTargetVersion()}.
*/
@Component
public class CustomNameScript implements DataMigrationScript
{
private final OwnerService service;

public CustomNameScript(@Autowired final OwnerService service)
{
this.service = service;
}

@Override
public MigrationVersion getTargetVersion()
{
return new MigrationVersion(1, 1, 0);
}

@Override
public void migrate(final Context<VersionedRoot, MigrationEmbeddedStorageManager> context)
{
this.service.createNewOwnerAndVisit("John", "McVie", "Ivan");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.demo.complex.migration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import software.xdev.micromigration.eclipsestore.MigrationEmbeddedStorageManager;
import software.xdev.micromigration.scripts.Context;
import software.xdev.spring.data.eclipse.store.demo.complex.OwnerService;
import software.xdev.spring.data.eclipse.store.repository.root.VersionedRoot;
import software.xdev.spring.data.eclipse.store.repository.root.data.version.ReflectiveDataMigrationScript;


/**
* This is automatically called by the
* {@link software.xdev.spring.data.eclipse.store.repository.root.data.version.DataMigrater} through dependency
* injection.
* <p>
* In contrast to {@link CustomNameScript} the version of this script is defined by
* <b>the name of the class defines the version</b>.
*/
@SuppressWarnings("checkstyle:TypeName")
@Component
public class v1_0_0_Init extends ReflectiveDataMigrationScript
{
private final OwnerService service;

@Autowired
public v1_0_0_Init(final OwnerService service)
{
this.service = service;
}

@Override
public void migrate(final Context<VersionedRoot, MigrationEmbeddedStorageManager> context)
{
this.service.createNewOwnerAndVisit("Mick", "Fleetwood", "Isabella");
}
}
3 changes: 2 additions & 1 deletion spring-data-eclipse-store/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<jakarta.el-api.version>6.0.1</jakarta.el-api.version>
<expressly.version>6.0.0-M1</expressly.version>
<hibernate-core.version>6.6.4.Final</hibernate-core.version>
<micro-migration.version>3.0.1</micro-migration.version>
</properties>

<repositories>
Expand Down Expand Up @@ -156,7 +157,7 @@
<dependency>
<groupId>software.xdev</groupId>
<artifactId>micro-migration</artifactId>
<version>2.0.0</version>
<version>${micro-migration.version}</version>
<exclusions>
<exclusion>
<artifactId>storage-embedded</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
import software.xdev.spring.data.eclipse.store.repository.config.EclipseStoreClientConfiguration;
import software.xdev.spring.data.eclipse.store.repository.config.EclipseStoreStorageFoundationProvider;
import software.xdev.spring.data.eclipse.store.repository.interfaces.EclipseStoreRepository;
import software.xdev.spring.data.eclipse.store.repository.root.EclipseStoreMigrator;
import software.xdev.spring.data.eclipse.store.repository.root.VersionedRoot;
import software.xdev.spring.data.eclipse.store.repository.root.data.version.DataVersion;
import software.xdev.spring.data.eclipse.store.repository.root.v2_4.EntityData;
import software.xdev.spring.data.eclipse.store.repository.support.SimpleEclipseStoreRepository;
import software.xdev.spring.data.eclipse.store.repository.support.concurrency.ReadWriteLock;
Expand Down Expand Up @@ -92,7 +94,7 @@ public EclipseStoreStorage(final EclipseStoreClientConfiguration storeConfigurat
this.classLoaderProvider = storeConfiguration.getClassLoaderProvider();
}

public StorageManager getInstanceOfStorageManager()
public EmbeddedStorageManager getInstanceOfStorageManager()
{
this.ensureEntitiesInRoot();
return this.storageManager;
Expand Down Expand Up @@ -120,7 +122,7 @@ private synchronized void ensureEntitiesInRoot()
this.root.getCurrentRootData().getEntityTypesCount(),
this.root.getCurrentRootData().getEntityCount()
);
EclipseStoreMigrator.migrate(this.root, this.storageManager);
EclipseStoreMigrator.migrateStructure(this.root, this.storageManager);
}
}

Expand Down Expand Up @@ -492,6 +494,11 @@ public Object getObject(final long objectId)
return this.storageManager.getObject(objectId);
}

public DataVersion getDataVersion()
{
return this.getRoot().getDataVersion();
}

@Override
public ReadWriteLock getReadWriteLock()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@


/**
* This is the root object for all versions <2.0.0 and is used for upgrading to the new root.
* @deprecated should not be initialised any more. Version for <2.0.0
* This is the root object for all versions {@literal <}2.0.0 and is used for upgrading to the new root.
* @deprecated should not be initialised any more. Version for {@literal <}2.0.0
*/
@Deprecated(forRemoval = false, since = "2.0.0")
public class Root
Expand Down
Loading

0 comments on commit 0e80868

Please sign in to comment.