Convertisseurs d'attributs JPA

1. Introduction

Dans cet article rapide, nous allons couvrir l'utilisation des convertisseurs d'attributs disponibles dans JPA 2.1 - qui, en termes simples, nous permettent de mapper les types JDBC aux classes Java.

Nous utiliserons Hibernate 5 comme implémentation JPA ici.

2. Création d'un convertisseur

Nous allons montrer comment implémenter un convertisseur d'attributs pour une classe Java personnalisée.

Commençons par créer une classe PersonName - qui sera convertie plus tard:

public class PersonName implements Serializable { private String name; private String surname; // getters and setters }

Ensuite, nous ajouterons un attribut de type PersonName à une classe @Entity :

@Entity(name = "PersonTable") public class Person { private PersonName personName; //... }

Nous devons maintenant créer un convertisseur qui transforme l' attribut PersonName en colonne de base de données et vice-versa. Dans notre cas, nous allons convertir l'attribut en une valeur String qui contient à la fois les champs nom et prénom.

Pour ce faire, nous devons annoter notre classe de convertisseur avec @Converter et implémenter l' interface AttributeConverter . Nous paramétrerons l'interface avec les types de la classe et de la colonne de base de données, dans cet ordre:

@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; } }

Notez que nous avons dû implémenter 2 méthodes: convertToDatabaseColumn () et convertToEntityAttribute ().

Les deux méthodes sont utilisées pour convertir l'attribut en colonne de base de données et vice-versa.

3. Utilisation du convertisseur

Pour utiliser notre convertisseur, il suffit d'ajouter l' annotation @Convert à l'attribut et de spécifier la classe de convertisseur que nous voulons utiliser :

@Entity(name = "PersonTable") public class Person { @Convert(converter = PersonNameConverter.class) private PersonName personName; // ... }

Enfin, créons un test unitaire pour voir qu'il fonctionne vraiment.

Pour ce faire, nous allons d'abord stocker un objet Person dans notre base de données:

@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(); }

Ensuite, nous allons tester que le PersonName a été stocké tel que nous l'avons défini dans le convertisseur - en récupérant ce champ à partir de la table de base de données:

@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); }

Testons également que la conversion de la valeur stockée dans la base de données vers la classe PersonName fonctionne comme défini dans le convertisseur en écrivant une requête qui récupère toute la classe Person :

@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. Conclusion

Dans ce bref didacticiel, nous avons montré comment utiliser les convertisseurs d'attributs nouvellement introduits dans JPA 2.1.

Comme toujours, le code source complet des exemples est disponible à l'adresse over sur GitHub.