Spring JPA出现could not deserialize反序列化错误(EOFException)求助
问题:Spring JPA原生查询反序列化失败(org.springframework.orm.jpa.JpaSystemException: could not deserialize)
在Spring JPA开发中,调用TeamRepo的自定义原生查询方法(如getAll、findTeam)时,抛出org.springframework.orm.jpa.JpaSystemException: could not deserialize异常,根因为java.io.EOFException。
实体类代码
@Entity @Table(name = "report_queue") public class Team implements Serializable { @Id private Long id; private String queue; private Number offered; private Number answered; private Number answerRate; @Column(name="abandoned") private Number abandoned; private Number unmanaged; private String avgAbandonTime; @Temporal(TemporalType.TIMESTAMP) private Date totalTalkTime; private Number avgTalkTime; public Team() { String queue; Number offered; Number answered; Number answerRate; Number abandoned; Number unmanaged; String avgAbandonTime; Date totalTalkTime; Number avgTalkTime; } public Team(Long id, String queue, Number answered, Number offered, Number answerRate, Number abandoned, String avgAbandonTime, Date totalTalkTime, Number avgTalkTime, Number unmanaged) { this.id = id; this.queue = queue; this.answered = answered; this.offered = offered; this.answerRate = answerRate; this.abandoned = abandoned; this.avgAbandonTime = avgAbandonTime; this.totalTalkTime = totalTalkTime; this.avgTalkTime = avgTalkTime; this.unmanaged=unmanaged; } // getters and setters.. }
Repository代码
public interface TeamRepo extends JpaRepository<Team, Long>{ @Query(value="select q.id, q.queue,q.offered,q.answered,q.answerRate,q.abandoned,q.unmanaged,q.totalTalkTime,q.avgTalkTime,q.avgAbandonTime from(SELECT id as \"id\", queue AS \"queue\", COUNT(DISTINCT uniqueid) AS \"offered\", SUM(queuecallercomplete) AS \"answered\", ROUND(SUM(queuecallercomplete)/COUNT(DISTINCT uniqueid)*100,1) AS \"answerRate\", SUM(queuecallerabandon) AS \"abandoned\", SUM(queuecallerexit) AS \"unmanaged\", SUM(UNIX_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt)) AS \"totalTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt))) AS \"avgTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallerAbandonAt)-UNIX_TIMESTAMP(queuecallerjoinAt))) AS \"avgAbandonTime\" FROM report_queue GROUP BY queue)q",nativeQuery = true) List<Team> getAll(); @Query(value="SELECT id as \"id\", queue AS \"queue\", COUNT(DISTINCT uniqueid) AS \"offered\", SUM(queuecallercomplete) AS \"answered\", ROUND(SUM(queuecallercomplete)/COUNT(DISTINCT uniqueid)*100,1) AS \"answerRate\", SUM(queuecallerabandon) AS \"abandoned\", SUM(queuecallerexit) AS \"unmanaged\", SUM(UNI X_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt)) AS \"totalTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt))) AS \"avgTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallerAbandonAt)-UNIX_TIMESTAMP(queuecallerjoinAt))) AS \"avgAbandonTime\" FROM report_queue where GROUP BY queue",nativeQuery = true) public List<Team> findTeam(); void deleteTeamById(Long id); Optional<Team> findTeamById(Long id); }
错误栈信息
ERROR 87656 --- [nio-8090-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: could not deserialize; nested exception is org.hibernate.type.SerializationException: could not deserialize] with root cause java.io.EOFException: null
排查与解决方案
核心问题分析
- 字段类型不匹配:
totalTalkTime在SQL中是时间差的秒数(数值型),但实体类中定义为Date类型,Hibernate无法将数值反序列化为Date,直接引发转换异常。 - Number类型的不确定性:使用抽象类型
Number作为数值属性,Hibernate无法确定具体的子类(Integer/Double等),反序列化时无法正确映射。 - 无参构造函数不规范:实体类的无参构造函数内部声明了局部变量,未初始化实例属性,不符合JPA实体类规范,可能导致实例化异常。
- SQL语法错误:
findTeam方法的SQL中存在where GROUP BY的无效语法,导致SQL执行返回异常结果,间接引发反序列化失败。
具体修复步骤
1. 修正实体类字段类型与构造函数
将抽象的Number替换为具体数值类型,修正totalTalkTime的类型为Long(存储秒数总和),并规范无参构造函数:
@Entity @Table(name = "report_queue") public class Team implements Serializable { @Id private Long id; private String queue; private Integer offered; private Integer answered; private Double answerRate; @Column(name="abandoned") private Integer abandoned; private Integer unmanaged; private Double avgAbandonTime; // 原SQL返回数值,替换String为Double private Long totalTalkTime; // 替换Date为Long,存储秒数总和 private Double avgTalkTime; // 规范的无参构造函数 public Team() { } // 构造函数参数与修改后的字段类型匹配 public Team(Long id, String queue, Integer answered, Integer offered, Double answerRate, Integer abandoned, Double avgAbandonTime, Long totalTalkTime, Double avgTalkTime, Integer unmanaged) { this.id = id; this.queue = queue; this.answered = answered; this.offered = offered; this.answerRate = answerRate; this.abandoned = abandoned; this.avgAbandonTime = avgAbandonTime; this.totalTalkTime = totalTalkTime; this.avgTalkTime = avgTalkTime; this.unmanaged=unmanaged; } // getters and setters.. }
2. 修复Repository中的SQL错误
移除findTeam方法SQL中多余的where关键字,并修正UNI X_TIMESTAMP的拼写错误:
public interface TeamRepo extends JpaRepository<Team, Long>{ @Query(value="select q.id, q.queue,q.offered,q.answered,q.answerRate,q.abandoned,q.unmanaged,q.totalTalkTime,q.avgTalkTime,q.avgAbandonTime from(SELECT id as \"id\", queue AS \"queue\", COUNT(DISTINCT uniqueid) AS \"offered\", SUM(queuecallercomplete) AS \"answered\", ROUND(SUM(queuecallercomplete)/COUNT(DISTINCT uniqueid)*100,1) AS \"answerRate\", SUM(queuecallerabandon) AS \"abandoned\", SUM(queuecallerexit) AS \"unmanaged\", SUM(UNIX_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt)) AS \"totalTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt))) AS \"avgTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallerAbandonAt)-UNIX_TIMESTAMP(queuecallerjoinAt))) AS \"avgAbandonTime\" FROM report_queue GROUP BY queue)q",nativeQuery = true) List<Team> getAll(); @Query(value="SELECT id as \"id\", queue AS \"queue\", COUNT(DISTINCT uniqueid) AS \"offered\", SUM(queuecallercomplete) AS \"answered\", ROUND(SUM(queuecallercomplete)/COUNT(DISTINCT uniqueid)*100,1) AS \"answerRate\", SUM(queuecallerabandon) AS \"abandoned\", SUM(queuecallerexit) AS \"unmanaged\", SUM(UNIX_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt)) AS \"totalTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallercompleteAt)-UNIX_TIMESTAMP(queuecallerleaveAt))) AS \"avgTalkTime\", ROUND(AVG(UNIX_TIMESTAMP(queuecallerAbandonAt)-UNIX_TIMESTAMP(queuecallerjoinAt))) AS \"avgAbandonTime\" FROM report_queue GROUP BY queue",nativeQuery = true) List<Team> findTeam(); void deleteTeamById(Long id); Optional<Team> findTeamById(Long id); }
3. 额外建议
如果原生查询返回的结果不需要作为持久化实体操作,建议使用DTO(数据传输对象)接收结果,避免实体类与查询结果的强绑定,进一步降低反序列化风险。
内容的提问来源于stack exchange,提问作者Kiran Basavapattana
相关产品推荐
相关产品推荐

