#spring #spring-boot #hibernate #spring-data-jpa #spring-data
Вопрос:
У меня в базе данных есть две связанные таблицы, которые я пытаюсь объединить с их соответствующими сущностями, используя отношения @ManyToMany в приложении Spring (фотографии см. Ниже).
К сожалению, когда я пытаюсь это сделать, картографы идут по бесконечному циклу. Это, безусловно, должно быть связано с тем, как я присоединяюсь к своим сущностям, но я не могу понять, как это сделать, читая онлайн. Кто-нибудь знает, где я ошибаюсь? Ниже приведен мой код. Обратите внимание, что уровни контроллера, Сервиса и Репозитория опущены для краткости.
Сущности
@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
Note: This is auto generated but I have removed some attributes. Correct line numbers are noted where needed.
@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 Error (no pun intended)
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...
Update 1.0:
I realized that perhaps a @ManyToMany relationship was not appropriate, and instead should be @OneToOne. Changing this seems to solve the mapping problem, however I now I have a different issue.
Here’s an update on my entities:
@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, но получаю ошибку. Вот полный след:
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
Ответ №1:
Не используйте @Data
аннотации Ломбока для объектов!
Особенно когда у вас двунаправленные отношения.
Генерация equals
hashCode
и toString
методы приведут к StackOverflowError
Вам лучше использовать только @Getter
и @Setter
с сущностями.
Для проблемы картографирования вам следует рассмотреть возможность использования только однонаправленной связи. Это также разорвет петлю.
Комментарии:
1. Спасибо вам за обратную связь. Однако это не решает проблему.
2. Пожалуйста, покажите код AdvisorMapperImpl. Особенно строки 149, 87, 151
3. Обновил свой пост с помощью AdvisorMapperImpl.
4. Я в том случае, если вы создаете цикл в impl картографа
5. Я понимаю, что AdvisorMapperImpl является причиной этого цикла, как показано в ошибке. Опять же, используя mapstruct. Mapper @Mapper, эта реализация генерируется автоматически. Я использовал это раньше до подключения этих таблиц без каких-либо проблем, поэтому я чувствую, что что-то не так с тем, как я использую javax.persistence.