【问题标题】:Mapping Issue for Spring Boot Application when Joining Tables for Entities为实体连接表时 Spring Boot 应用程序的映射问题
【发布时间】:2021-10-21 06:30:37
【问题描述】:

我的数据库中有两个连接的表,我正在尝试在 Spring 应用程序中使用 @ManyToMany 关系将它们与它们各自的实体连接起来(参见下面的照片)。

不幸的是,当我尝试这样做时,映射器进入了无限循环。这肯定是由于我如何加入我的实体,但我无法从在线阅读中弄清楚如何。有谁知道我要去哪里错了?下面是我的代码。请注意,为简洁起见,Controller、Service 和 Repository 层都被省略了。

实体

@Data
@Entity
@Table(name = "M_Advisor")
public class AdvisorEntity {

        @Id
        @Column(name = "ADVISOR_ID")
        private Long advisorId;

        @Column(name = "FIRST_NAME")
        private String firstName;

        @ManyToMany(fetch = FetchType.EAGER,
                        targetEntity = org.example.entities.contracts.ContractEntity.class)
        @JoinTable(name = "M_CONTRACT", joinColumns = @JoinColumn(name = "ADVISOR_ID"),
                        inverseJoinColumns = @JoinColumn(name = "CONTRACT_ID"))      
        private Collection<ContractEntity> contracts;
}
@Entity
@Data
@Table(name = "M_Contract")
public class ContractEntity {

    @Id
    @Column(name = "CONTRACT_ID")
    private Long contractId;

    @Column(name = "ADVISOR_ID")
    private Long advisorId;

    @ManyToMany(mappedBy = "contracts",
            targetEntity = org.example.entities.advisors.AdvisorEntity.class,
            fetch = FetchType.EAGER)
    private Collection<AdvisorEntity> advisors;
}

映射器

@Mapper
public interface AdvisorMapper {
    AdvisorDTO advisorEntityToAdvisorDTO(AdvisorEntity advisorEntity);

    AdvisorEntity advisorDTOToAdvisorEntity(AdvisorDTO advisorDto);
}
@Mapper
public interface ContractMapper {
    ContractDTO contractEntityToContractDTO(ContractEntity contractEntity);

    ContractEntity contractDTOToContractEntity(ContractDTO contractDto);

}

AdvisorMapperImpl 注意:这是自动生成的,但我删除了一些属性。需要时会注明正确的行号。

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-08-19T11:13:35-0400",
    comments = "version: 1.4.1.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-6.8.jar, environment: Java 11.0.9.1 (AdoptOpenJDK)"
)
@Component
public class AdvisorMapperImpl implements AdvisorMapper {

    @Override
    public AdvisorDTO advisorEntityToAdvisorDTO(AdvisorEntity advisorEntity) {
        if ( advisorEntity == null ) {
            return null;
        }

        AdvisorDTO advisorDTO = new AdvisorDTO();

        advisorDTO.setAdvisorId( advisorEntity.getAdvisorId() );
        advisorDTO.setFirstName( advisorEntity.getFirstName() );

// Line 44
        advisorDTO.setContracts( contractEntityCollectionToContractDTOCollection( advisorEntity.getContracts() ) );

        return advisorDTO;
    }

    @Override
    public AdvisorEntity advisorDTOToAdvisorEntity(AdvisorDTO advisorDto) {
        if ( advisorDto == null ) {
            return null;
        }

        AdvisorEntity advisorEntity = new AdvisorEntity();

        advisorEntity.advisorId( advisorDto.getAdvisorId() );
        advisorEntity.firstName( advisorDto.getFirstName() );
        advisorEntity.contracts( contractDTOCollectionToContractEntityCollection( advisorDto.getContracts() ) );

        return advisorEntity;
    }

