JPA Attribute Converters – JPA属性转换器

最后修改: 2018年 2月 13日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this quick article, we’ll cover the usage of the Attribute Converters available in JPA 2.1 – which, simply put, allow us to map JDBC types to Java classes.

在这篇文章中,我们将介绍JPA 2.1中可用的属性转换器的用法–简单地说,它允许我们将JDBC类型映射到Java类。

We’ll use Hibernate 5 as our JPA implementation here.

在这里我们将使用Hibernate 5作为我们的JPA实现。

2. Creating a Converter

2.创建一个转换器

We’re going to show how to implement an attribute converter for a custom Java class.

我们将展示如何为一个自定义的Java类实现一个属性转换器。

First, let’s create a PersonName class – that will be converted later:

首先,让我们创建一个PersonName类–这将在后面进行转换。

public class PersonName implements Serializable {

    private String name;
    private String surname;

    // getters and setters
}

Then, we’ll add an attribute of type PersonName to an @Entity class:

然后,我们将向@Entity类添加一个PersonName类型的属性。

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

    //...
}

Now we need to create a converter that transforms the PersonName attribute to a database column and vice-versa. In our case, we’ll convert the attribute to a String value that contains both name and surname fields.

现在我们需要创建一个转换器,将PersonName属性转换为数据库列,反之亦然。在我们的案例中,我们将把该属性转换为一个String值,其中包含姓名两个字段。

To do so we have to annotate our converter class with @Converter and implement the AttributeConverter interface. We’ll parametrize the interface with the types of the class and the database column, in that order:

为此,我们必须用@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;
    }
}

Notice that we had to implement 2 methods: convertToDatabaseColumn() and convertToEntityAttribute().

请注意,我们必须实现2个方法。convertToDatabaseColumn()convertToEntityAttribute()。

The two methods are used to convert from the attribute to a database column and vice-versa.

这两种方法用于从属性到数据库列的转换,反之亦然。

3. Using the Converter

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:

为了使用我们的转换器,我们只需要在属性中添加@Convert注解,并指定我们要使用的转换器类

@Entity(name = "PersonTable")
public class Person {

    @Convert(converter = PersonNameConverter.class)
    private PersonName personName;
    
    // ...
}

Finally, let’s create a unit test to see that it really works.

最后,让我们创建一个单元测试,看看它是否真的有效。

To do so, we’ll first store a Person object in our database:

要做到这一点,我们首先要在数据库中存储一个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();
}

Next, we’re going to test that the PersonName was stored as we defined it in the converter – by retrieving that field from the database table:

接下来,我们将测试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);
}

Let’s also test that the conversion from the value stored in the database to the PersonName class works as defined in the converter by writing a query that retrieves the whole Person class:

让我们也来测试一下,从数据库中存储的值到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. Conclusion

4.结论

In this brief tutorial, we showed how to use the newly introduced Attribute Converters in JPA 2.1.

在这个简短的教程中,我们展示了如何使用JPA 2.1中新引入的属性转换器。

As always, the full source code for the examples is available over on GitHub.

一如既往,这些示例的完整源代码可在GitHub上获得