JPA преобразуватели на атрибути

1. Въведение

В тази бърза статия ще разгледаме използването на преобразувателите на атрибути, налични в JPA 2.1 - което, просто казано, ни позволява да картографираме типовете JDBC в класове Java.

Тук ще използваме Hibernate 5 като наше изпълнение на JPA.

2. Създаване на конвертор

Ще покажем как да реализираме преобразувател на атрибути за персонализиран клас Java.

Първо, нека създадем клас PersonName - който ще бъде преобразуван по-късно:

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

След това, ние ще добавим атрибут на тип PERSONNAME до @Entity клас:

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

Сега трябва да създадем конвертор, който преобразува атрибута PersonName в колона на базата данни и обратно. В нашия случай ще преобразуваме атрибута в стойност String, която съдържа както полета за име, така и за фамилия.

За целта трябва да анотираме нашия конвертор клас с @Converter и да приложим интерфейса AttributeConverter . Ще параметризираме интерфейса с типовете на класа и колоната на базата данни, в този ред:

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

Забележете, че трябваше да приложим 2 метода: convertToDatabaseColumn () и convertToEntityAttribute ().

Двата метода се използват за преобразуване от атрибута в колона на база данни и обратно.

3. Използване на конвертора

За да използваме нашия конвертор, просто трябва да добавим анотацията @Convert към атрибута и да посочим класа на конвертора, който искаме да използваме :

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

И накрая, нека създадем единичен тест, за да видим дали наистина работи.

За целта първо съхраняваме обект Person в нашата база данни:

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

След това ще тестваме, че PersonName се съхранява, както сме го дефинирали в конвертора - чрез извличане на това поле от таблицата на базата данни:

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

Да проверим също така, че преобразуването от стойността, съхранена в базата данни в клас PersonName, работи както е дефинирано в конвертора, като напишем заявка, която извлича целия клас 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. Заключение

В този кратък урок показахме как да използваме нововъведените преобразуватели на атрибути в JPA 2.1.

Както винаги, пълният изходен код за примерите е достъпен в GitHub.