Lokaler Entwicklungsserver

DaBe1812

Mitglied
Moin zusammen,

ich hätte da mal ein Problem mit meiner lokalen Entwicklung.

Wir haben in IntelliJ ein Multi-Modul-Projekt. Jakarta EE mit Vaadin Frontend.

Dazu haben wir die Module
  • Datenbank
  • Backend
  • Frontend
  • WAR
  • Deployment (Kopiert alle Sourcen in den Main-Target-Folder)
Problem war/ist wir haben lokal einen WLP installiert, zum testen. Das heißt, damit ich lokal testen kann, müssen immer die Sourcen kompiliert werden und der Server gestartet werden. DAs kostet dann für jede Änderung mindestens 5 Minuten.

Außerdem ist die Datenbank für lokal aktuell dieselbe, wie die Entwicklungsdatenbank. Jetzt kam der Fall, der kommen musste, ich hatte lokal ein paar Daten umgestellt, die Änderung an den Daten ging beim Testen an die Entwicklungs-Datenbank und damit konnten dann alle anderen nicht mehr arbeiten und der Server in der Entwicklung hat auch nur noch Fehler geworfen.

Meine Idee war zum einen einen WLP in der Anwendung einzubinden, damit man lokal testen kann über das maven goal liberty:dev. Das funktioniert nur solala,weil der liberty liegt in Frontend, d.h. ich muss die Module Datenbank und Backend immer erst kompilieren, wenn ich etwas im Frontend testen möchte. Des weiteren funktionieren dadurch scheinbar auch nur Haltepunkte im Frontend, was beim Debuggen doch eher ärgerlich ist.

Zuletzt habe ich wegen des Datenbank-Problem noch eine lokale H2 Datenbank eingebunden, welche mit den aktuellen Dev-Daten bespielt wird. Da ist aber das Problem, dass diese trotz Oracle-Modus Probleme mit einigen Spaltennamen hat, aber jetzt laufe ich zusätzlich auf ein Problem mit
TypedQuery.setMaxResults und
TypedQuery.setFirstResult,
weil H2 scheinbar mit den Begriffen nichts anfangen kann, was ich aber nicht verstehe, weil ich eigentlich dachte, dass dafür JPA solche Sachen in H2 übersetzt.

Könnt ihr mit der Beschreibung etwas anfangen und mir evtl. ein wenig helfen?
 

KonradN

Super-Moderator
Mitarbeiter
Neben den Testcontainern kommt mir auch noch die folgenden Punkte in den Sinn:

a) Unabhängig von Docker und Co: Als Entwickler sollte man immer eine Möglichkeit haben, seinen Code richtig auszuführen. Das kann zur Not auch eine Umgebung sein, die man selbst aufgebaut hat (Also z.B. direkt auf dem Entwicklungsrechner oder in einer/mehrerer VMs). Das hat aber den Nachteil, dass ein Entwicklungssystem sehr viel an Komplexität gewinnt, was zu entsprechenden Aufbauzeiten und auch zu grossen Maintenenance-Aufwänden führt. Halt etwas, das durch die Testcontainer vermieden wird und das auch mein Ratschlag wäre...

b) Gliederung der Module: Die Möglichkeit, schnell seine Umgebung zu starten, ist aus meiner Sicht existenziell. Hier sollte man immer einen Weg vorsehen, wie man seine Umgebung mit Debuggeranbindung starten kann. Hier kommt dann aus meiner Sicht vermutlich das Liberty Maven Plugin ins Spiel. (Evtl. gibt es aber auch noch andere Möglichkeiten. Zu WLP kann ich nicht ganz so viel sagen, da ich es bsiher nicht gross benutzt habe. Wir setzen auf andere Technologien / Frameworks)

c) Mit den H2 Problematiken: Habe ich das richtig verstanden, dass Du H2 nutzt, aber Du den oracle mode gesetzt lässt? Aber das meintest Du vermutlich nicht und Du hast org.hibernate.dialect.H2Dialect oder so als mode gesetzt...
Das mit den setMaxResults/setFirstResult läuft ja auf ein OFFET ... FETCH NEXT ... SQL Query hinaus. Hier ggf. mal das Query im Detail ansehen um zu schauen, was da erstellt wurde. Fehlt evtl. ein ORDER BY oder so? Sowas kann man dann im Detail weiter prüfen, wenn man sich anschaut, was denn da an SQL generiert wurde.

