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, которое содержит поля имени и фамилии.
Для этогоwe have to annotate our converter class with @Converter and implement the AttributeConverter interface. Мы параметризуем интерфейс типами класса и столбца базы данных в следующем порядке:
@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. Использование конвертера
To use our converter, we just need to add the @Convert annotation to the attribute and specify the converter class we want to use:
@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.
Как всегда, доступен полный исходный код примеровover on GitHub.