我正在尝试使用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
处共享了相同的代码这里可能缺少什么?
答案 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
上注册ObjectMapper
。
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的更多信息。
答案 2 :(得分:0)
杰克逊在这里完全无关紧要;当您说要使用Serializable
时,是在谈论 Java序列化,而无论出于何种原因,Optional
都不是Serializable
。 / p>
对于在持久实体中这样的“可选”属性,推荐的方法是将实际值存储为可为空的字段,并在getter和setter中进行转换。
答案 3 :(得分:0)