Aber das mit den Testcontainern macht aus meiner Sicht am meisten Sinn und das solltest Du weiter verfolgen. Und statt H2 dann evtl. ein PorstgreSQL Container (falls es da nichts brauchbares von Oracle geben sollte)....
 

DaBe1812

Mitglied
Okay, in Docker müsste ich mich mal reinarbeiten, das Thema habe ich bisher erfolgreich vermeiden können. Wie muss ich mir das grob vorstellen?
Alle meine Module sind da irgendwie als JAR oder EAR drin und der Server läuft fröhlich vor sich hin, bis ihn einer runterfährt?

Da wir im Entwicklerteam mehr als nur ich sind, war meine Idee, dass das aktuelle Setup komplett im Git liegt und damit jeder immer die komplette Umgebung hat. Bisher war es immer so, dass wenn jemand etwas im WLP geändert hatte (weil er z.B. ein zusätzliches Feature gebraucht hat) dann musste er das immer kommunizieren, damit jeder im Team das Feature bei seinem lokalen WLP auch aktiviert, sonst konnte es eben zu Fehlern kommen. So wäre jetzt alles zentral im Repository und keiner muss mehr Konfigurieren.

Für "schnelles" Testen meiner Arbeit hab ich immer Unittests geschrieben, die kann ich einfach direkt starten und gucken, ob die Methoden das machen, was sie sollen. Problem ist dabei aber auch immer gewesen, dass dann keine Datenbank verwendet werden konnte, weil die ja per JNDI eingebunden ist und dann im Test, ohne Server nicht da ist. Oder Fremdsysteme, die man per Rest abfragen will. Da musste ich dann immer alles Mocken, statt es einfach schnell "richtig" auszuführen.

Ich verwende lokal H2 und auf dem Server haben wir Oracle. Dafür kann man in H2 den Oracle Mode setzen:
XML:
<dataSource id="DB" jndiName="jdbc/appDataSource" transactional="false">
    <jdbcDriver libraryRef="H2Lib"/>
    <properties URL="jdbc:h2:mem:testdb;MODE=Oracle;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'classpath:scripts/h2-init-logger.sql'"
                password="" user="sa"/>
</dataSource>
Angeblich ist dann der Befehlssatz nicht ganz so weit weg.

Ich habe jetzt von JPQL auf CriteriaBuilder umgestellt, das funktioniert besser, aber ich laufe immer noch auf den Fehler in der Pagination:
Das ist der Code:
Java:
TypedQuery<LoggingRecord> query = em.createQuery(cq);

if (offset > 0) {
    query.setFirstResult(offset);
}

if (pageSize > 0) {
    query.setMaxResults(pageSize);
}

return query.getResultList();
Und das der Fehler:
Code:
There was an exception while trying to navigate to 'log_reader' with the root cause 'org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax Fehler in SQL Befehl "SELECT ID AS a1, LOG_DATE AS a2, LOG_LEVEL AS a3, LOG_ROW AS a4, LOGGER AS a5, MARKER AS a6, MESSAGE_TXT AS a7, THROWABLE AS a8 FROM LOGGING ORDER BY LOG_DATE DESC [*]LIMIT ? OFFSET ?"

Ich denke ja mal, dass der Stern den Fehler markieren soll, das wäre damit Limit.
 

Oneixee5

Top Contributor
Wenn ihr Docker (noch) nicht verwendet, dann sie dir https://podman.io/ an. Podman steht unter Apache Lizenz. Wenn du Docker gewerblich nutzen möchtest, dann benötigst du eine kostenpflichtige Lizenz. Bei uns, mit den vielen VM's ist das manchmal problematisch. Kubernetes kann mit Podman genauso gut umgehen.
 

KonradN

Super-Moderator
Mitarbeiter
Wie muss ich mir das grob vorstellen?
Container sind einfach leichtgewichtige virtuelle Maschinen. Leichtgewichtig, weil nicht wirklich eine komplette virtuelle Maschine bereit gestellt wird. Und der grosse Vorteil, dass neue Container sehr einfach und schnell erzeugt werden können.

