【问题标题】:Why implementing an Hibernate @OneToMany relationship I am obtaining this error? " Repeated column in mapping for entity: XXX.Address column: id"为什么要实现 Hibernate @OneToMany 关系我收到此错误? “实体映射中的重复列:XXX.Address 列:id”
【发布时间】:2021-11-05 18:15:03
【问题描述】:

我正在使用 Spring Data JPA 开发一个 Spring Boot 应用程序,我发现实现 Hibernate 映射存在以下问题。

基本上我首先在我的数据库上创建了表,然后我创建了映射这些表的实体类(在我的项目中,数据库表不是从实体类自动生成的)。

我有这两个数据库表:

  1. portal_user:这是包含代表门户用户的记录的主表。

     CREATE TABLE IF NOT EXISTS public.portal_user
     (
         id bigint NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
         first_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
         middle_name character varying(50) COLLATE pg_catalog."default",
         surname character varying(50) COLLATE pg_catalog."default" NOT NULL,
         sex "char" NOT NULL,
         birthdate date NOT NULL,
         tex_code character varying(50) COLLATE pg_catalog."default" NOT NULL,
         e_mail character varying(50) COLLATE pg_catalog."default" NOT NULL,
         contact_number character varying(50) COLLATE pg_catalog."default" NOT NULL,
         created_at date NOT NULL,
         CONSTRAINT user_pkey PRIMARY KEY (id)
     )
    
  2. 地址:它包含代表以前用户地址的记录。每个用户可以有一个或多个地址。

     CREATE TABLE IF NOT EXISTS public.address
     (
         id bigint NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
         country character varying(50) COLLATE pg_catalog."default" NOT NULL,
         province character varying(50) COLLATE pg_catalog."default" NOT NULL,
         zip_code character varying(5) COLLATE pg_catalog."default" NOT NULL,
         street character varying(250) COLLATE pg_catalog."default" NOT NULL,
         notes character varying(250) COLLATE pg_catalog."default",
         fk_user_id bigint NOT NULL,
         CONSTRAINT address_pkey PRIMARY KEY (id),
         CONSTRAINT fk_user_id FOREIGN KEY (fk_user_id)
             REFERENCES public.portal_user (id) MATCH SIMPLE
             ON UPDATE NO ACTION
             ON DELETE NO ACTION
             NOT VALID
     )
    

如您所见,每个用户可以有一个或多个地址,因此 portal_useraddress 之间的关系是 ONE TO MANY(因为一个用户可以有多个地址)。为实现此行为,address 包含 fk_user_id 字段,表示特定 portal_user 记录的 ID。

好的,那么我正在尝试使用 Hibernate 映射这两个表及其关系。

首先我创建了 User 实体类:

@Entity
@Table(name = "portal_user")
@Data
public class User implements Serializable {
     
    private static final long serialVersionUID = 5062673109048808267L;
    
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    
    @Column(name = "first_name")
    private String firstName;
    
    @Column(name = "middle_name")
    private String middleName;
    
    @Column(name = "surname")
    private String surname;
    
    @Column(name = "sex")
    private char sex;
    
    @Column(name = "birthdate")
    private Date birthdate;
    
    @Column(name = "tex_code")
    private String taxCode;
    
    @Column(name = "e_mail")
    private String eMail;
    
    @Column(name = "contact_number")
    private String contactNumber;
    
    @Temporal(TemporalType.DATE)
    @Column(name = "created_at")
    private Date createdAt;
    
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
    @JsonManagedReference
    private Set<Address> addressesList = new HashSet<>();

    public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
            String eMail, String contactNumber, Date createdAt) {
        super();
        this.firstName = firstName;
        this.middleName = middleName;
        this.surname = surname;
        this.sex = sex;
        this.birthdate = birthdate;
        this.taxCode = taxCode;
        this.eMail = eMail;
        this.contactNumber = contactNumber;
        this.createdAt = createdAt;
    }
        

}

如您所见,该类包含定义到 portal_user 数据库表和此 OneToMany 关系中的所有字段。基本上它说一个用户与多个地址有关:

@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
@JsonManagedReference
private Set<Address> addressesList = new HashSet<>();

然后我创建了 Address 实体类映射我的 DB address 表,这个:

@Entity
@Table(name = "address")
@Data
@NoArgsConstructor
@AllArgsConstructor 
public class Address implements Serializable {
     
    private static final long serialVersionUID = 6956974379644960088L;
    
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    
    @Column(name = "country")
    private String country;
    
    @Column(name = "province")
    private String province;
    
    @Column(name = "zip_code")
    private String zipCode;
    
    @Column(name = "street")
    private String street;
    
