Перейти к основному содержимому

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

· 3 мин. чтения

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 в столбец базы данных и наоборот. В нашем случае мы преобразуем атрибут в строковое значение, содержащее поля имени и фамилии.

Для этого нам нужно аннотировать наш класс конвертера с помощью @Converter и реализовать интерфейс AttributeConverter . Мы параметризуем интерфейс типами класса и столбца базы данных в следующем порядке:

@Converter
public class PersonNameConverter implements
AttributeConverter<PersonName, String> {

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 .