    protected Collection<AdvisorDTO> advisorEntityCollectionToAdvisorDTOCollection(Collection<AdvisorEntity> collection) {
        if ( collection == null ) {
            return null;
        }

        Collection<AdvisorDTO> collection1 = new ArrayList<AdvisorDTO>( collection.size() );
        for ( AdvisorEntity advisorEntity : collection ) {

// Line 87
            collection1.add( advisorEntityToAdvisorDTO( advisorEntity ) );
        }

        return collection1;
    }

    protected ContractDTO contractEntityToContractDTO(ContractEntity contractEntity) {
        if ( contractEntity == null ) {
            return null;
        }

        ContractDTO contractDTO = new ContractDTO();

        contractDTO.setContractId( contractEntity.getContractId() );
        contractDTO.setAdvisorId( contractEntity.getAdvisorId() );
        contractDTO.setDetails( contractEntity.getDetails() );

//Line 139
        contractDTO.setAdvisors( advisorEntityCollectionToAdvisorDTOCollection( contractEntity.getAdvisors() ) );

        return contractDTO;
    }

    protected Collection<ContractDTO> contractEntityCollectionToContractDTOCollection(Collection<ContractEntity> collection) {
        if ( collection == null ) {
            return null;
        }

        Collection<ContractDTO> collection1 = new ArrayList<ContractDTO>( collection.size() );
        for ( ContractEntity contractEntity : collection ) {

// Line 151
            collection1.add( contractEntityToContractDTO( contractEntity ) );
        }

        return collection1;
    }

    protected Collection<AdvisorEntity> advisorDTOCollectionToAdvisorEntityCollection(Collection<AdvisorDTO> collection) {
        if ( collection == null ) {
            return null;
        }

        Collection<AdvisorEntity> collection1 = new ArrayList<AdvisorEntity>( collection.size() );
        for ( AdvisorDTO advisorDTO : collection ) {
            collection1.add( advisorDTOToAdvisorEntity( advisorDTO ) );
        }

        return collection1;
    }

    protected ContractEntity contractDTOToContractEntity(ContractDTO contractDTO) {
        if ( contractDTO == null ) {
            return null;
        }

        ContractEntity contractEntity = new ContractEntity();

        contractEntity.contractId( contractDTO.getContractId() );
        contractEntity.advisorId( contractDTO.getAdvisorId() );
        contractEntity.advisors( advisorDTOCollectionToAdvisorEntityCollection( contractDTO.getAdvisors() ) );
        contractEntity.details( contractDTO.getDetails() );


        return contractEntity;
    }

    protected Collection<ContractEntity> contractDTOCollectionToContractEntityCollection(Collection<ContractDTO> collection) {
        if ( collection == null ) {
            return null;
        }

        Collection<ContractEntity> collection1 = new ArrayList<ContractEntity>( collection.size() );
        for ( ContractDTO contractDTO : collection ) {
            collection1.add( contractDTOToContractEntity( contractDTO ) );
        }

        return collection1;
    }
}

Stackoverflow 错误(没有双关语)

java.lang.StackOverflowError: null
        at org.example.entities.advisors.AdvisorMapperImpl.contractEntityCollectionToContractDTOCollection(AdvisorMapperImpl.java:149) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.advisorEntityToAdvisorDTO(AdvisorMapperImpl.java:44) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.advisorEntityCollectionToAdvisorDTOCollection(AdvisorMapperImpl.java:87) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.contractEntityToContractDTO(AdvisorMapperImpl.java:139) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.contractEntityCollectionToContractDTOCollection(AdvisorMapperImpl.java:151) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.advisorEntityToAdvisorDTO(AdvisorMapperImpl.java:44) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.advisorEntityCollectionToAdvisorDTOCollection(AdvisorMapperImpl.java:87) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.contractEntityToContractDTO(AdvisorMapperImpl.java:139) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.contractEntityCollectionToContractDTOCollection(AdvisorMapperImpl.java:151) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.advisorEntityToAdvisorDTO(AdvisorMapperImpl.java:44) ~[main/:na]
        at org.example.entities.advisors.AdvisorMapperImpl.advisorEntityCollectionToAdvisorDTOCollection(AdvisorMapperImpl.java:87) ~[main/:na]