war meine Idee, dass das aktuelle Setup komplett im Git liegt und damit jeder immer die komplette Umgebung hat.
Ja, genau das soll das Ziel sein. Testcontainer ist dabei einfach eine Lösung, um Testumgebungen bei Bedarf schnell zu erzeugen. Wenn ich also bei unseren Projekten einen Microservice starte (Wir nutzen Quarkus), dann werden die notwendigen Docker Container erzeugt und gestartet. (Das ist dann bei uns z.B. ein PostgreSQL Container und der Container mit dem Quarkus Service). Dabei gibt es dann auch gewisse Konifgurationsmöglichkeiten so gibt es z.B. SQL Skripte, die beim Start direkt gestartet werden um die Datenbank zu befüllen.

Die Integrationstests sind dann unter dem Strich ein Verzeichnis in der Sourceverwaltung, die dann mit Bruno geöffnet werden.

Ob man nun Testcontainer, Docker, ... nutzt ist dabei egal. Wichtig ist unter dem Strich nur, dass Du halt für Tests eine Umgebung starten kannst mit genau Deinem Entwicklungsstand. Dabei kannst Du mehr oder weniger automatisieren. Je mehr Du automatisiert hast, desto einfacher ist es für ein Team, da zu entwickeln.

Testcontainer ist ein Weg der Automatisierung. Evtl. hilft es, wenn ich einmal beschreibe, wie das bei uns aussieht (Ausgangslage ein neuer Laptop):
  • Ein Entwickler lädt sich bei uns einfach IntelliJ und Docker herunter und installiert beides.
  • in Intellij öffnet man das Project von der Sourcecode Verwaltung
  • Intellij lädt dann - nach Abfrage - automatisch direkt einige AddOns herunter
  • Der Entwickler lädt dann noch das richtige OpenJDK herunter in IntelliJ
==> Alles fertig (ok, zwei Konfigurationen für Maven und Docker fehlen doch, aber das ist erst einmal unwichtig) - nun kann in mvn install gestartet werden ==> Der Bau und alle Tests sollten direkt durchlaufen...
==> Jetzt kann direkt ein Service des Projektes gestartet werden - per Run oder Debug. Es ist nichts weiter notwendig. Es werden automatisch die notwendigen Testcontainer erzeugt und in Docker gestartet. Wenn man es stoppt, dann verschwinden die Container direkt wieder ...
Die Idee, die hier deutlich werden soll, ist der hohe Grad der Automatisierung. Es muss nichts mehr gross gemacht werden. Ein Entwicklungsrechner muss ich nicht gross vorbereitet werden. Das vereinfacht auch das Onboarding von Entwicklern.


Ich denke ja mal, dass der Stern den Fehler markieren soll, das wäre damit Limit.
Wenn man sich https://h2database.com/html/features.html anschaut, dann fällt mir auf, dass bei vielen Modis explizit geschrieben steht: "LIMIT / OFFSET clauses are supported." aber beim Oracle Mode steht das nicht.
Man findet aber dann bei SO teilweise Hinweise, dass man sowas ggf. einschalten kann, siehe z.B. https://stackoverflow.com/a/70702801

Aber ggf. solltest Du einfach einmal schauen, ob Du den Oracle Modus nicht einfach weg lässt. Wenn Ihr JPA nutzt, dann solltet Ihr euch auf einer Ebene über der Datenbank befinden und nicht direkt auf fixe Datenbank-Features setzen. Überlasst das doch der JPA Implementation. (Setzt dann voraus, dass Ihr z.B. kein SQL selbst vorgebt sondern wenn dann nur JPQL nutzt und so ... So dass ihr eben nicht auf Datenbankebene herunter geht. Dann kann man in Testumgebungen auf andere Datenbanken zugreifen wie H2 aber auch postgresql oder so)
 

thecain

Top Contributor

Ich würde mich persönlich nicht zu lange mit H2 oder auch Postgresql quälen.

Wenn die Anforderung nach mehreren dbms nicht besteht, würde ich versuchen in dev und test so nahe an Prod zu sein wie möglich. Ja, JPA sollte abstrahieren, aber wenn ich potentielle inkopabilitäten im vorneherein ausschliessen kann, mache ich das.

