From 51f83b4501d04445066d9caf8ae9628fecd9b906 Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Wed, 20 Apr 2022 22:28:28 +0200
Subject: [PATCH] #663 - added support for mariadb

---
 BudgetMasterDatabaseMigrator/pom.xml          |  4 +
 .../databasemigrator/CommandLineOptions.java  |  3 +-
 .../src/main/resources/application.properties |  2 +-
 BudgetMasterServer/pom.xml                    |  5 ++
 .../budgetmaster/migration/DatabaseType.java  | 39 ++++++++
 .../migration/MigrationArguments.java         | 14 ++-
 .../migration/MigrationController.java        |  8 +-
 .../migration/MigrationService.java           |  2 +-
 .../migration/MigrationSettings.java          | 88 ++++++++++++++++++-
 .../utils/DatabaseConfiguration.java          |  2 +-
 .../DatabaseConfigurationProperties.java      |  6 ++
 .../resources/languages/base_de.properties    |  3 +-
 .../resources/languages/base_en.properties    |  3 +-
 .../templates/migration/migration.ftl         | 16 ++++
 14 files changed, 183 insertions(+), 12 deletions(-)
 create mode 100644 BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/DatabaseType.java

diff --git a/BudgetMasterDatabaseMigrator/pom.xml b/BudgetMasterDatabaseMigrator/pom.xml
index 5e98177fa..e3603b286 100644
--- a/BudgetMasterDatabaseMigrator/pom.xml
+++ b/BudgetMasterDatabaseMigrator/pom.xml
@@ -41,6 +41,10 @@
             <groupId>org.postgresql</groupId>
             <artifactId>postgresql</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.mariadb.jdbc</groupId>