    @Column(name = "notes")
    private String notes;
    
    //@Column(name = "fk_user_id")
    //private int fkUserId;
    
    @ManyToOne
    @EqualsAndHashCode.Exclude          // Needed by Lombock in "Many To One" relathionship to avoid error
    @JoinColumn(name = "id", referencedColumnName = "id")
    @JsonBackReference
    private User user;

    public Address(String country, String province, String zipCode, String street, String notes, User user) {
        super();
        this.country = country;
        this.province = province;
        this.zipCode = zipCode;
        this.street = street;
        this.notes = notes;
        this.user = user;
    }
    
}

像往常一样,它包含 address 数据库表的映射,还包含将此类链接到之前的 User 类的 ManyToOne 关系:

@ManyToOne
@EqualsAndHashCode.Exclude          // Needed by Lombock in "Many To One" relathionship to avoid error
@JoinColumn(name = "id", referencedColumnName = "id")
@JsonBackReference
private User user;

基本上它表示 Address 的多个实例链接到 User 的单个特定实例。

为了完整起见,这只是我的存储库接口(目前它是空的,因为我只测试 JpaRepository 提供的 save() 方法):

public interface UsersRepository extends JpaRepository<User, Integer> {

}

最后我创建了这个单元测试类,以测试插入具有相关地址的新用户:

@SpringBootTest()
@ContextConfiguration(classes = GetUserWsApplication.class)
@TestMethodOrder(OrderAnnotation.class)
public class UserRepositoryTest {
    
    @Autowired
    private UsersRepository userRepository;
    
    
    @Test
    @Order(1)
    public void testInsertUser() {
        User user = new User("Mario", null, "Rossi", 'M', new Date(), "XXX", "xxx@gmail.com", "329123456", new Date());
        
        userRepository.save(user);
        
        Set<Address> addressesList = new HashSet<>();
        addressesList.add(new Address("Italy", "RM", "00100", "Via XXX 123", "near YYY", user));
        
        assertTrue(true);
        
    }
    
}

问题是运行此测试方法时,我在堆栈跟踪中收到以下错误消息:

Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Repeated column in mapping for entity: com.easydefi.users.entity.Address column: id (should be mapped with insert="false" update="false")
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421) ~[spring-orm-5.3.12.jar:5.3.12]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-5.3.12.jar:5.3.12]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.3.12.jar:5.3.12]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.12.jar:5.3.12]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.12.jar:5.3.12]
    ... 84 common frames omitted
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.easydefi.users.entity.Address column: id (should be mapped with insert="false" update="false")
    at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:862) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:880) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:902) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:634) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.mapping.RootClass.validate(RootClass.java:267) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:354) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:298) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:468) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) ~[spring-orm-5.3.12.jar:5.3.12]
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.3.12.jar:5.3.12]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-5.3.12.jar:5.3.12]
    ... 88 common frames omitted

为什么会出现这个错误?我错过了什么?我该如何解决这个错误?

【问题讨论】:

    标签: java spring-boot hibernate spring-data-jpa nhibernate-mapping


    【解决方案1】:

    这是你的双重映射

    @ManyToOne
    @EqualsAndHashCode.Exclude          // Needed by Lombock in "Many To One" relathionship to avoid error
    @JoinColumn(name = "id", referencedColumnName = "id")
    private User user;
    

    应该是:

    @ManyToOne
    @EqualsAndHashCode.Exclude          // Needed by Lombock in "Many To One" relathionship to avoid error
    @JoinColumn(name = "fk_user_id", referencedColumnName = "id")
    private User user;
    

    您正在映射id 两次(第一次出现是@Id 列名),根据您的DDL,外键列应该是fk_user_id

    【讨论】:

    • 我该怎么做才能解决我的问题?我要删除什么?
    • 应该是@JoinColumn(name = "fk_user_id" 而不是@JoinColumn(name = "id"
    【解决方案2】:

    您似乎在地址中映射了同一列两次

     @EqualsAndHashCode.Exclude          // Needed by Lombock in "Many To One" relathionship to avoid error
     @JoinColumn(name = "id", referencedColumnName = "id")
     @JsonBackReference
     private User user;
    

        @Id
        @Column(name = "id")
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private int id;
    

    您可能想在第一个中说 user_id 但不能确定,因为我认为您两次粘贴相同的 SQL 来创建表,而不是地址之一。
    编辑: 尝试在地址中的用户用户映射中使用 user_id 而不是 id,错误应该消失了。

    【讨论】:

    • 对不起,我编辑了我的原始帖子,为“地址”表插入了正确的 SQL
    猜你喜欢
    • 1970-01-01
    • 2020-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-12
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    相关资源
    最近更新 更多