Convertitori di attributi JPA

1. Introduzione

In questo rapido articolo, tratteremo l'utilizzo dei convertitori di attributi disponibili in JPA 2.1, che, in poche parole, ci consentono di mappare i tipi JDBC alle classi Java.

Useremo Hibernate 5 come implementazione JPA qui.

2. Creazione di un convertitore

Mostreremo come implementare un convertitore di attributi per una classe Java personalizzata.

Innanzitutto, creiamo una classe PersonName , che verrà convertita in seguito:

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

Quindi, aggiungeremo un attributo di tipo PersonName a una classe @Entity :

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

Ora dobbiamo creare un convertitore che trasformi l' attributo PersonName in una colonna di database e viceversa. Nel nostro caso, convertiremo l'attributo in un valore String che contiene i campi nome e cognome.

Per fare ciò dobbiamo annotare la nostra classe convertitore con @Converter e implementare l' interfaccia AttributeConverter . Parametrizzeremo l'interfaccia con i tipi della classe e della colonna del database, in quest'ordine:

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

Si noti che abbiamo dovuto implementare 2 metodi: convertToDatabaseColumn () e convertToEntityAttribute ().

I due metodi vengono utilizzati per convertire dall'attributo a una colonna del database e viceversa.

3. Utilizzo del convertitore

Per utilizzare il nostro convertitore, dobbiamo solo aggiungere l' annotazione @Convert all'attributo e specificare la classe del convertitore che vogliamo utilizzare :

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

Infine, creiamo uno unit test per vedere che funziona davvero.

Per fare ciò, memorizzeremo prima un oggetto Person nel nostro database:

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

Successivamente, testeremo che PersonName sia stato memorizzato come lo abbiamo definito nel convertitore, recuperando quel campo dalla tabella del database:

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

Testiamo anche che la conversione dal valore memorizzato nel database alla classe PersonName funzioni come definito nel convertitore scrivendo una query che recuperi l'intera 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. Conclusione

In questo breve tutorial, abbiamo mostrato come utilizzare i convertitori di attributi appena introdotti in JPA 2.1.

Come sempre, il codice sorgente completo per gli esempi è disponibile su GitHub.