Best Practise Automatisierung in Spring

shredding

Mitglied
Ich bin mir nicht ganz sicher wie ich meine Frage formulieren soll und ob ich hier richtig bin, aber ich fange mal an.

Ich komme aus dem Bereich dynamische Sprachen und fange mit Spring an und sehe auch dass es ein sehr mächtiges Framework ist.

Trotzdem habe ich mit meinen Spring Boot Applikationen oft das Gefühl, dass ich "am Framework vorbeiprogrammiere" und viel zu viel repetitiven Code schreibe.

Ein Beispiel:

Bisher nutzte ich etwa das www.django-rest-framework.org und dort schreibe ich etwa eine Zeile Code die sieht so aus:

Code:
email = models.Email(required=True, unique=True)

Und daraus generiert das Framework ein Standard DTO (heisst da Serializer) und ein CRUD Rest View, den ich noch anpassen kann. Ich bekomme Datenbankabstrahierte Migrationen.

Vor allem aber gibt es bereits vernünftige Validierung. Django gibt mir etwa einen 409er Status Code zurück und eine i18n'te Fehlermeldung im JSON wenn ich die Email doppelt anlege usw. Im Kern definiere ich in dieser einen Zeile das komplette Feld.

Meine Spring Experience ist, dass ich eine "@Unique" annotation anlege, die vom Framework komplett ignoriert wird und ich muss mein SQL selbst schreiben obwohl ich eine library dafür benutze (flywaydb).

Die ganzen Crud Operationen kann ich mit Java Data Rest freigeben, das habe ich gesehen (ich mag das zwar architektonisch nicht, aber das ist eine andere Frage).

Vor allem bekomme ich aber immer als Fehlermeldung eine 500er JSON und muss mich um absolut banale Anwendungsfälle selber kümmern. Für das "unique=True" in-Django handling gibt es dazu ganze Tutorials (http://www.baeldung.com/spring-dataIntegrityviolationexception).

Das ganze ist keineswegs als Java Bashing gemeint! Ich bin tief beeindruckt von Spring!

Ich kann mir nur nicht vorstellen dass jedesmal wenn jemand in der Java Feld einen REST Service mit Email Feld schreibt denselben Code schreibt.

Gehe ich irgendwie falsch an die Sache ran? Überlese ich etwas?
 

mrBrown

Super-Moderator
Mitarbeiter
Das Äquivalent zu dem von dir genannten wäre am ehesten Spring Data REST, hat halt Vor- und Nachteile ;)


Vor allem aber gibt es bereits vernünftige Validierung.
Input-Valididerung etc würde man in Controllern mit @Valid durchführen, ein BindingResult enthält dann die Fehler. Die Rückgabe muss man aber mWn selber daraus generieren.

Meine Spring Experience ist, dass ich eine "@Unique" annotation anlege, die vom Framework komplett ignoriert wird

Eine @Unique-Annotation gibt es mWn überhaupt nicht, was für eine nutzt du da?
Der richtige Weg wäre, das Feld mit @Column(unique=true) zu annotieren, dann wird die Datenbank entsprechend eingerichtet.

ich muss mein SQL selbst schreiben
Wenn du Spring Data JPA nutzt, musst du nur in Ausnahmefällen SQL selbst schreiben (und wenn, dann JPQL).
Du brauchst nur ein entsprechendes Repository-Interface mit entsprechenden Methoden und der Rest passiert automagisch.

obwohl ich eine library dafür benutze (flywaydb).
Was genau meinst du mit flywaydb?
FlyWay ist zur Datenbank-Migration da, zur Laufzeit deiner Anwendung passiert damit üblicherweise nichts.
Stattdessen braucht du passende Datenbank-Treiber.
 

shredding

Mitglied
Ich habe mir Data Rest auch angeschaut. Ich bin eigentlich großer Fan von Annotations für die Konfiguration, aber mir gefällt nicht dass ich hier sicherheitskritische Aspekte wie Field-Whitelisting vornehme, aber das ist halt eine Designentscheidung, damit könnte ich leben.

Kannst du mir ein Beispiel geben, was du mit dem Migrationsweg über JPA Data meinst? Soweit ich das verstanden hatte spielt FlyWay Migrationen ein, kann aber keine erstellen. Ich bin gewohnt dass ich Migrationsdateien vom Framework erstellen lassen kann, auch was änderungen angeht. Ich weiß auch dass das mit Hibernate funktioniert, aber ich habe irgendwie keinen Best-Practise Guide dazu gefunden (insgesamt fehlen mir glaube ich hauptsächlich Best Practice Fälle).

