杰克逊的 java.util.Optional 的 NotSerializableException [重复]
Posted
技术标签:
【中文标题】杰克逊的 java.util.Optional 的 NotSerializableException [重复]【英文标题】:NotSerializableException for java.util.Optional with Jackson [duplicate] 【发布时间】:2019-08-26 04:07:31 【问题描述】:我正在尝试使用 JPA 保存一个实体,但为 Optional 类型的字段获取 NotSerializableException
。
这里我的目标是以 JSON 序列化形式存储自定义对象(这里是 Grade
类型)。
[学生.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()
[等级.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
类中的 marks
字段出现以下异常:
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>
Spring boot 版本:2.1.6.RELEASE
在https://github.com/Omkar-Shetkar/spring-repo-test分享了相同的代码
这里可能缺少什么?
【问题讨论】:
【参考方案1】:看起来 Hibernate 尝试序列化您的 Grade 对象,并且由于它使用默认的序列化算法,它尝试序列化 Optional<Integer> marks
字段。
现在,在 Java 中 Optional 不可序列化:请参阅 https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
所以序列化失败了。
由于 Optional 不打算在数据字段中使用(原因请参阅 [此讨论]1),请考虑删除一个可选项,留下 Integer marks
)
【讨论】:
实际上我的目标是将自定义对象以 JSON 序列化形式存储到表中。自定义对象还包含 Optional 之外的其他字段。似乎hibernate正在使用Java序列化来存储对象。我可以改用杰克逊提供的 JSON 序列化吗? 我不太熟悉 Hibernate/JPA,但这似乎是相关的:***.com/questions/25738569/…【参考方案2】:是的,Optional
不是 Serializable
。 JPA 尝试序列化标有@Lob
的非文本(除String
、char[]
或Char[]
之外的任何内容)字段。
推荐的方法是使用 JPA 转换器将对象序列化为 Json,同时在读取时持久化和反序列化持久化的 Json。 Jackson 支持序列化 Optional 字段,您只需添加 jackson-datatype-jdk8
依赖项并在您的 ObjectMapper
上注册 Jdk8Module
。
GradeConverter
的实现如下所示:
@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 的信息。
【讨论】:
【参考方案3】:杰克逊在这里完全无关紧要;当你说你想使用Serializable
时,你说的是Java 序列化,不管出于什么原因,Optional
不是Serializable
。
对于持久实体中此类“可选”属性的推荐方法是将实际值存储为可空字段并在 getter 和 setter 中进行转换。
【讨论】:
【参考方案4】:com.google.common.base 也有 serializable 的 Optional 类:
Java Doc
Maven Repo
【讨论】:
虽然链接可以提供有用的信息,但最好多描述一下解决方案。 只需要添加[依赖]mvnrepository.com/artifact/com.google.guava/guava并将导入包从java optional改为guava optional即可。以上是关于杰克逊的 java.util.Optional 的 NotSerializableException [重复]的主要内容,如果未能解决你的问题,请参考以下文章
UnsatisfiedDependencyException java.lang.IllegalArgumentException:查询方法公共抽象 java.util.Optional 的验证失败
com.fasterxml.jackson.databind.JsonMappingException:java.util.Optional 不能转换为 java.time.LocalDate