Docker und Testcontainers benötigt einiges an einarbeitung, ist aber eine riiiese Erleichterung die ich in meinem Setup nicht mehr missen möchte.

Wenn ich nur an die Zeiten mit x installierten JBoss versionen und dem rumkopieren von WARs und standalone.xmls zurückdenke wird mir graus
 

DaBe1812

Mitglied
So, jetzt komme ich wieder zum Testen, allerdings habe ich gerade keine Zeit für Experimente.
Docker und Testcontainer prüfe ich. Die IntelliJ AddOns werden wohl das Problem hier werden, weil unsere IT nicht mal das Lombock Plugin genehmigt, weil "Es gibt doch jetzt Records, da braucht man das nicht mehr".
Testcontainer hört sich nach eurer Beschreibung so an, als wäre das eine Abhängigkeit im Projekt?

Die Sache mit den Limits ist tatsächlich so, wie im Artikel, also oracleMode an == Limits kaputt, aus == Limits gehen, dafür geht halt nix, was ich in native SQL geschrieben habe (zumindest ein Teil davon). Wegen des reaktivieren der Limits schaue ich nochmal, da hilft mir der Artikel nur bedingt.

Oracle XE wäre natürlich auch eine Lösung. Hat das jemand schonmal probiert und kann behaupten, dass das genauso, wie H2 funktioniert? Also im lokalen Nexus ist das schonmal drin. Vielleicht gebe ich auch dem mal einen Try. Sollte ja dann mit Liquibase ohne Probleme die Testdatenbank zusammenschrauben.

Dazu vielleicht nochmal eine Frage. Ich logge mittlerweile in die Datenbank. Liquibase greift bei mir in einer @Startup Klasse. Das war der einzige Weg, den ich gefunden habe, den ich selbst steuern kann, weil alle Abteilungen, die Deployment machen nichts darüber hinaus machen und mvn liquibase:* oder ein SQL absetzen war schon zu viel.
Jetzt fängt aber der Logger an zu arbeiten, bevor Liquibase die Loggingtabelle erstellt hat. Führt dann halt zu einigen Exceptions beim Serverstart. Dem bin ich jetzt entgegen gekommen, indem ich ein Startup-Script an H2 übergeben habe, welches mir die Loggingtabelle einfach beim ersten Connect anlegt. Gibt es dafür vielleicht einen eleganteren, konsistenteren Weg?

Oh, und vielleicht sehe ich das mit dem testing falsch. ich dachte immer
XML:
<scope>test</scope>
bedeutet, dass das nur gezogen wird, wenn ich Testfälle laufen lasse und ansonsten diese Abhängigkeit nicht eingebunden wird. Wenn ich aber
Code:
mvn liberty:dev
benutze, um meinen Server zu starten, dann dachte ich, ich bin aus dem Testfall raus, oder habe ich da ein völlig falsches Verständnis von Maven?
 

Oneixee5

Top Contributor
Tests werden mit "test.skip" übersprungen
Code:
mvn *** -Dmaven.test.skip=true
# oder
mvn *** -DskipTests
# oder pom.xml
<properties>
  <maven.test.skip>true</maven.test.skip>
</properties>

Das bedeutet, dass die Lib nur bei Tests dem Classpath hinzugefügt wird
Code:
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>...</version>
    <scope>test</scope>
</dependency>

Aber andere Libs können bspw. Junit ebenfalls als Abhängigkeit ziehen.
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Evtl. hilft es auch, einmal die Dokumentation zum dev Goal des Plugins zu lesen:


a) es wird u.a. ein install ausgeführt. Das install goal durchläuft auch die goals für die Tests (Test-compile und so). Wenn du das nicht willst, ist das -DskipTests evtl. hilfreich

b) der gestartete Server kann wohl auch zur Ausführung von Tests verwendet werden. Das erklärt vermutlich, dass auch scope test Abhängigkeiten mit geladen werden.

Was für Probleme hast du mit dem scope Test? Die zusätzlichen Abhängigkeiten sollten eigentlich keine Probleme verursachen… hast du evtl. Probleme, weil es Konflikte mit Libraries vom Application Server gibt? Dann ist evtl. auch ein Scope provided oder so denkbar? Aber ohne Details, was für eine Libraries du wann wie brauchst, ist das nur ein Hinweis, den du prüfen musst statt es direkt als Lösungsweg anzusehen ….
 

