JPA-Attributkonverter

JPA-Attributkonverter

1. Einführung

In diesem kurzen Artikel behandeln wir die Verwendung der in JPA 2.1 verfügbaren Attributkonverter, mit denen wir einfach ausgedrückt JDBC-Typen Java-Klassen zuordnen können.

Wir werden hier Hibernate 5 als JPA-Implementierung verwenden.

2. Konverter erstellen

Wir werden zeigen, wie ein Attributkonverter für eine benutzerdefinierte Java-Klasse implementiert wird.

Erstellen wir zunächst einePersonName-Klasse, die später konvertiert wird:

public class PersonName implements Serializable {

    private String name;
    private String surname;

    // getters and setters
}

Anschließend fügen wir einer@Entity-Klasse ein Attribut vom TypPersonName hinzu:

@Entity(name = "PersonTable")
public class Person {

    private PersonName personName;

    //...
}

Jetzt müssen wir einen Konverter erstellen, der das AttributPersonNamein eine Datenbankspalte umwandelt und umgekehrt. In unserem Fall konvertieren wir das Attribut in einenString-Wert, der sowohl Vor- als auch Nachnamenfelder enthält.

Dazuwe have to annotate our converter class with @Converter and implement the AttributeConverter interface. Wir parametrisieren die Schnittstelle mit den Typen der Klasse und der Datenbankspalte in der folgenden Reihenfolge:

@Converter
public class PersonNameConverter implements
  AttributeConverter {

    private static final String SEPARATOR = ", ";

    @Override
    public String convertToDatabaseColumn(PersonName personName) {
        if (personName == null) {
            return null;
        }

        StringBuilder sb = new StringBuilder();
        if (personName.getSurname() != null && !personName.getSurname()
            .isEmpty()) {
            sb.append(personName.getSurname());
            sb.append(SEPARATOR);
        }

        if (personName.getName() != null
          && !personName.getName().isEmpty()) {
            sb.append(personName.getName());
        }

        return sb.toString();
    }

    @Override
    public PersonName convertToEntityAttribute(String dbPersonName) {
        if (dbPersonName == null || dbPersonName.isEmpty()) {
            return null;
        }

        String[] pieces = dbPersonName.split(SEPARATOR);

        if (pieces == null || pieces.length == 0) {
            return null;
        }

        PersonName personName = new PersonName();
        String firstPiece = !pieces[0].isEmpty() ? pieces[0] : null;
        if (dbPersonName.contains(SEPARATOR)) {
            personName.setSurname(firstPiece);

            if (pieces.length >= 2 && pieces[1] != null
              && !pieces[1].isEmpty()) {
                personName.setName(pieces[1]);
            }
        } else {
            personName.setName(firstPiece);
        }

        return personName;
    }
}

Beachten Sie, dass wir zwei Methoden implementieren mussten:convertToDatabaseColumn() undconvertToEntityAttribute().

Die beiden Methoden werden zum Konvertieren des Attributs in eine Datenbankspalte und umgekehrt verwendet.

3. Konverter verwenden

To use our converter, we just need to add the @Convert annotation to the attribute and specify the converter class we want to use:

@Entity(name = "PersonTable")
public class Person {

    @Convert(converter = PersonNameConverter.class)
    private PersonName personName;

    // ...
}

Erstellen wir abschließend einen Komponententest, um festzustellen, ob er wirklich funktioniert.

Dazu speichern wir zuerst einPerson-Objekt in unserer Datenbank:

@Test
public void givenPersonName_whenSaving_thenNameAndSurnameConcat() {
    String name = "name";
    String surname = "surname";

    PersonName personName = new PersonName();
    personName.setName(name);
    personName.setSurname(surname);

    Person person = new Person();
    person.setPersonName(personName);

    Long id = (Long) session.save(person);

    session.flush();
    session.clear();
}

Als Nächstes testen wir, obPersonName so gespeichert wurde, wie wir es im Konverter definiert haben - indem wir dieses Feld aus der Datenbanktabelle abrufen:

@Test
public void givenPersonName_whenSaving_thenNameAndSurnameConcat() {
    // ...

    String dbPersonName = (String) session.createNativeQuery(
      "select p.personName from PersonTable p where p.id = :id")
      .setParameter("id", id)
      .getSingleResult();

    assertEquals(surname + ", " + name, dbPersonName);
}

Testen Sie außerdem, ob die Konvertierung von dem in der Datenbank gespeicherten Wert in die KlassePersonNamewie im Konverter definiert funktioniert, indem Sie eine Abfrage schreiben, die die gesamte KlassePersonabruft:

@Test
public void givenPersonName_whenSaving_thenNameAndSurnameConcat() {
    // ...

    Person dbPerson = session.createNativeQuery(
      "select * from PersonTable p where p.id = :id", Person.class)
        .setParameter("id", id)
        .getSingleResult();

    assertEquals(dbPerson.getPersonName()
      .getName(), name);
    assertEquals(dbPerson.getPersonName()
      .getSurname(), surname);
}

4. Fazit

In diesem kurzen Tutorial wurde gezeigt, wie die neu eingeführten Attributkonverter in JPA 2.1 verwendet werden.

Wie immer ist der vollständige Quellcode für die Beispieleover on GitHub verfügbar.