Skip to content

Micro migration for developer #219

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
Jan 8, 2025
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
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));
}
----
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
Loading