连接实体表时 Spring 启动应用程序的映射问题

Mapping Issue for Spring Boot Application when Joining Tables for Entities

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

不幸的是,当我尝试这样做时,Mappers 进入无限循环。这肯定是由于我如何加入我的实体,但我无法通过在线阅读弄清楚如何。有谁知道我哪里出错了?下面是我的代码。请注意,为简洁起见,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;
    }
}

Whosebug 错误(无双关语)

java.lang.WhosebugError: 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[=19=](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

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

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

生成 equalshashCodetoString 方法将生成 WhosebugError

您最好只对实体使用 @Getter@Setter

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