+            <artifactId>mariadb-java-client</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.projectlombok</groupId>
diff --git a/BudgetMasterDatabaseMigrator/src/main/java/de/deadlocker8/budgetmaster/databasemigrator/CommandLineOptions.java b/BudgetMasterDatabaseMigrator/src/main/java/de/deadlocker8/budgetmaster/databasemigrator/CommandLineOptions.java
index 966fa806d..88746d544 100644
--- a/BudgetMasterDatabaseMigrator/src/main/java/de/deadlocker8/budgetmaster/databasemigrator/CommandLineOptions.java
+++ b/BudgetMasterDatabaseMigrator/src/main/java/de/deadlocker8/budgetmaster/databasemigrator/CommandLineOptions.java
@@ -3,7 +3,8 @@ package de.deadlocker8.budgetmaster.databasemigrator;
 public enum CommandLineOptions
 {
 	SOURCE_URL("spring.datasource.jdbc-url", "source h2 database JDBC url, e.g. jdbc:h2:/C:/Users/Admin/AppData/Roaming/Deadlocker/BudgetMaster/debug/budgetmaster"),
-	DESTINATION_URL("spring.seconddatasource.jdbc-url", "destination postresql database JDBC url, e.g. jdbc:postgresql://localhost:5432/budgetmaster"),
+	DESTINATION_URL("spring.seconddatasource.jdbc-url", "destination database JDBC url (postgres ir mariadb), e.g. jdbc:postgresql://localhost:5432/budgetmaster"),
+	DESTINATION_DRIVER_CLASS_NAME("spring.seconddatasource.driver-class-name", "destination database driver class name, e.g. org.postgresql.Driver or org.mariadb.jdbc.Driver"),
 	DESTINATION_USER_NAME("spring.seconddatasource.username", "destination postresql user name, e.g. budgetmaster"),
 	DESTINATION_PASSWORD("spring.seconddatasource.password", "destination postresql password, e.g. BudgetMaster");
 
diff --git a/BudgetMasterDatabaseMigrator/src/main/resources/application.properties b/BudgetMasterDatabaseMigrator/src/main/resources/application.properties
index 8f534251f..8c007825b 100644
--- a/BudgetMasterDatabaseMigrator/src/main/resources/application.properties
+++ b/BudgetMasterDatabaseMigrator/src/main/resources/application.properties
@@ -7,7 +7,7 @@ spring.datasource.driver-class-name=org.h2.Driver
 #spring.seconddatasource.jdbc-url=
 #spring.seconddatasource.username=
 #spring.seconddatasource.password=
-spring.seconddatasource.driver-class-name=org.postgresql.Driver
+#spring.seconddatasource.driver-class-name=
 
 spring.jpa.database=default
 spring.jpa.show-sql=false
diff --git a/BudgetMasterServer/pom.xml b/BudgetMasterServer/pom.xml
index 2aee70c85..4425e571d 100644
--- a/BudgetMasterServer/pom.xml
+++ b/BudgetMasterServer/pom.xml
@@ -110,6 +110,11 @@
             <artifactId>postgresql</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.mariadb.jdbc</groupId>
+            <artifactId>mariadb-java-client</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>com.itextpdf</groupId>
             <artifactId>itextpdf</artifactId>
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/DatabaseType.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/DatabaseType.java
new file mode 100644
index 000000000..df5e54567
--- /dev/null
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/DatabaseType.java
@@ -0,0 +1,39 @@
+package de.deadlocker8.budgetmaster.migration;
+
+public enum DatabaseType
+{
+	POSTGRESQL("PostgreSQL", "org.postgresql.Driver"),
+	MARIADB("MariaDB", "org.mariadb.jdbc.Driver");
+
+	private final String name;
+	private final String driverClassName;
+
+	DatabaseType(String name, String driverClassName)
+	{
+		this.name = name;
+		this.driverClassName = driverClassName;
+	}
+
+	public String getName()
+	{
+		return name;
+	}
+
+	public String getDriverClassName()
+	{
+		return driverClassName;
+	}
+
+	public static DatabaseType fromName(String name)
+	{
+		for(DatabaseType type : values())
+		{
+			if(type.getName().equalsIgnoreCase(name))
+			{
+				return type;
+			}
+		}
+
+		return null;
+	}
+}
\ No newline at end of file
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationArguments.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationArguments.java
index b19d71f50..a105cf0af 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationArguments.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationArguments.java
@@ -3,6 +3,7 @@ package de.deadlocker8.budgetmaster.migration;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 public class MigrationArguments
 {
@@ -10,6 +11,7 @@ public class MigrationArguments
 	{
 		private String sourceUrl;
 		private String destinationUrl;
+		private String destinationDriverClassName;
 		private String destinationUsername;
 		private String destinationPassword;
 
@@ -20,9 +22,10 @@ public class MigrationArguments
 			return this;
 		}
 
-		public MigrationArgumentBuilder withDestinationUrl(String hostname, Integer port, String databaseName)
+		public MigrationArgumentBuilder withDestinationUrl(DatabaseType databaseType, String hostname, Integer port, String databaseName)
 		{
-			this.destinationUrl = MessageFormat.format("jdbc:postgresql://{0}:{1,number,#}/{2}", hostname, port, databaseName);
+			this.destinationDriverClassName = databaseType.getDriverClassName();
+			this.destinationUrl = MessageFormat.format("jdbc:{0}://{1}:{2,number,#}/{3}", databaseType.getName().toLowerCase(), hostname, port, databaseName);
 			return this;
 		}
 
@@ -35,19 +38,21 @@ public class MigrationArguments
 
 		public MigrationArguments build()
 		{
-			return new MigrationArguments(sourceUrl, destinationUrl, destinationUsername, destinationPassword);
+			return new MigrationArguments(sourceUrl, destinationUrl, destinationDriverClassName, destinationUsername, destinationPassword);
 		}
 	}
 
 	private final String sourceUrl;
 	private final String destinationUrl;
+	private final String destinationDriverClassName;
 	private final String destinationUsername;
 	private final String destinationPassword;
 
-	private MigrationArguments(String sourceUrl, String destinationUrl, String destinationUsername, String destinationPassword)
+	private MigrationArguments(String sourceUrl, String destinationUrl, String driverClassName, String destinationUsername, String destinationPassword)
 	{
 		this.sourceUrl = sourceUrl;
 		this.destinationUrl = destinationUrl;
+		this.destinationDriverClassName = driverClassName;
 		this.destinationUsername = destinationUsername;
 		this.destinationPassword = destinationPassword;
 	}
@@ -57,6 +62,7 @@ public class MigrationArguments
 		final ArrayList<String> arguments = new ArrayList<>();
 		arguments.add(MessageFormat.format("--spring.datasource.jdbc-url={0}", sourceUrl));
 		arguments.add(MessageFormat.format("--spring.seconddatasource.jdbc-url={0}", destinationUrl));
+		arguments.add(MessageFormat.format("--spring.seconddatasource.driver-class-name={0}", destinationDriverClassName));
 		arguments.add(MessageFormat.format("--spring.seconddatasource.username={0}", destinationUsername));
 		arguments.add(MessageFormat.format("--spring.seconddatasource.password={0}", destinationPassword));
 		return arguments;
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationController.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationController.java
index 17cc1df1c..4103efd70 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationController.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationController.java
@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.validation.Valid;
+import java.util.List;
 
 
 @Controller
@@ -26,6 +27,7 @@ public class MigrationController extends BaseController
 		public static final String MIGRATION_SETTINGS = "migrationSettings";
 		public static final String STATUS = "status";
 		public static final String SUMMARY = "summary";
+		public static final String DATABASE_TYPES = "databaseTypes";
 	}
 
 	private static class ReturnValues
