java.util的NotSerializableException。对于Jackson是可选的

时间:2019-08-26 04:07:31

标签: java spring-boot jackson

我正在尝试使用JPA保存实体,但为类型Optional的字段获取NotSerializableException。 在这里,我的目标是以JSON序列化形式存储自定义对象(此处为Grade类型)。

[Student.java]

@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Size(min = 3, max = 20)
    private String name;

    @Lob
    private Grade grade;

    public Student() {
    }

[Grade.java]

public class Grade implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 7351334541533041431L;
    private Optional<Integer> marks;

    public Optional<Integer> getMarks() {
        return marks;
    }

    public void setMarks(Optional<Integer> marks) {
        this.marks = marks;
    }

    @Override
    public String toString() {
        return "Grade [marks=" + marks + "]";
    }

}

[StudentRepository.java]

public interface StudentRepository extends JpaRepository<Student, Long> {

    public Student findByName(String name);
}

[StudentRepositoryTest.java]

@RunWith(SpringRunner.class)
@DataJpaTest
public class StudentRepositoryTest {
    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private StudentRepository studentRepository;

    @Test
    public void whenFindByName_thenReturnEmployee() {

        // given
        Student alex = new Student("alex");
        Grade grade = new Grade();
        grade.setMarks(Optional.ofNullable(90));
        alex.setGrade(grade);

        entityManager.persist(alex);
        entityManager.flush();

        // when
        Student found = studentRepository.findByName(alex.getName());

        System.out.println(found);
        // then
        assertThat(found.getName()).isEqualTo(alex.getName());
    }
}

运行上述测试后,在marks类的Grade字段中获得以下异常:

Caused by: java.io.NotSerializableException: java.util.Optional
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at org.hibernate.internal.util.SerializationHelper.serialize(SerializationHelper.java:115)
    ... 49 more

我包括以下依赖项以支持Jackson的java8数据类型:

<dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jdk8</artifactId>
        </dependency>

春季启动版本:2.1.6.RELEASE

https://github.com/Omkar-Shetkar/spring-repo-test

处共享了相同的代码

这里可能缺少什么?

4 个答案:

答案 0 :(得分:3)

似乎Hibernate尝试序列化Grade对象,并且由于它使用默认的序列化算法,因此它尝试序列化Optional<Integer> marks字段。

现在,在Java中,可选项不可序列化:请参见https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

所以序列化失败。

由于不打算在数据字段中使用“可选”项(出于某些原因,请参见[此讨论] 1),请考虑删除一个可选项,并保留Integer marks

答案 1 :(得分:1)

是的,Optional不是Serializable。 JPA尝试序列化标记为String的非文本字段(char[]Char[]@Lob以外的任何字段)。

推荐的方法是使用JPA转换器将对象序列化为Json,同时持久化并在读取时反序列化持久化的Json。 Jackson支持序列化Optional字段,您只需要添加jackson-datatype-jdk8依赖项并在Jdk8Module上注册ObjectMapperGradeConverter实现如下:

@Converter
public class GradeConverter implements AttributeConverter<Grade, String> {

    private ObjectMapper objectMapper;

    public GradeConverter() {
        objectMapper = new ObjectMapper();
        objectMapper.registerModule(new Jdk8Module());
    }

    @Override
    public String convertToDatabaseColumn(Grade grade) {

        if (grade == null) {
            return null;
        }

        return objectMapper.writeValueAsString(grade);
    }

    @Override
    public Grade convertToEntityAttribute(String s) {

        if (s == null) {
            return null;
        }

        return objectMapper.readValue(s, Grade.class);
    }
}

在您的Grade字段上:

@Convert(converter = GradeConverter.class)
private Grade grade;

如果您坚持序列化字段(我的意思是Java序列化),则应提供自定义序列化方法。 您可以阅读有关here的更多信息。

答案 2 :(得分:0)

杰克逊在这里完全无关紧要;当您说要使用Serializable时,是在谈论 Java序列化,而无论出于何种原因,Optional都不是Serializable。 / p>

对于在持久实体中这样的“可选”属性,推荐的方法是将实际值存储为可为空的字段,并在getter和setter中进行转换。

答案 3 :(得分:0)

com.google.common.base 还具有可序列化的Optional类:

Java Doc

Maven Repo