Ein Beispiel zu der @Valid Validierung fände ich auch super. Aber das erste was ich machen würde, wäre das so zu abstrahieren dass common cases wie etwa der Database Constraint Error abgefangen würde - weshalb ich die Annahme hege dass das auch schon mal jemand gemacht hat und ich das nur falsch anwende - oder schreibt ihr bei einer REST API wirklich jeden einzig möglichen Fehler, der sich aus der Modelkonfiguration offensichtlich ergibt selbst?
 

shredding

Mitglied
Also konkret habe ich etwa folgendes Model:

Code:
@Entity
internal data class Client(

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private var id: Int = 0,

        @NotBlank
        private var uuid: String = "",

        @NotBlank
        @Column(unique = true)
        private var ip : String = "",

        @Column(columnDefinition="DATETIME")
        private var lastSeen : Date = Date()

) {
    constructor() : this(0)

    internal fun toDto() = ClientDto(uuid = this.uuid, ip = this.ip)
}

Dazu habe ich - von Hand das folgende, datenbankspezifische SQL angelegt:

Code:
CREATE TABLE client (
  id SERIAL PRIMARY KEY NOT NULL,
  uuid varchar(256) NOT NULL,
  ip varchar(256) NOT NULL,
  last_seen TIMESTAMP NOT NULL
);

Und flyWay legt dann die Tabelle beim nächsten Applikationsstart auch an.

Dann entscheide ich mich die uuid unique zu machen, weil dafür steht ja das u :)

Code:
@NotBlank
@Column(unique = true)
private var uuid: String = ""

Jetzt lege ich eine neue datenbankspezifische SQL an:

Code:
ALTER TABLE client ADD CONSTRAINT UQ_client_uuid UNIQUE (uuid)

... das funktioniert auch alles, aber es fühlt sich aus verschiedenen Gründen falsch für mich an.

1. Die Informationen welches SQL ich benötige stehen ja schon im Model (@Column(unique = true)) und ich vermute mal das Spring das auch irgendwie verwerten kann um den Code für die Datenbank zu generieren.

2. Ich hätte diesen Code gerne datenbankagnostisch (generisches Java oder so).

Ich stelle mir also irgendwie was vor wie gradle create_migrations oder sowas und das schaut ob es eine Differenz zwischen den bisher erstellten Migrationsfiles und den registrierten models gibt und dann ggf neue files erstellt.

Diese Funktionalität war bei allen bisherigen Frameworks (Rails, Django etc.) immer dabei und ich sehe ja auch dass es das in Spring auch irgendwie gibt, finde aber keine best practises or how-tos.
 

mrBrown

Super-Moderator
Mitarbeiter
Naja, annotier deine Entities, hab Spring Data JPA und die Datenbank-Treiber als Dependencies und in der Config URL etc zur DB eingetragen und starte einfach mal die Applikation mit frischer, leerer Datenbank und guck der Magie zu ;)

Die Datenbank wird allein aufgesetzt, anhand der Annotationen, genauso wie später alle Queries autogeneriert werden (wenn man JpaRepositorys nutzt)
 

shredding

Mitglied
Hm. Also bei mir nicht. Ich bekomme dann eine

Code:
org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table client

Meine Config sieht so aus:

Code:
spring.datasource.url=jdbc:postgresql://localhost:5454/core
spring.datasource.username=postgres
spring.datasource.password=
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=validate

Ich will auch nicht unbedingt dass das autogenriert wird, weil ich ja auch gerne reverten können möchte, aber gut, das wäre zumindest der erste Schritt.
 

mrBrown

Super-Moderator
Mitarbeiter
Ansonsten geht (zumindest mit Liquibase, ob mit FlyWay auch, weiß ich nicht), das aus den Annotationen die für das Tool passende Config erzeugt wird, und du diese dann damit anwenden kannst.

Ich kann später mal suchen, ob ich’s finde.


Zum Testen find ich H2 super ;)
 

shredding

Mitglied
Mich beruhigt das ja ein bisschen, dass es keine einfache Antwort gibt, die ich vielleicht überlesen habe.

Das stört mich halt so ein bisschen an Spring.

In meiner "dynamischen Welt" bin ich es gewohnt, dass es halt eine Best Practise Dokumentation gibt wie etwa

- hier: https://docs.djangoproject.com/en/2.0/topics/migrations/
- und hier: http://edgeguides.rubyonrails.org/active_record_migrations.html

... so etwas vermisse ich bei ganz vielen Themen bei Spring und irgendwie bisher auch bei Java im Allgemeinen und es macht die Einarbeitung irgendwie unnötig kompliziert.
 

Neue Themen


Oben