etc etc...

更新 1.0: 我意识到@ManyToMany 关系可能不合适,而应该是@OneToOne。改变这个似乎可以解决映射问题,但是我现在有一个不同的问题。

这是我的实体的更新:

@Entity
@Table(name = "M_Advisor")
public class AdvisorEntity {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "ADVISOR_ID")
        private Long advisorId;

        @Column(name = "FIRST_NAME")
        private String firstName;

        @OneToOne(mappedBy = "advisor", cascade = CascadeType.ALL)
        @PrimaryKeyJoinColumn
        ContractEntity contract;

//getters and setters
}
@Entity
@Table(name = "M_Contract")
public class ContractEntity {

    @Id
    @Column(name = "CONTRACT_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long contractId;

    @Column(name = "ADVISOR_ID")
    private Long advisorId;

    @Column(name = "DETAILS")
    private String details;

    @OneToOne
    @MapsId
    @JoinColumn(name = "ADVISOR_ID", referencedColumnName = "ADVISOR_ID")
    private ContractEntity advisor;

//getters and setters

我已尝试关注此Baeldung walkthrough,但出现错误。这是完整的跟踪:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Repeated column in mapping for entity: org.example.entities.contracts.ContractEntity column: advisor_id (should be mapped with insert="false" update="false")
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154) ~[spring-context-5.3.7.jar:5.3.7]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908) ~[spring-context-5.3.7.jar:5.3.7]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.7.jar:5.3.7]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.0.jar:2.5.0]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.5.0.jar:2.5.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) ~[spring-boot-2.5.0.jar:2.5.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:337) ~[spring-boot-2.5.0.jar:2.5.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336) ~[spring-boot-2.5.0.jar:2.5.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325) ~[spring-boot-2.5.0.jar:2.5.0]
        at org.example.Application.main(Application.java:10) ~[main/:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
        at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.5.0.jar:2.5.0]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Repeated column in mapping for entity: org.example.entities.contracts.ContractEntity column: advisor_id (should be mapped with insert="false" update="false")
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421) ~[spring-orm-5.3.7.jar:5.3.7]
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-5.3.7.jar:5.3.7]
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845) ~[spring-beans-5.3.7.jar:5.3.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-5.3.7.jar:5.3.7]
        ... 21 common frames omitted
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: org.example.entities.contracts.ContractEntity column: advisor_id (should be mapped with insert="false" update="false")
        at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:862) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:880) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:902) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:634) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.mapping.RootClass.validate(RootClass.java:267) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:354) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:298) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:468) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
        at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) ~[spring-orm-5.3.7.jar:5.3.7]
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.3.7.jar:5.3.7]
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-5.3.7.jar:5.3.7]
        ... 25 common frames omitted

【问题讨论】:

    标签: spring spring-boot hibernate spring-data-jpa spring-data


    【解决方案1】:

    不要在实体上使用 Lombok @Data 注释!

    尤其是当您有双向关系时。

    生成equalshashCodetoString 方法将生成StackOverflowError

    您最好只将@Getter@Setter 与实体一起使用。

    对于映射器问题,您应该只考虑使用单向关系。这也会打破循环。

    【讨论】:

    • 感谢您的反馈。然而,它并没有解决问题。
    • 请出示AdvisorMapperImpl的代码。尤其是第 149、87、151 行
    • 用 AdvisorMapperImpl 更新了我的帖子。
    • 我就是你在映射器 impl 中创建循环
    • 我意识到 AdvisorMapperImpl 是导致此循环的原因,如错误所示。同样,使用 mapstruct.Mapper @Mapper,这个实现是自动生成的。在连接这些表之前我已经使用过这个,没有任何问题,所以我强烈觉得我使用 javax.persistence 的方式有问题。
    猜你喜欢
    • 2021-01-05
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 2016-02-17
    • 2018-09-23
    • 2017-08-21
    • 2020-05-07
    • 2021-01-07
    相关资源
    最近更新 更多