- Java — good practices and recommendations
- Hidden Gems In Java 11
- Modeling Uncertainity with Reactive DDD
- Open Session In View Anti-Pattern
- Entity Graphs
- Howto zur Aktualisierung von Jenkins. Jenkins läuft dabei in einem Docker Container.
Sonntag, November 25, 2018
Links der Woche
Samstag, November 24, 2018
Javascript Object Destruction
Dieses Feature wünsche ich mir für Java: Objekt Dekonstruktion.
Variablen austauschen:
Das auslesen von Rückgabewerten aus Arrays bzw. das ignorieren von Rückgabewerten.
Das teilweise übernehmen von Objekteigenschaften.
Zuweisen mit neuem Variablennamen:
Quelle: Devloper Mozilla
Mittwoch, Oktober 31, 2018
CompletableFuture II
Das CompletableFuture
wurde mit Java 8 eingeführt und ist ein Teil des
Packages java.util.concurrent
. Ziel: Verkettung von asynchronen Aufgaben.
Als Einstiegspunkt dienen die Methoden #get(...)
und #complete(...)
.
#get(...)
stellt den aktiven Thread wartend und setzt die Ausführung erst
dann fort, wenn ein paralleler Thread die Methode #complete(...)
ausführt.
Folgendes Beispiel zeigt, wie man es nicht umsetzen sollte. Das
CompletableFuture wird mit #get()
befragt. Da es keinen parallelen Thread
gibt, der #complete()
aufrufen könnte, wird der Thread ewig warten,
wenn wir #get()
keinen Timeout Wert mitgeben.
@Test
public void dontDoThat() {
CompletableFuture<String> cf = new CompletableFuture<>();
try {
// cf.get() would wait forever! Don´t do that!
cf.get(10, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException
| TimeoutException ex) {
assertThat(ex).isNotNull();
}
}
Erfolgreiche asynchrone Verarbeitung
Das erste Beispiel zeigt eine typische (sehr einfache) asynchrone Verarbeitung.
Die asynchrone Verarbeitung wird in der Methode #calculateAsync()
(siehe [1])
gestartet. CompletableFuture#get()
(siehe [2]) wartet auf die Fertigstellung
der Berechnung und liefert das berechnete Ergebnis.
In [3] wird ein CompletableFuture direkt mit dem berechneten Ergebnis erzeugt.
/**
* Startet einen Thread und übergibt als Synchronisationspunkt ein
* CompletableFuture. Der Thread meldet über CompletableFuture#complete(...)
* die fertige Berechnung.
*/
private CompletableFuture<String> calculateAsync()
throws InterruptedException {
CompletableFuture<String> completableFuture = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete("Hello");
return null;
});
return completableFuture;
}
private CompletableFuture<String> calculateAsync2() {
return CompletableFuture.completedFuture("Hello2");
}
@Test
public void completableFuture() throws Exception {
/* (1) */
CompletableFuture<String> future = calculateAsync();
/* (2) */
String result = future.get();
assertThat(result).isEqualTo("Hello");
assertThat(future.isCompletedExceptionally()).isFalse();
/* (3) */
assertThat(calculateAsync2().get()).isEqualTo("Hello2");
}
Abgebrochene asynchrone Verarbeitung
In dem nächsten Beispiel wird die Verarbeitung über CompletableFuture#cancel(boolean)
abgebrochen. Am Synchronisationspunkt #get()
erhält der Aufrufer eine
CancellationException
. Der Parameter, den man #cancel(boolean)
übergibt, hat
keine Auswirkungen. Der Interrupt-Mechanismus wird für die Ablaufsteuerung
von CompletabeleFutures nicht verwendet.
private CompletableFuture<String> calculateAsyncWithCancellation()
throws InterruptedException {
CompletableFuture<String> completableFuture = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
// Die Verarbeitung wird abgebrochen. Der Aufrufer erhaelt eine
// CancellationException.
completableFuture.cancel(false);
return null;
});
return completableFuture;
}
@Test
public void cancelCompletableFuture()
throws InterruptedException, ExecutionException {
CompletableFuture<String> future = calculateAsyncWithCancellation();
try {
future.get(); // CancellationException
fail("Expected CancellationException");
} catch (CancellationException ex) {
assertThat(future.isCancelled()).isTrue();
assertThat(future.isCompletedExceptionally()).isTrue();
}
}
Utility Methoden
Neben den oben genannten Möglichkeiten bietet CompletableFuture
einen
ganzen Blumenstrauß an weiteren hilfreichen Methoden.
Z.B. #completeAsync(Supplier)
nimmt eine Funktion entgegen, die
asynchron mit Hilfe des ForkJoinPool#commonPool()
verarbeitet wird.
@Test
public void completeAsyncOfCompletableFuture() throws Exception {
CompletableFuture<String> cf = new CompletableFuture<>();
cf.completeAsync(() -> { return "Hallo"; });
assertThat(cf.get()).isEqualTo("Hallo");
}
Mit #runAsync(Runnable)
kann eine parameterlose Funktion übergeben werden.
Ein Rückgabeparameter entfällt ebenfalls. In dem Beispiel verwende
ich eine Variable, die außerhalb der Funktion liegt. Im Sinne der
funktionalen Programmierung ist das nicht optimal. In diesem Fall
wöre #completeAsync()
die bessere Wahl.
@Test
public void runAsyncOfCompletableFuture() throws Exception {
final StringBuilder sb = new StringBuilder();
CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
sb.append("Start calculation ...");
});
cf.get();
assertThat(sb.toString()).isEqualTo("Start calculation ...");
}
Mehrfach Synchronisierung
Um verschiedene nebenläufige Tasks zu einem gemeinsamen Synchronisationspunkt
zu bringen, hat CompletableFuture
eine Lösung zur Verfügung. In dem
Beispiel werden drei CompletableFutures instanziert und per #allOff()
zu einem neuen CompletableFuture zusammen gefasst. Der Aufruf von #get()
wartete so lange, bis alle drei CompletableFutures ein Ergebnis liefern.
@Test
public void completableFutureWithMultipleParallelTasks() throws Exception {
CompletableFuture<String> future1 = CompletableFuture
.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture
.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3 = CompletableFuture
.supplyAsync(() -> "World");
CompletableFuture<Void> combinedFuture = CompletableFuture
.allOf(future1, future2, future3);
combinedFuture.get();
assertThat(future1.isDone()).isTrue();
assertThat(future2.isDone()).isTrue();
assertThat(future3.isDone()).isTrue();
}
Referenzen
Freitag, Oktober 26, 2018
Java 8 und das CompletableFuture
Freitag, Oktober 12, 2018
Links der Woche
- Adam Bien - 55th Airhacks
- Effective Java, Third Edition Keepin' it Effective (J. Bloch)
- JavaScript Pro Tips - Code This, NOT That
Samstag, September 29, 2018
Donnerstag, September 13, 2018
Links der Woche
- Mathematik Apps für das Smartphone. Enthält eine Sammlung von Spielen/Rätseln rund um das Thema Mathematik/Geometrie.
- Was für Dinge benötigt man, wenn man einen Videokanal eröffnen möchte? Hier gibt es ein paar Tipps.
- Die Öffi App ist zurück im Play Store.
- Test Driven Development noch einmal erklärt.
- Ein Artikel auf martinfowler.com über die Aufspaltung eines Datenmonolithen.
- Ein Kurzanleitung wie 1:N Beziehungen in der JPA Welt abzubilden sind.
- Visual Studio Code vs Postman. Ein Rest-Client integriert in der IDE. Das hört sich sehr interessant an. Die Extension findet ihr hier.
Sonntag, September 09, 2018
JPA Entities, equals und hashCode
Wer hat noch nicht mit seinen Kollegen über die ‘korrekte’ Implementierung
von #hashCode()
und #equals()
diskutiert? Hier kommt meine Zusammenfassung.
Grundlagen
Zunächst die Grundlagen: Welche Bedingungen stellt Java an #hashCode()
und #equals()
?
Da wären:
- reflexiv: Für alle x gilt:
x.equals(x)
liefert immer true zurück. - symmetrisch: Für alle x,y gilt:
x.equals(y) == y.equals(x)
- transitiv: Für alle x,y,z gilt:
x.equals(y)
undy.equals(z)
dann giltx.equals(z)
- konsistent: Das Ergebnis von
#hashCode()
und#equals()
muss während der gesamten Lebensdauer eines Objekts gleich sein. Konsistenz kann eigentlich nur für unveränderliche Objekte gelten. Oder sagen wir mal so: Es wäre vermutlich ganz praktisch. Allerdings gibt es hier Tricks. Wie z.B. das die Methode#hashCode()
eine Konstante zurückliefert. Oder das sich der HashCode nur aus den fachlichen Schlüsseln zusammensetzt.
Das sollte als Einführung reichen. In der JPA Community habe ich 3 Stilrichtungen gefunden, die praktikabel erscheinen. Jede mit gewissen Vor- und Nachteilen. Meine persönliche Präferenz, um das mal vorweg zu nehmen, ist die, die den wenigsten Code erzeugt.
Konsens oder ‘no-go’
Die drei Experten (Gavin King, Vlad Mihalcea, Mark Struberg) sind sich in einer Sache
einig. Habe ich eine JPA Entity sollte ich auf gar keinen Fall #equals()
und
#hashCode()
über alle Eigenschaften der Entity ermitteln. Denn wenn ich das tue,
was hätte das für die geforderte Eigenschaft ‘konsistent’ zur Folge? Genau. Die Entity müsste
unveränderlich sein. D.h. die Code-Generatoren der bekannten IDEs und die
Klassen EqualsBuilder
oder HashCodeBuilder
aus Apache Commons sind tabu.
Variante 1. Die Struberg-Methode | Die Nicht-Implementierung.
Struberg hat einen eigenen Blog. Allein
der ist schon empfehlenswert. Der für mich interessante Artikel findet sich
hier.
Die sogenannte Struberg Variante empfiehlt, auf #equals()
und #hashCode
zu
verzichten. Hauptgrund: Die anderen Varianten (die ich später aufzähle) sind
kompliziert und fehleranfällig. Zu dem stellt sich die Frage, benötige
ich überhaupt eine spezielle #hashCode()
Methode für meine Entity?
I.d.R wird dann angeführt, dass die Entities in einem Set
gespeichert
werden. Also z.B.
@OneToMany
private Set others;
Aber selbst hier ist die Defaut-Implementierung aus Object
absolut ausreichend.
Das wird durch zwei Annahmen untermauert. Das erste Argument: Der EntityManager
selbst sorgt für die Eindeutigkeit der Objekte im Set
. Das zweite Argument,
in dem Artikel nicht aufgeführt, aber dennoch interessant: Die Datenbank selbst
sorgt mit Unique-Constraints dafür, dass die Elemente im Set
eindeutig sind.
Allerdings richtig ist der Einwand, dass in diesem Fall das Set
nicht mehr
selbst in der Lage ist, doppelte Objekte zu erkennen. Aber vielleicht ist in diesem
Fall die Validierung in der Geschäftslogik zu lückenhaft?
Wichtig: Das abwägen von Aufwand und Nutzen. Als ‘so-wenig-Code-wie-möglich’ Liebhaber bin ich auf alle Fälle ein Fan dieser Variante.
Variante 2. Hibernate Dokumentation | Der fachliche Schlüssel
In der Hibernate Dokumentation
findet sich die Variante 2. Eine Empfehlung lautet aber auch hier,
eventuell auf die Implementierung von #hashCode()
und #equals()
zu verzichten.
Will oder kann man das nicht, sollte man den natürlichen Schlüssel der Entity bei der
Gestaltung verwenden. Erwähnt wird ebenso, wie bei Struberg oben, dass der
EntityManager oder die Session dafür sorgt, dass gleiche Objekte nur einmal
instanziert werden. Das Problem kann nur dann auftreten, wenn gleiche Objekte
über verschiedene Sessions aus der Datenbank gelesen werden. In diesem Fall
würde das #equals()
, #hashCode()
aus Object
zu kurz greifen. Doch
man muss sich die Frage stellen, habe ich so eine Situation in meiner Anwendung?
In der Dokumentation findet sich der Hinweis, dass das heranziehen allein
der ID
aus der Entity i.d.R. nicht ausreicht. Bei Neuinstanzierung einer
Entity ist diese Eigenschaft nicht gesetzt und wird erst nach einem flush
oder commit
der Session in der Entity gesetzt. Damit hätten wir ebenfalls
eine Verletzung der Konsistenz. Also bleibt nur der natürliche oder
fachliche Schlüssel, der sich nicht ändert und von Anfang an bekannt ist.
In dem Beispiel ist es die ISBN eines Buches. Ich verzichte darauf, dass
Beispiel hier wiederzugeben. Der interessierte folgt einfach dem
Link.
Variante 3. Die Vlad-Methode
Vlad ist der Meinung, dass man #hashCode()
und #equals()
in jedem Fall
implementieren sollte. Er hat für Entities einen sehr guten Test geschrieben,
der entscheidet, ob die gewählte Implementierung den Forderungen von
#hashCode()
und #equals()
und den Anforderungen der JPA Spezifikation
entspricht. Siehe dafür in seinem
GitHub
Account oder direkt in der
Klasse AbstractEqualityCheckTest.java.
Ohne korrekte #hashCode()
und #equals()
Implementierung wird dieser
Test einen Fehler reporten.
In seinem Blog-Artikel unterscheidet Vlad zwischen zwei Typen von Schlüsseln (Identifiables):
Natürliche/Fachliche Schlüssel oder UUIDS
Beiden gemein ist, dass sie bereits vor dem #flush()
(Persistierung) in
die Datenbank bekannt sind und der Entity zugewiesen werden. In diesen Fällen sollte eine Implementierung so aussehen:
@Entity(name = "Book")
@Table(name = "book")
public class Book
implements Identifiable<Long> {
@Id
@GeneratedValue
private Long id;
private String title;
@NaturalId
private String isbn;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return Objects.equals(getIsbn(), book.getIsbn());
}
@Override
public int hashCode() {
return Objects.hash(getIsbn());
}
//Getters and setters omitted for brevity
}
D.h. die Implementierung zieht nur den fachlichen Schlüssel. Alle anderen Eigenschaft werden ignoriert.
Datenbank generierte Schlüssel
In den anderen Fällen, in denen der Schlüssel von der Datenbank generiert wird, empfiehlt er die folgende Implementierung:
@Entity
public class Book implements Identifiable<Long> {
@Id
@GeneratedValue
private Long id;
private String title;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return id != null && id.equals(o.id);
}
@Override
public int hashCode() {
return 31;
}
//Getters and setters omitted for brevity
}
Wichtig: Die #hashCode()
Methode liefert eine Konstante zurück. Damit ist sichergestellt,
dass der gleiche HashCode über alle Entity-Persistenz-Zustände geliefert wird.
Weitere Erkenntnisse
So, having a cache is really a great idea, but please do not store JPA entities in the cache. At least not as long as they are managed.
Der Dirty-Check von Hibernate funktioniert NICHT über die
#equals()
Methode. Hibernate verwaltet ein Duplikat der Entity und fährt über dieses Duplikat einen eigenen Vergleich. Siehe Hibernate dirty check.
Another way is to generated a UUID in the constructor or the getId() method. But this is pretty performance intense and also not very nice to handle on the DB side (large Strings as PK consume a lot more storage in the indexes on disk and in memory).
Referenzen
- https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
- https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
- https://vladmihalcea.com/hibernate-facts-equals-and-hashcode/
- http://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode
- https://github.com/vladmihalcea/high-performance-java-persistence/blob/master/core/src/test/java/com/vladmihalcea/book/hpjp/hibernate/equality/AbstractEqualityCheckTest.java#L55
- https://struberg.wordpress.com/2016/10/15/tostring-equals-and-hashcode-in-jpa-entities
- https://courses.vladmihalcea.com/?utm_source=blog&utm_medium=banner&utm_campaign=article
- Das gleich Buch wie oben, aber bei amazon.de
- Thoughts on Java
Mittwoch, August 29, 2018
Links der Woche
- Schon mal in Eclipse das Problem gehabt, dass im Editor die Tastenkürzel
CTRL-left
undCTRL-right
nicht funktionierten? Dann einfach den Autostarthaken der Eclipse Startseite abwählen und Eclipse neu starten. Dann funktionierts. Problemlösung gefunden auf Stackoverflow. - Rod Johnson, einer der Erfinder von Spring, mit einem Artikel über die Zukunft der 'Software Delivery'.
- Ein Artikel auf Heise über die zukünftigen Releases von Java. Thema ist der halbjährige Release-Zyklus, die kostenpflichtigen Varianten des Oracle JDKs und die Alternativen der Anbieter wie IBM, RedHat oder das OpenJDK.
- Ein Podcast von Adam Bien und Elder Moraes über das langweilige JEE. Bzw. warum JEE so effizient in der Entwicklung von Unternehmensanwendungen ist.
- Microservices sind keine Patentlösung. Vor allem der Ausblick auf die Zukunft von Microservices im letzten Abschnitt des Artikels interessant.
Mittwoch, August 22, 2018
Links der Woche
- Ein Video-Tutorial zur besseren Beherrschung der Bash: LinuxTutorials auf Youtube
- Das agile Leben im digitalen Kapitalismus auf Heise.
- Thonny. Ein minimaler Editor für Pyhon. Optimiert für Anfänger.
- Visual Studio Code ist in der Version 1.26 erschienen.
- Das Problem der Agilität. Toller Artikel über die praktischen und alltäglichen Probleme bei der Umsetzung von agilen Prozessen.
- Hackathons sind Selbstausbeutung. Kann man auch so sehen.
Samstag, August 11, 2018
Links der Woche
- Es gibt ein Thunderbird Update.
- Das Bounded Context Pattern. In dem Artikel findet sich ein Verweis auf MultipleCanonicalModels. Ist das das Ende, der alles vereinheitlichenden Datenmodelle und Modellbegriffe? Vermutlich nicht, da die Ideen schon ein paar Jahre auf dem Buckel haben und es zu reizvoll ist, es dennoch zu versuchen.
-
Schon mal was von der
zsh
gehört? Hier gibt es ein Video zu den Vorzügen. In schriftlicher Kurzform:zsh
kann die gleichen Dinge wie diebash
. Um in andere Verzeichnisse zu wechseln muss man keincd
mehr tippen. Außerdem kann man tiefe Verzeichnispfade in Kurzform eintippen und automatisch erweitern lassen. Hört sich alles praktisch an. Ich werde es ausprobieren. Geht das auch mitcygwin
? Siehe da: Es geht. Und zwar wie hier beschrieben. - Meinen alten Tipp für das öffnen einer cygwin Shell aus Eclipse heraus, muss ich ändern. Auf Stackoverflow findet sich eine bessere Variante.
Montag, Juli 30, 2018
Links der Woche
- Apache´s XMLBeans ist zurück. Das Projekt war bereits auf der Abschussliste und erlebt ein Comeback. Die Meldung und weitere Infos gibt es auf Heise.
- Angular 6 ist (schon lange) da. Die neuen Features in der Zusammenfassung.
- Für die Programmierer von morgen: Codecombat. Das könnte bei meinen Kindern funktionieren.
- Spring Framework 5.0.8 ist erschienen.
- Falls ihr eure Kommunikation verbessern möchtet, dann kann ich diesen Youtube Kanal empfehlen.
Montag, Juli 02, 2018
DTOs mit einem Interface
Frage: Lohnt es sich ein DTO (Data Transfer Object) mit einem Interface zu versehen? Aus dem Bauch heraus würde ich sagen nein. Eine Google-Suche nach 'Interface' und 'DTO' bzw. 'Value Object' scheint meine Annahme zu unterstützen. Siehe Stackexchange oder Stackoverflow. Letzter Beitrag erhielt von den Nutzern des Forums eine schlechte Bewertung. Die Begründung: closed as primarily opinion-based.
Und in dieser Bewertung steckt auch schon die Antwort. Die Antwort auf diese Frage ist stark abhängig von der persönlichen Meinung der beteiligten Person. Es gibt kein eindeutiges wahr oder falsch zu dieser Frage. Ich versuche einfach mal ein paar Argumente zu sammeln.
Wann lohnt es sich, ein Interface zu verwenden? Wenn ich mehr als eine Implementierung habe? Wenn mir nur eine sinnvolle Implementierung für ein Interface einfällt, ist es dann noch in Ordnung ein Interface für die Implementierung anzulegen? Bei einem DTO fällt es mir schwer, eine andere Implementierung als die Default-Implementierung vorzustellen. Was ist die Default-Implementierung für ein DTO? Ein Objekt mit ein oder mehreren Eigenschaften, versehen mit einer getter und/oder setter Methode. Erweitere ich das DTO um eine weitere Eigenschaft, muss ich das Interface ebenfalls erweitern, wenn ich möchte, dass die Eigenschaft nach außen sichtbar ist. Wenn ich das nicht möchte, benötigt das DTO nicht die neue Eigenschaft. Zwei DTOs, die die gleiche Schnittstelle implementieren, werden sich gleich Verhalten. Was für ein anderes Verhalten sollte das zweite DTO im Vergleich zu dem Ersten haben?
Wenn ich mich entscheide, dass ein Service ein DTO zurückliefert, treffe ich die architektonische Entscheidung, das ein Service nur banale DTOs zurückliefert. Ich würde vermutlich der allgemeinen Architekturentscheidung widersprechen, wenn ich was anderes als ein DTO zurückgebe. Insofern habe ich Zweifel, dass ein Interface für ein DTO einen Mehrwert bietet.
Allerdings gibt es keinen kategorischen Grund, Interfaces für DTOs nicht zu definieren. Es entsteht im Zweifel erst einmal nur mehr Code. Obwohl die Codevermeidung kein schlechtes Qualitätsziel ist.
Zum Abschluss habe ich ein etwas ungewöhnliches Beispiel für die Verwendung eines DTOs. Schaut euch mal dazu den folgenden Code an:
public interface DataTransferObject { Long getId(); String getName(); LocalDateTime getBirthday(); default DefaultDataTransferObject newDTO(DataTransferObject dto) { DefaultDataTransferObject ddto = new DefaultDataTransferObject(dto.getId()); ddto.setBirthday(dto.getBirthday()); ddto.setName(dto.getName()); return ddto; } public static class DefaultDataTransferObject implements DataTransferObject { private final Long id; private String name; private LocalDateTime birthday; public DefaultDataTransferObject(Long id) { this.id = id; } @Override public Long getId() { return id; } @Override public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public LocalDateTime getBirthday() { return birthday; } public void setBirthday(LocalDateTime birthday) { this.birthday = birthday; } } } public class Service { public DataTransferObject read(Long id) { return DTOMapper.toDto(repository.find(id)); } public DataTransferObject write(DataTransferObject dto) { Entity entity = repository.find(dto.getId()); DTOMapper.from(dto).to(entity); return DTOMapper.to(entity); } }
Die Idee ist, dass der Service, der das DTO returniert, in seiner Signatur als Return-Typ das Interface verwendet.
Der Aufrufer des Service erhält erst einmal ein nicht veränderliches Objekt. Um Eigenschaften per Setter Methode
zu ändern, muss der Aufrufer newDTO
aufrufen und erhält eine Kopie des DTOs. An diesem kann
er seine Änderungen vornehmen und ggf. das neue DTO einem Service übergeben.
Welchen Mehrwert hat diese Lösung? Nun zunächst steht dem Aufrufer das Original DTO zur Verfügung, welches nicht
verändert werden kann. Das kann praktisch sein, wenn man Änderungen im Client mit dem Original vergleichen möchte.
Ein Argument für dieses Konstrukt ist die explizite Kennzeichnung von Änderungen am DTO. Der Service-Benutzer
muss sich aktiv entscheiden newDTO()
auzurufen und erhält dann ein neues Objekt.
Erst jetzt kann er die Setter-Methoden aufrufen. Ein unveränderliches DTO hat durchaus einen gewissen Mehrwert,
insbesondere wenn man einen funktionalen Programmierstil pflegt.
Donnerstag, Juni 28, 2018
Links der Woche
- Eclipse PHOTON ist da.
- WHATWG über Fortschritte und Ideen bei der Standardisierung von HTML. Hier finden sich interessierte Menschen, die eher einen pragmatischen Ansatz einsetzen, um die Standardisierung von HTML voranzutreiben. Also ein Gegenentwurf zum W3C. Interessant finde ich das Motto: Please leave your sense of logic at the door, thanks!
- Die Javascript Engine Nashorn soll aus dem JDK entfernt werden. Damit geht Oracle den Weg konsequent weiter bei der Entschlackung des JDKs.
- Einen kurzen Blog-Post, wann es sich lohnt einen Testfall zu automatisieren.
- Eine Buchempfehlung: 'Wie man sie alle rumkriegt' von Scott Adams. Genau. Das ist der Erfinder der Dilbert-Comics, der den Wahlsieg von Trump vorhergesagt hat und dafür gehasst wird.
- Und noch eine Buchempfehlung: Homo Deus, Eine Geschichte der Zeit. Ich habe mir die Hörbuchversion angehört. Sehr unterhaltend, die Schlussfolgerungen nachvollziehbar, natürlich eher populärwissenschaftlich. Anhand der Geschichte des Menschen versucht der Autor die Zukunft vorherzusagen.
Donnerstag, Juni 14, 2018
Die Links der Woche...
- Die Browser unterstützen endlich Modules. Eine Überblick verschafft der Artikel Basics of modular Javascript. Verwendet jemand diese Konzepte bereits in seinem Produktionscode?
- Schon mal was von Processing gehört? Ich bin schon lange auf der Suche nach einer Programmiersprache, die meine Kinder etwas mehr fesselt. Nach einem Selbstversuch habe ich leise Zweifel, dass Processing Begeisterung bei den Kids weckt. Rechtecke, Ellipsen. Sehr schön. Aber nicht spannend. Hat schon mal jemand Minecraft Educations ausprobiert? Das Lizenz Modell erscheint mir etwas umständlich. Mit Lego Mindstorms bzw. in Kombination mit Open Roberta hatte ich kleine Erfolge. Ebenfalls gut aufgenommen wurde Blockly. Eine echte intrinsische Eigeninitiative ist beim Nachwuchs allerdings nicht entstanden.
- Nach dem Aufschrei über die Veräußerung von GitHub an Microsoft, habe ich mir mal GitLab angeschaut. Auf den ersten Blick eine 1:1 Kopie von GitHub. Allerdings bietet GitLab eine Docker Registry an. Das finde ich interessant.
- Der Schachblog der FAZ.
Freitag, Juni 08, 2018
Links der Woche (EE8, Github, Microsoft, Angular)
- Wildfly (ehemals JBoss) liefert eine erste, fast vollständige, EE8 kompatible Version aus.
Ein guter Zeitpunkt um mal in die EE8 Neuerungen reinzuschauen.
Große Versionssprünge gibt es in den Modulen
- 'Java Servlet' (3.1 nach 4.0)
- 'Contexts and Dependency Injection for Java' (1.2 nach 2.0)
- und 'Bean Validation' (1.2 nach 2.0).
-
Der Angular Update Guide: Update Angular. Ein kleines Wunder. Und das
npm install rxjs@6 rxjs-compat@6 --save
soll auch helfen. - Microsoft übernimmt GitHub. Welche Strategie steckt dahinter? Wie will Microsoft mit Github Geld verdienen? Vielleicht sind es die Daten, die Github über uns gesammelt hat? Oder die Plattform GitHub an sich? GitHub hat Sourceforge als führende Plattform für OpenSource Code abgelöst und die Idee des kollaborativen Arbeiten mit Hilfe von GIT ins Web gebracht. Meinen ersten Pull-Request habe ich mit GitHub erstellt (oder war es Bitbucket?). Zu dem hat Microsoft mit VSCode und Typescript (mindestens) zwei sehr bekannte Projekte auf der Plattform. Insgesamt besitzt Microsoft 1836 Repositories auf Github. Auf Heise gibt es hier und hier einen kritischen Bericht zu der Übernahme.
- Thema Finanzen: Der Finanzwesir beleuchtet die Frage nach dem optimalen ETF und warum diese Frage bzw. die Suche nach einer Antwort vermutlich nur Zeitverschwendung ist.
UPDATE: Zu dem Thema CDI habe ich einen alten Artikel von Adam Bien auf Oracle.com gefunden. Es wird Zeit, dass ich mich mit dem Thema mehr beschäftige.
UPDATE: Jetzt weiß ich auch, warum mein CDI Kenntnisse eher sporadischer Natur sind. Spring unterstützt CDI gar nicht erst vollumfänglich. Und wird es vermutlich auch in Zukunft nicht.
Dienstag, Mai 29, 2018
Links der Woche
- Eine Sammlung der 75 besten(?) Blogs über das Testen von Software: abtstracta.us/blog/75-best-software-testing-blogs
- Werden manuelle Tests benötigt? Eine Antwort versucht dieser Blog zu geben.
- Und hier ein Beitrag zu dem Thema, wann ist Testautomatisierung sinnvoll.
- Ein Post über das Löschen von automatisierten Test.
- Wie ist mit nicht-deterministischen Tests umzugehen? Dieser Blog versucht das Thema zu beleuchten.
- Google´s Guava ist mittlerweile ein alter Hut und mit Java 8 sind viele Ideen aus Guava mit ins JDK eingeflossen. Dennoch lohnt sich ein Blick. Beispiel: Ranges, Throwables, Graphs
- Wiremock simuliert HTTP basierte APIs. Interessant ist die Wiremock API selber. Die Aufrufe für das Starten/Stoppen und Stubben der Methoden kann man bequem im Testcode integrieren oder man startet Wiremock als eigenständigen Service.
- Zahlen-, Datums- oder Währungskonvertierungen in Javascript? Dann empfiehlt sich Globalize.
- Ein Texteditor für das Web. Im Mittelpunkt der Funktionalität steht das gemeinsame, gleichzeitige Arbeiten an einem Dokument.
- Fefes Blog. Kennt ihr schon, oder?
- Brewcraft, die wahre Kunst Bier zu brauen. Hier gibt es alles für den Hobbybrauer. Mit einem interessanten Artikel über alkoholfreie Biere.
-
Das
java.util.Optional
lässt mich nicht in Ruhe. Auf der einen Seite bietet es neben#isPresent()
nette Abkürzungen wie#ifPresent()
,#orElse()
oder#orElseThrows()
. Auf der anderen Seite habe ich im Alltag das Gefühl, dass dasOptional
eine inflationäre Verwendung findet. Im www fand ich dazu die folgenden Artikel.- Nothing is better than optional. Zeigt die Pros und Cons zu dem Thema auf.
- Ein ausführlicher Artikel zu Optionals.
- Und hier ein tiefgründiger Text, warum
Optional
'broken' ist.
- Never, ever, use null for an Optional variable or return value.
- Never use Optional.get() unless you can prove that the Optional is present.
- Prefer alternative APIs over Optional.isPresent() and Optional.get().
- It’s generally a bad idea to create an Optional for the specific purpose of chaining methods from it to get a value.
- If an Optional chain has a nested Optional chain, or has an intermediate result of Optional, it’s probably too complex.
- Avoid using Optional in fields, method parameters, and collections.On a related note, I thought of another rule after I presented the session: Don’t use an Optional to wrap any collection type (List, Set, Map). Instead, use an empty collection to represent the absence of values.
Dienstag, Mai 22, 2018
Hibernate und java.util.Date
java.util.Date
Typ einer Eigenschaft aus einer Hibernate Entity. Dieses Datumsfeld wurde für einen Vergleich mit einem anderen Datum herangezogen. Als Beispiel dient die Entity Person
mit dem Datumsfeld #geburtstag
. Die interessanten Dinge finden sich in (1) und (2). Bevor die Entity in der Datenbank angelegt wird, ist alles wie erwartet. Das java.util.Date
ist ein java.util.Date
. Der Vergleich liefert ebenfalls ein positives Ergebnis.
@Entity
@Table("PERSON")
public class Person {
@Id
@Column(name = "PERSON_ID")
private Long personId;
@Column(name = "NAME")
private String name;
@Column(name = "VORNAME")
private String vorname;
@Column(name = "GEBURTSTAG")
private java.util.Date geburtstag;
...
}
final Date date = new DateTime(2018, 5, 22, 17, 0, 0).toDate();
Person person = new Person();
person.setGeburtstag(date);
person.setName("Name");
person.setVorname("Vorname");
// (1) Vergleich vor #save() und #refresh()
assertThat(person.getGeburtstag()).isInstanceOf(Date.class);
assertThat(person.getGeburtstag()).isNotInstanceOf(java.sql.Timestamp.class);
assertThat(person.getGeburtstag()).isEqualTo(date);
getSessionFactory().getCurrentSession().saveOrUpdate(person);
getSessionFactory().getCurrentSession().refresh(person);
// (2) Vergleich nach #save() und #refresh()
assertThat(person.getGeburtstag()).isInstanceOf(Date.class);
assertThat(person.getGeburtstag()).isInstanceOf(java.sql.Timestamp.class);
assertThat(person.getGeburtstag()).isNotEqualTo(date);
// IS NOT EQUAL, weil java.sql.Timestamp!
assertThat(now).isEqualTo(newRound.getDateTime());
// IS EQUAL! java.util.Date benutzt für den Vergleich long getTime()
Nach dem persistieren der Daten geschehen die bemerkenswerten Dinge. Aus dem java.util.Date
wird ein java.sql.Timestamp
. Das wäre nicht weiter tragisch, wenn nicht der Vergleich assertThat(person.getGeburtstag()).isNotEqualTo(now);
positiv ausfallen würde.
Was ist passiert? Mit dem refresh
wurde die Entity Person
neu aus der Datenbank gelesen, d.h. Hibernate liest per JDBC Verbindung die Daten aus der Datenbank. Für eine Timestamp
Spalte lautet der entsprechende JDBC Typ java.sql.Timestamp
, welcher von java.util.Date
abgeleitet ist. In der #equals()
Methode von java.sql.Timestamp
findet sich der Hinweis:
true if the given Object is an instance of a Timestamp that is equal
to this Timestamp object; false otherwise
D.h. ein Vergleich von java.sql.Timestamp
und java.util.Date
ist immer false
. Umgekehrt funktioniert der Vergleich: assertThat(now).isEqualTo(newRound.getDateTime());
, da java.util.Date#equals() für den Vergleich auf das Ergebnis der Methode #getTime()
zugreift. Das beste ist, man umgeht dieses Problem und verwendet gleich das java.time.LocalDateTime
aus JDK 8 oder höher. Falls diese Option nicht besteht, empfiehlt sich für den Vergleich die #compare()
Methode. java.sql.Timestamp
implementiert #compare(Timestamp)
und ein #compare(Date)
.
Donnerstag, Mai 17, 2018
Mockito Stubbing
Mockito gibt dem Entwickler zwei Möglichkeiten an die Hand, Methoden zu stubben. Variante 1, und vermutlich die beliebteste, ist die when/thenReturn
Variante. Die Variante 2 doReturn/when
dreht die Syntax um. Ein wesentlicher Unterschied zwischen den beiden Methoden ist die Typsicherheit der Variante 1.
when(mockOfClassXy.methodCall(anyParam())).thenReturn(anObject);
Der Typ von anObject
muss dem Rückgabetyp der Methode #methodCall(...)
entsprechen. Im Gegensatz zu der Variante 2
doReturn(anObject).when(mockOfClassXy).methodCall(anyParam());
kann anObject
ein beliebiger Typ sein. Der Fehler wird erst zur Laufzeit entdeckt. Für das Stubben von void
Methoden muss auf Variante 2 zurückgegriffen werden. Statt doReturn
wird doNothing
verwendet. Beispiel:
doNothing().when(mockOfClassA).voidMethodCall(anyParam());
Das stört bei strikter Verwendung von Variante 1 den Lesefluß. Teilweise gibt es hier die Empfehlung ganz auf Variante 1 zu verzichten und komplett auf Variante 2 umzusteigen. Wie gesagt, würde man dann auf die Typprüfung verzichten. Insbesondere bei ‘großen’ Projekten mit langen Testausführungszeiten wäre das vermutlich ein Problem.
Auf Stackoverflow finden sich zu dem Thema massig Einsendungen. Hier ein Beispiel:
Mockito when/thenReturn
vs doReturn/when
Links der Woche
- CSV Dateien verarbeiten mit Javascript? Vielleicht verrückt. Hier ist eine Lösung: papaparse
- Auf Heise gibt es einen Kommentar zu den aktuellen Problemen mit EFail und der Aufregung, dass PGP kaputt sei: heise.de
- Eine Javascript Bibliothek zum Zeichnen von Diagrammen. Die Diagramme versuchen möglichst handgemalt auszusehen. Das lässt das Ganze weniger steril wirken: Rough.js
- Für C++ Programmierer ein Muss. Der CPP Core Guide von und mit Bjarne Stroustrup zu finden auf GitHub:
- Eine Alternative zu Angular und anderen -eher schwergewichtigen- Frameworks. cujojs.com. Cujo orientiert sich wieder mehr an den Web-Standards und an den Möglichkeiten, die Javascript bietet.
- Fear of the Dark. Interpretiert von den Mozart Heros: Fear of the Dark
AssertJ und java.util.List
AssertJ hat eine praktische Möglichkeit, Listen in JUnit Tests abzuprüfen. Insbesondere, wenn in der Liste komplexe Objekte abgelegt sind, s...
-
Wer das erste mal einen klassischen Rest-Service mit Spring schreibt, also einen @RestController implementiert, der JSON (application/json;...
-
Die Tastensteuerung im VI kann ich mir nur grob granular merken. Deswegen dieser Versuch, die Sachen einmal aufzuschreiben und natürlich neb...
-
DOS-Box: Next Generation Wer als Entwickler behauptet, er arbeitet unter Windows, der erntet von seinen Linux/MacOS Kollegen ein müdes läch...