DaBe1812

Mitglied
Oh je, ihr setzt bei der Flughöhe schon zu hoch an. ICh glaube ich hab ein Basic-Verständnisproblem.
Also den Part mit den -Dskiptests kann ich schon. Das ist unglaublich praktisch, wenn man mal schnell für lokal kompilieren will. Außerdem sollen die ganzen Unit und Integrationstests doch einfach auf dem Jenkins laufen, der hat keinen Zeitdruck.

Mein gedankliches Problem ist in den Maven Goals und was genau im Hintergrund wirklich passiert. Da ist meine Vorstellung sehr abstrakt.
Ich dachte, ich klicke in einer Test-Klasse, die im Modul unter src/test liegt auf Play und dann und werden alle Dependencies incl. der Test-Scope Dependencies geladen. Wenn ich aber etwas anderes mache, wie z.B. Package, dann werden diese spätestens nach den Test wieder rausgeworfen.
Also da ich für meinen Server immer ein WAR gebaut habe, um lokal zu testen, bin ich davon ausgegangen, dass die Test-Dependencies nicht mehr geladen sind.
Das Liberty Dokument werde ich mir heute noch durchlesen, vielleicht schafft das nochmal Klarheit.
 

KonradN

Super-Moderator
Mitarbeiter
Prinzipiell liegst Du bei den meisten Punkten richtig. Der schnelle Überblick bei Maven Goals wäre aus meiner Sicht:

- Goals sind nicht eigenständig sondern haben sowas wie Abhängigkeiten. Das führt unter dem Strich dazu, dass Du ein Goal ausführen willst aber er durchläuft dann mehrere Goals. Bei einem mvn install macht er nicht nur den install sondern eine ganze Sequenz angefangen mit Initialisierungen über Code Generierungen, Compile, .... Und auch bei Plugins ist dies so. Und bei liberty:dev hast Du dann:
This goal also invokes the create, install-feature, and deploy goals before starting the runtime.

- Was die einzelnen goals von Maven angeht: Hier findet dann tatsächlich eine Unterscheidung statt. bei compile wird z.B. src/main/java berücksichtigt und bei test-compile eben src/test/java. Und die Zielverzeichnisse halten diese Dinge auch getrennt. So ist dann sicher gestellt, dass Dein WAR eben keine test Scope Abhängigkeiten enthält. Aber generell ist das eine Sache, wie ein Plugin damit umgeht. In der POM hast Du einfach nur eine Konfiguration und da steckt eben ein Scope drin. Was also genau passieren soll, kann man dann selbst festlegen (Als Nutzer von Maven natürlich nur in den Grenzen, die von den Entwicklern vorgegeben wurden. Aber wenn man ein eigenes Maven Plugin schreibt, dann kannst Du da natürlich alles machen ....)

- Was macht das Liberty Plugin? Ich habe damit keinerlei Erfahrung - Ich bewege mich hier in einem Bereich, den ich selbst nicht nutze. Bei mir gab es lediglich Gedanken wie: Ich kenne so Plugins für Spring Boot, Quarkus, ... - gibt es evtl. auch für Liberty. Oder eben: Ich kenne Testcontainer aber ich hatte keine Ahnung, dass es sowas für Oracle gibt, bis es eben verlinkt wurde. Das erwähne ich nur, um deutlich zu machen, dass es garantiert bessere Experten als mich gibt. Ich habe aber die Dokumentation etwas gelesen und da gibt es einen Abschnitt zu Test-Ausführungen:
Dev mode provides three key features. Code changes are detected, recompiled, and picked up by your running server. Unit and integration tests are run on demand when you press Enter in the command terminal where dev mode is running, or optionally on every code change to give you instant feedback on the status of your code. Finally, it allows you to attach a debugger to the running server at any time to step through your code.
Und wenn hier also Unit und Integrationstests ausgeführt werden können, dann ist klar, dass diese auch im Scope von dem Plugin sind. Dazu werden dann ja die test Scope Abhängigkeiten benötigt. Das sieht also für mich auf den ersten Blick erst einmal logisch aus - aber ich kann mich hier natürlich auch massiv irren und etwas missverstehen.

