You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data查询出现ORA-01722: invalid number错误求助

解决Spring Data中ORA-01722错误并通过@EmbeddedId获取Department主键

首先,咱们先拆解下问题:ORA-01722本质是字符串与数字类型不匹配导致的隐式转换失败——你的原生SQL是手动关联了两张表的非主键字段(类型匹配),但Spring Data自动生成的查询可能错误地把某个字符串字段和Department的数字类型主键deptno做了关联,触发了转换错误。用@EmbeddedId的思路是对的,尤其是当你的关联关系基于非主键字段,且需要明确映射复合关联条件时,下面是分步实现方案:

1. 定义@Embeddable的复合关联键类

首先创建一个实现Serializable的嵌入式类,用来封装两张表之间的非主键关联字段(这是避免类型不匹配的核心)。假设你的关联字段是Employee表的deptCode(字符串类型)和Department表的deptCode(同类型),代码如下:

import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;

@Embeddable
public class DeptAssociationId implements Serializable {
    private String deptCode; // 这里是你的非主键关联字段,类型要和两张表的对应字段完全一致

    // 必须要有无参构造器(JPA要求)
    public DeptAssociationId() {}

    public DeptAssociationId(String deptCode) {
        this.deptCode = deptCode;
    }

    // 重写equals和hashCode(复合键必须实现)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DeptAssociationId that = (DeptAssociationId) o;
        return Objects.equals(deptCode, that.deptCode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(deptCode);
    }

    // getter和setter
    public String getDeptCode() {
        return deptCode;
    }

    public void setDeptCode(String deptCode) {
        this.deptCode = deptCode;
    }
}

2. 修改实体类的映射关系

(1)调整Department实体

确保Department的非主键关联字段(比如deptCode)被正确映射,同时保留主键deptno:

import jakarta.persistence.*;

@Entity
@Table(name = "DEPARTMENT")
public class Department {
    @Id
    @Column(name = "DEPTNO")
    private Integer deptno; // 你的数字类型主键

    @Column(name = "DEPT_CODE")
    private String deptCode; // 非主键关联字段

    // 其他字段、构造器、getter/setter省略
}

(2)调整关联实体(比如Employee)

用@EmbeddedId标记复合键,再通过@MapsId和@ManyToOne关联到Department,确保关联基于非主键字段:

import jakarta.persistence.*;

@Entity
@Table(name = "EMPLOYEE")
public class Employee {
    @EmbeddedId
    private DeptAssociationId id; // 嵌入式复合键

    // 通过复合键中的deptCode关联到Department的deptCode
    @ManyToOne
    @JoinColumn(name = "DEPT_CODE", referencedColumnName = "DEPT_CODE", insertable = false, updatable = false)
    @MapsId("deptCode") // 关联复合键中的deptCode字段
    private Department department;

    // 其他业务字段、构造器、getter/setter省略

    // 示例:获取Department的主键deptno
    public Integer getDepartmentDeptno() {
        return department != null ? department.getDeptno() : null;
    }
}

3. 编写Spring Data Repository查询

现在你可以正常编写Repository方法,Spring Data会基于嵌入式键的正确关联字段生成SQL,避免类型不匹配:

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface EmployeeRepository extends JpaRepository<Employee, DeptAssociationId> {
    // 示例:查询所有关联了Department的Employee,同时能获取Department的deptno
    List<Employee> findAllByDepartmentIsNotNull();
}

关键注意事项

  • 字段类型严格匹配:确保嵌入式键中的关联字段类型和数据库表字段完全一致(比如都是VARCHAR2,不要一边是String一边是Integer),这是解决ORA-01722的核心。
  • @MapsId的作用:它会将实体中的关联字段与嵌入式键中的字段绑定,避免重复映射。
  • Serializable必须实现:嵌入式键类必须实现Serializable,否则JPA会抛出序列化异常。

如果你的关联场景更复杂(比如多个非主键字段关联),只需要在DeptAssociationId中添加对应字段,并在@MapsId中指定多个字段即可。

内容的提问来源于stack exchange,提问作者Angular_Newbie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:29:26