Рубрики
Без рубрики

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

Взгляните на сопоставление типов JDBC с классами Java в JPA с помощью преобразователей атрибутов.

Автор оригинала: Marcos Lopez Gonzalez.

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
}

Затем мы добавим атрибут типа Имя человека в класс @Entity :

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

    //...
}

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