- Nutzen der IDE: Wenn man einen Unit Test direkt in der IDE startet, dann ist immer die Frage, was denn hier die IDE genau macht. Das ist in vielen Bereichen konfigurierbar. Wichtig ist hier aber: Viele IDEs haben ihre eigene interne Projektverwaltung. Es wird dann also nicht zwingend Maven benutzt. Bei IntelliJ kann man da z.B. unter Build, Execution, Deployment / Maven / Runner den Punkt "Delegate IDE build/run to Maven setzen.
Hier muss man aber schauen, ob das wirklich gewollt ist. Die IDEs bieten mit Ihrem Vorgehen oft auch Vorteile, die besser in den Ablauf integriert sind und daher Zeit sparen können ....

Daher ist Deine Erkenntnis von wegen "Flughöhe" durchaus nachvollziehbar. Die generelle Thematik ist extrem komplex und da es kein wirkliches richtig/falsch gibt, kann man auch nur empfehlen, damit dann einfach mit der Zeit herum zu spielen.... (Wobei ein öffnen der Settings meist schon extrem deprimierend ist ... finde mal die Settings, mit denen Du womöglich herum spielen willst .... Egal ob IntelliJ oder Eclipse oder VSC ... Weil so viel konfigurierbar ist, ist es schwer, sich da am Anfang zurecht zu finden)

Ich hoffe, dass ich Dir damit etwas helfen konnte. Und vielleicht können Leute mit mehr praktischer Erfahrung mit genau den verwendeten Tools Dir noch konkretere Hinweise geben.
 

DaBe1812

Mitglied
Moin, will mich mal wieder melden, damit hier nicht der Eindruck der Undankbarkeit entsteht. Wir sind gerade in der Abnahme und in der Version hab ich wieder so viel Core-Changes gemacht, dass mal wieder die Hälfte nicht funktioniert und ich wirklich ein wenig im Stress bin, dazu will ich auch noch ab Mittwoch Urlaub machen und am Freitag muss die Entscheidung über die Installation auf dem Piloten getroffen werden. Moment, ich sehe gerade, Donnerstag.

Zum Testing in IntelliJ hab ich eine witzige Anekdote, die das von dir @KonradN unterstreicht. Als ich mit Unittests angefangen habe, war es noch kein Problem, aber als ich mehr Tests hatte, ist mr aufgefallen, dass wenn ich einen Test debuggen möchte, er vorher irgendwie alle Tests erst ausführt, weil er scheinbar einen vollen Maven durchlauf vorher macht. Das musste ich dann in den Optionen abschalten. Das führt aber dazu, dass wenn ich z.B. in Backend etwas testen möchte, das aber wiederum Abhängigkeiten im Database-Modul hat, die sich verändert haben, dann muss ich vorher erst das Database-Modul mit instal ausführen, damit die Abhängigkeiten dann im Backend auch akzeptiert werden.

Zur Testumgebung gibt es aber positive Neuerungen. Ich habe jetzt Podman lokal genehmigt bekommen, das ist Docker sehr ähnlich und darin werde ich dann versuchen mit Oracle XE eine Testumgebung aufzubauen, die dann über das Repository gepflegt wird. Ist das eigentlich ab einer gewissen "Abstraktionsebene" normal, dass man einen extra Debugger starten muss, damit man auch Debuggen kann?
Das hat mich ein wenig kalt erwischt, wenn ich meinen WLP über die Konfiguration mit Debug gestartet habe, dann war ich im Debugging-Modus. Wenn ich jetzt das Maven Goal für den liberty mit Debug starte, dann könnte ich zwar Maven ansich debuggen, aber nicht den laufenden WLP. Auf den muss ich einen extra Debugger starten, damit ich dann auch mit Haltepunkten arbeiten kann. Funktioniert super, wenn man dran denkt.

Und nochmal generell: Ich höre gerne von euren Erfahrungen, weil das ist das, was uns alle noch von KIs unterscheidet, wir können Zusammenhänge aus unseren Erfahrungen erkennen und dass ich mit dem blöden WLP leider in einer Welt leben muss, die kaum einer kennt, daran habe ich mich in sechs Jahren gewöhnt, aber die Kombination aus allen Erfahrungen macht eben das Bild klarer.
 

Oben