Sobald man in seinem Projekt mit einer Datenbank zu tun hat, ist es ratsam die Datenbank-Tabellen mittels ORM (object-relational mapping) in objektorientierten Entitäten abzubilden.
Im Symfony (PHP) ist Doctrine die erste Wahl, in Spring Boot (Java) ist es Hibernate.
Damit das Validieren des Datenbank-Schemas möglich ist, benötigt man eine Datenbank worin die Migrations-Scripte geladen und ausgeführt werden können.
Es möglich eine Datenbank mittels Docker zu starten oder man wählt stattdessen eine In-Memory-Datenbank wie H2. Der Vorteil bei der In-Memory-Datenbank ist es, dass sie sehr schnell geladen wird, da alles im Arbeitsspeicher ausgeführt wird.
Letztens ist ein interessantes Problem aufgefallen. Die Validierung des Datenbank-Schemas wurde erst nachträglich aktiviert und beim ausführen der Acceptance-Tests, wo die Datenbank geladen wird und auch alle Migrations-Scripte ausgeführt werden, ist es zu einer Exception gekommen. Die Exception lautet SchemaManagementException – Schema-validation: wrong column type encountered in column [bar] in table [foo]; found [double (Types#DOUBLE)], but expecting [float (Types#FLOAT)].
Setup
Die Schema-Validierung wird für das Test-Environment in der application-test.yml aktiviert:
// application-test.yaml database: username: sa password: sa host: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 spring: jpa.hibernate.ddl-auto: validate
Das Datenbank-Migrations-Script:
// V1__create_table_foo.sql CREATE Table Foo ( `id` bigint NOT NULL AUTO_INCREMENT, `bar` float DEFAULT NULL, PRIMARY KEY (`id`) );
Die dazugehörige Entität:
// Foo.java import lombok.Data; import javax.persistence.*; @Data @Entity @Table public class Foo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private Float bar; }
Problem
Float ist in H2 nicht vorhanden. Der Fallback dafür ist der Datentyp Double. Sobald Hibernate die Entitäten mit den Datenbanktabellen validiert kommt es zu einer Fehlermeldung, dass die Datentypen nicht identisch sind, da H2 den Datentyp von Float selbstständig zu Double geändert hat.
Der Unterschied zwischen den Datentypen Float und Double is der, dass Float 32 Bit lang ist und Double 64 Bit. Das heißt, dass Double eine höhere Genauigkeit aufweist.
Sofern in dem Feld Währungseinheiten gespeichert werden sollen, wird der Java Datentyp BigDecimal und für MySQL äquivalent der Datentyp DECIMAL(20,2) empfohlen.
Lösung
Die Lösung lautet, dass das Attribut in der Entität den Fallback direkt zugewiesen bekommt, in diesem Fall also Double. Also ändern wir das Attribut der Entität auf Double.
// Foo.java import lombok.Data; import javax.persistence.*; @Data @Entity @Table public class Foo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private Double bar; }
Zusätzlich muss auch in der Datenbank der Datentyp der Spalte bar auf Double geändert werden. Entweder initial richtig mit solch einem Migrationsscript:
// V1__create_table_foo_double.sql CREATE Table Foo ( `id` bigint NOT NULL AUTO_INCREMENT, `bar` double DEFAULT NULL, PRIMARY KEY (`id`) );
Sofern die Migration mit dem fehlerhaften Datentyp bereits ohne Validierung bis ins Live Environment ausgeführt wurde, dann muss per zusätzlichem Migrations-Script der Datentyp nachträglich geändert werden:
// V2__modify_datatype_to_double.sql ALTER TABLE `foo` MODIFY `bar` double;
Nun sind in der produktiven MySQL Datenbank, in der H2 Datenbank für die Acceptance Tests und in der Java Entität alle Datentypen identisch.
Doch Vorsicht: Änderungen an der Datenbank können nicht widerrufen werden. Deshalb ist es ratsam ein Backup von der Datenbank zu erstellen.
Leave A Comment