| Home | Techniek | Keywords


Programmeren

Java

JaxB marshalling

Het probleem

Bij het marshallen van een JAXB object komt de merkwaardige foutmelding "javax.xml.bind.JAXBException: package.Class is not known to this context". De class Class is wel te vinden, maar toch gaat het fout.

De oorzaak

Het bleek dat er meerdere WSDL’s waren waarmee code was gegenereerd. In de pom.xml waren de WSDL’s allemaal in een execution gemaakt. Die WSDL’s hadden in de zelfde namespace verschillende classes waardoor er een verkeerde ObjectFactory is gemaakt.

De oplossing

Voor elke WSDL een nieuwe packagename laten genereren, dit kan door meerdere executions te gebruiken. Zie http://stackoverflow.com/questions/17674456 En ja, de pom.xml wordt wel erg groot.

Lessons learned

Maak WSDL’s zorgvuldig: niet de zelfde namespace gebruiken voor verschillende dingen.

Transacties loggen in JPA

Het is niet altijd even duidelijk wanneer welke transactie actief is.

Code

private static final Map<Integer, String> STATUS_MAP = Map.of(
    0, "STATUS_ACTIVE",
    1, "STATUS_MARKED_ROLLBACK",
    2, "STATUS_PREPARED",
    3, "STATUS_COMMITTED",
    4, "STATUS_ROLLEDBACK",
    5, "STATUS_UNKNOWN",
    6, "STATUS_NO_TRANSACTION",
    7, "STATUS_PREPARING",
    8, "STATUS_COMMITTING",
    9, "STATUS_ROLLING_BACK"
);

@Resource
private TransactionSynchronizationRegistry txReg;

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void doeIets() {
  int txStatus = txReg.getTransactionStatus();
  String status = STATUS_MAP.get(txStatus) + " (" + txStatus + ")";
  LOG.info(String.format("txStatus = {%s}, txKey = {%s}", status, txReg.getTransactionKey()));
  // de echte code
}

Probleem

Een commandLink in JSF werkte niet.

Oplossing

Het bleek dat de link in een <ui:repeat> zat. Waar het erg vaak af te raden is om <c:forEach> te gebruiken, was het hier wel de oplossing.

Date naar LocalDate conversie (en terug)

Probleem

LocalDate en Date moeten vaak van de een naar de ander omgezet worden, een heel makkelijke methode is er niet Oplossing

LocalDate naar Date

LocalDate localDate = ...
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());

Maar, het kan nog makkelijker:

Date date = java.sql.Date.valueOf(localDate);

Date naar LocalDate

Date date = ...
LocalDate ld = new java.sql.Date(date.getTime()).toLocalDate();

Het kan ook weer langer:

Date date = ...
LocalDate ld = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();

Zeg eens eerlijk, Date komt toch niet meer voor in je code?

JSF FacesContext testen

Testen van controllers met een FacesContext is wat gedoe, maar als je de test overerft van onderstaande test, krijg je geen NullPointerException bij FacesContext.getCurrentInstance().get…​()

@RunWith(MockitoJUnitRunner.class)
public abstract class FacesContextMockingTest {
    @BeforeClass
    public static void facesContextSetup() {
        FacesContext facesContext = TestFacesContext.mockFacesContext();
        ExternalContext external = mock(ExternalContext.class);
        when(facesContext.getExternalContext()).thenReturn(external);
        assertThat(FacesContext.getCurrentInstance()).isEqualTo(facesContext);
    }

}

abstract class TestFacesContext extends FacesContext {
    public static FacesContext mockFacesContext() {
        FacesContext context = mock(FacesContext.class);
        setCurrentInstance(context);
        return context;
    }
}

Prachtige code

Exception throws Exception

Een beetje programmeur weet hoe hij een exception moet maken, maar niet de programmeur van dit stukje code:

public class FlutException extends Exception {
  public FlutException(String message) throws FlutException {
    super(message);
  }
}

Een exception die zichzelf throwed?

Maven

Verander de versie van het hele project

mvn versions:set -DnewVersion=<nieuwe-versie>

Oracle en Hibernate

Trage queries

Hibernate is erg goed in bij elkaar harken van gegevens, maar…​ Een @OneToMany met een eager kan erg traag worden, voor elk object in de @OneToMany property wordt een aparte query gedaan. Dit valt op te lossen door eager weg te laten en in de queries waar je de @OneToMany werkelijk nodig hebt het volgende te zetten: "LEFT JOIN FETCH a.books b "

Dubbele nummertjes

Het probleem

De applicatie draaide prima. Geen enkele klacht was ons ter ore gekomen. Plotseling kregen we vreemde foutmeldingen in onze ontwikkelomgeving: er was een DB constraint waar we tegenaan liepen. Al snel bleek het de primary key te zijn. Uit de code bleek dat we netjes een uniek getal uit een Oracle sequence haalden. Toch zagen we in de database records met een id dat een paar honderd hoger was dan de laatste sequence.

Het duurde even voor we het lek boven hadden. De sequence in Oracle was gedefinieerd met een increment van één, dus elke keer als iemand een nieuwe waarde opvroeg werd het getal met één verhoogd.

In de Hibernate configuratie stond een increment_size van 1000 en een optimzer die 'pooled-lo' was. Uit de documentatie bleek dat Hibernate in dat geval een getal uit de sequence haalt en daar intern door mee gaat tellen tot hij 1000 getallen had uitgegeven, daarna zou Hibernate pas een nieuw getal op vragen.

We hadden meerdere applicaties draaien in onze ontwikkelomgeving. Het is mij een raadsel hier niemand eerder tegenaan is gelopen. In alle andere omgevingen ging het wel goed, daar was de sequence in Oracle wel gedefinieerd met een increment van 1000.

Uitleg

Hibernate heeft een optimalizatie bedacht waardoor zij niet telkens aan de database om een sequencenummer moet vragen. Hibernate vraagt een nummer, en hoogt dat zelf een aantal keer op. Pas als er een 'increment' aantal keer een nieuw nummer is uitgegeven, vraagt Hibernate weer een nieuw nummer. Dit gaat natuurlijk alleen goed als de database een zelfde aantal nummers overslaat als de database.

Dit geldt voor Hibernate versie 3 en 4.

Lessons learned

Kijk naar de specs, wat bedoelen ze, snap je het.

Performance issues oplossen is leuk, maar bedenk dat dat altijd weer andere problemen oplevert. Dit is een mooi geval van optimalisatie die verkeerd uitpakt. Wellicht heb je iets minder communicatie met je database, maar de kans op fouten is een stuk groter.