@@ -59,14 +61,17 @@ public class MigrationController extends BaseController
 	public String migrate(Model model)
 	{
 		model.addAttribute(ModelAttributes.MIGRATION_SETTINGS, migrationService.getPrefilledMigrationSettings());
+		model.addAttribute(ModelAttributes.DATABASE_TYPES, List.of(DatabaseType.values()));
 		return ReturnValues.MIGRATION_SETTINGS;
 	}
 
 	@PostMapping
 	public String post(Model model,
 					   @ModelAttribute("MigrationSettings") @Valid MigrationSettings migrationSettings, BindingResult bindingResult,
+					   @RequestParam(value = "databaseTypeName") String databaseTypeName,
 					   @RequestParam(value = "verificationPassword") String verificationPassword)
 	{
+		migrationSettings.setDatabaseType(DatabaseType.fromName(databaseTypeName));
 		final MigrationSettingsValidator migrationSettingsValidator = new MigrationSettingsValidator();
 		migrationSettingsValidator.validate(migrationSettings, bindingResult);
 
@@ -82,12 +87,13 @@ public class MigrationController extends BaseController
 		if(bindingResult.hasErrors())
 		{
 			model.addAttribute(ModelAttributes.ERROR, bindingResult);
+			model.addAttribute(ModelAttributes.DATABASE_TYPES, List.of(DatabaseType.values()));
 			return ReturnValues.MIGRATION_SETTINGS;
 		}
 
 		final MigrationArguments migrationArguments = new MigrationArguments.MigrationArgumentBuilder()
 				.withSourceUrl(migrationService.getDatabaseFromPreviousVersionPathWithoutExtension().toString())
-				.withDestinationUrl(migrationSettings.hostname(), migrationSettings.port(), migrationSettings.databaseName())
+				.withDestinationUrl(migrationSettings.databaseType(), migrationSettings.hostname(), migrationSettings.port(), migrationSettings.databaseName())
 				.withDestinationCredentials(migrationSettings.username(), migrationSettings.password())
 				.build();
 		migrationService.startMigration(migrationArguments);
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationService.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationService.java
index b5aaf0f91..493c73e80 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationService.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationService.java
@@ -50,7 +50,7 @@ public class MigrationService
 
 	public MigrationSettings getPrefilledMigrationSettings()
 	{
-		return new MigrationSettings(databaseConfig.getHostname(), databaseConfig.getPort(), databaseConfig.getDatabaseName(), databaseConfig.getUsername(), databaseConfig.getPassword());
+		return new MigrationSettings(databaseConfig.getHostname(), databaseConfig.getPort(), databaseConfig.getDatabaseName(), databaseConfig.getUsername(), databaseConfig.getPassword(), databaseConfig.getDatabaseType());
 	}
 
 	public boolean needToShowMigrationDialog(String loadedPage)
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationSettings.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationSettings.java
index c909cb3fa..b4f9682ac 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationSettings.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/migration/MigrationSettings.java
@@ -1,5 +1,91 @@
 package de.deadlocker8.budgetmaster.migration;
 
-public record MigrationSettings(String hostname, Integer port, String databaseName, String username, String password)
+import java.util.Objects;
+
+public final class MigrationSettings
 {
+	private final String hostname;
+	private final Integer port;
+	private final String databaseName;
+	private final String username;
+	private final String password;
+	private DatabaseType databaseType;
+
+	public MigrationSettings(String hostname, Integer port, String databaseName, String username, String password, DatabaseType databaseType)
+	{
+		this.hostname = hostname;
+		this.port = port;
+		this.databaseName = databaseName;
+		this.username = username;
+		this.password = password;
+		this.databaseType = databaseType;
+	}
+
+	public String hostname()
+	{
+		return hostname;
+	}
+
+	public Integer port()
+	{
+		return port;
+	}
+
+	public String databaseName()
+	{
+		return databaseName;
+	}
+
+	public String username()
+	{
+		return username;
+	}
+
+	public String password()
+	{
+		return password;
+	}
+
+	public DatabaseType databaseType()
+	{
+		return databaseType;
+	}
+
+	public void setDatabaseType(DatabaseType databaseType)
+	{
+		this.databaseType = databaseType;
+	}
+
+	@Override
+	public boolean equals(Object obj)
+	{
+		if(obj == this) return true;
+		if(obj == null || obj.getClass() != this.getClass()) return false;
+		var that = (MigrationSettings) obj;
+		return Objects.equals(this.hostname, that.hostname) &&
+				Objects.equals(this.port, that.port) &&
+				Objects.equals(this.databaseName, that.databaseName) &&
+				Objects.equals(this.username, that.username) &&
+				Objects.equals(this.password, that.password) &&
+				Objects.equals(this.databaseType, that.databaseType);
+	}
+
+	@Override
+	public int hashCode()
+	{
+		return Objects.hash(hostname, port, databaseName, username, password, databaseType);
+	}
+
+	@Override
+	public String toString()
+	{
+		return "MigrationSettings[" +
+				"hostname=" + hostname + ", " +
+				"port=" + port + ", " +
+				"databaseName=" + databaseName + ", " +
+				"username=" + username + ", " +
+				"password=" + password + ", " +
+				"databaseType=" + databaseType + ']';
+	}
+
 }
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfiguration.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfiguration.java
index f1bd49f16..c34e996f2 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfiguration.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfiguration.java
@@ -41,7 +41,7 @@ public class DatabaseConfiguration
 					.username(databaseConfig.getUsername())
 					.password(databaseConfig.getPassword())
 					.url(jdbcString)
-					.driverClassName(MessageFormat.format("org.{0}.Driver", databaseConfig.getType()))
+					.driverClassName(databaseConfig.getDatabaseType().getDriverClassName())
 					.build();
 		}
 		else
diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfigurationProperties.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfigurationProperties.java
index 0bf84b162..deba17314 100644
--- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfigurationProperties.java
+++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/utils/DatabaseConfigurationProperties.java
@@ -1,5 +1,6 @@
 package de.deadlocker8.budgetmaster.utils;
 
+import de.deadlocker8.budgetmaster.migration.DatabaseType;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
 import javax.validation.constraints.Max;
@@ -38,6 +39,11 @@ public class DatabaseConfigurationProperties
 		return type;
 	}
 
+	public DatabaseType getDatabaseType()
+	{
+		return DatabaseType.fromName(type);
+	}
+
 	public void setType(String type)
 	{
 		this.type = type;
diff --git a/BudgetMasterServer/src/main/resources/languages/base_de.properties b/BudgetMasterServer/src/main/resources/languages/base_de.properties
index ad1ca8120..6bd31dac8 100644
--- a/BudgetMasterServer/src/main/resources/languages/base_de.properties
+++ b/BudgetMasterServer/src/main/resources/languages/base_de.properties
@@ -638,9 +638,10 @@ import.entity.chart=Es werden nur die benutzerdefinierten Diagramme importiert.<
 
 copied=Kopiert!
 
-migration.settings.description=Alle Daten aus deiner bestehenden BudgetMaster Datenbank werden in die neue Datenbank migriert.<br> Bitte gib die Einstellungen für das neue Datenbank-Backend (z.B. postgresql) ein.<br><br><span class="bold">Bitte stelle sicher, dass deine Datenbank mindestens einmal mit BudgetMaster v2.9.2 geöffnet wurde, bevor du diese Migration durchführst!</span>
+migration.settings.description=Alle Daten aus deiner bestehenden BudgetMaster Datenbank werden in die neue Datenbank migriert.<br> Bitte gib die Einstellungen für das neue Datenbank-Backend (z.B. postgresql) ein.<br><br><span class="bold">Bitte stelle sicher, dass deine bestehende Datenbank mindestens einmal mit BudgetMaster v2.9.2 geöffnet wurde, bevor du diese Migration durchführst!</span>
 migration.settings.description.warning=Alle vorhandenen Daten der Zieldatenbank werden unwiderruflich gelöscht!
 migration.settings.description.info=Die Einstellungen werden mit der aktuell verwendeten Datenbank vorbefüllt.
+migration.settings.databaseType=Datenbanktyp
 migration.settings.hostname=Hostname
 migration.settings.port=Port
 migration.settings.databaseName=Datenbankname
diff --git a/BudgetMasterServer/src/main/resources/languages/base_en.properties b/BudgetMasterServer/src/main/resources/languages/base_en.properties
index 4a8e50b8e..c79600afc 100644
--- a/BudgetMasterServer/src/main/resources/languages/base_en.properties
+++ b/BudgetMasterServer/src/main/resources/languages/base_en.properties
@@ -637,9 +637,10 @@ import.entity.chart=Only user-defined charts will be imported.<br>The import of
 
 copied=Copied!
 
-migration.settings.description=All data from your existing BudgetMaster database will be migrated to the new database.<br>Please enter the settings for the new database backend (e.g. postgresql).<br><br><span class="bold">Please make sure your database has been opened at least once with BudgetMaster v2.9.2 before running this migration!</span>
+migration.settings.description=All data from your existing BudgetMaster database will be migrated to the new database.<br>Please enter the settings for the new database backend (e.g. postgresql).<br><br><span class="bold">Please make sure your existing database has been opened at least once with BudgetMaster v2.9.2 before running this migration!</span>
 migration.settings.description.warning=All existing data of the target database will be permanently deleted!
 migration.settings.description.info=The settings are prefilled with the currently used database.
+migration.settings.databaseType=Database type
 migration.settings.hostname=Hostname
 migration.settings.port=Port
 migration.settings.databaseName=Database name
diff --git a/BudgetMasterServer/src/main/resources/templates/migration/migration.ftl b/BudgetMasterServer/src/main/resources/templates/migration/migration.ftl
index 9a470f388..79bf0559e 100644
--- a/BudgetMasterServer/src/main/resources/templates/migration/migration.ftl
+++ b/BudgetMasterServer/src/main/resources/templates/migration/migration.ftl
@@ -51,6 +51,22 @@
                                 </div>
                             </div>
 
+                            <div class="row">
+                                <div class="input-field col s12 m12 l8 offset-l2">
+                                    <i class="material-icons prefix">widgets</i>
+                                    <select id="migration-database-type" name="databaseTypeName">
+                                        <#list databaseTypes as databaseType>
+                                            <#if migrationSettings.databaseType() == databaseType>
+                                                <option selected value="${databaseType.getName()}">${databaseType.getName()}</option>
+                                            <#else>
+                                                <option value="${databaseType.getName()}">${databaseType.getName()}</option>
+                                            </#if>
+                                        </#list>
+                                    </select>
+                                    <label for="migration-database-type">${locale.getString("migration.settings.databaseType")}</label>
+                                </div>
+                            </div>
+
                             <div class="row">
                                 <div class="input-field col s12 m12 l8 offset-l2">
                                     <i class="material-icons prefix">public</i>
-- 
GitLab