0

I'm new to spring boot. I searched a lot but I coudn't figure out what I'm missing here

I have three entities

Student

@Getter
@Setter
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "STUDENT_PK_SEQ")
    @SequenceGenerator(name = "STUDENT_PK_SEQ", sequenceName = "STUDENT_PK_SEQ", allocationSize = 1)
    private Long id;

    private String title;

    @OneToOne(cascade = CascadeType.ALL)
    private ClassInstance classInstance;

}

Class

@Getter
@Setter
@Entity
@Table(name = "class")
public class Class {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CLASS_PK_SEQ")
    @SequenceGenerator(name = "CLASS_PK_SEQ", sequenceName = "CLASS_PK_SEQ", allocationSize = 1)
    private Long id;

    private String title;

    @OneToMany(mappedBy = "class")
    @JsonIgnoreProperties(value = {"class"}, allowSetters = true)
    private Collection<ClassInstance> classInstances;

}

ClassInstance

    @Getter
    @Setter
    @Entity
    @Table(name = "class_instance")
    public class ClassInstance {
        
            @Id
            @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CLASS_INSTANCE_PK_SEQ")
            @SequenceGenerator(name = "CLASS_INSTANCE_PK_SEQ", sequenceName = "CLASS_INSTANCE_PK_SEQ", allocationSize = 1)
            private Long id;
        
            private String title;
            @ManyToOne
            private Class class;
    
            @OneToOne(cascade = CascadeType.ALL)
            private Student student;
        
        }

The problem occurs when I would like to assign an existing Student to ClassIntance

   @Transactional
        @Override
        public ClassInstanceDto createClassInstance(Long idClass, Long idStudent) {
            if(idClass == null || idStudent == null){
                throw new ApiRequestException(Constants.INVALID_ID, HttpStatus.PRECONDITION_FAILED);
            }   
            Optional<Class> class = classRepository.findById(idClass);
            if(class.isPresent()) {
                Student student = studentRepository.getById(idStudent);
                student.setTitle("X");
                studentRepository.save(student);
    
                ClassInstance classInstance = new ClassInstance();
                classInstance.setClass(class.get());
                classInstance.setStudent(student);
    
                ClassInstance result = classInstanceRepository.save(classInstance);
  //start  //if I remove this part, it works 
                student.setClassInstance(result);
                studentRepository.save(student);
   //end 
                return classInstanceMapper.toClassInstanceDto(result);
            } else {
                throw new ApiRequestException(Constants.CLASS_NOT_FOUND, HttpStatus.NOT_FOUND);
            }
        }

I can't even understand what's the error message all I got is that

ma.company.app.mapper.ClassInstanceMapperImpl.toClassInstanceDto(ClassInstanceMapperImpl.java:37) ~[classes/:na]
    at ma.company.app.mapper.ClassInstanceMapperImpl.studentToStudentDto(ClassInstanceMapperImpl.java:162) ~[classes/:na]
ma.company.app.mapper.ClassInstanceMapperImpl.toClassInstanceDto(ClassInstanceMapperImpl.java:37) ~[classes/:na]
    at ma.company.app.mapper.ClassInstanceMapperImpl.studentToStudentDto(ClassInstanceMapperImpl.java:162) ~[classes/:na]
ma.company.app.mapper.ClassInstanceMapperImpl.toClassInstanceDto(ClassInstanceMapperImpl.java:37) ~[classes/:na]
    at ma.company.app.mapper.ClassInstanceMapperImpl.studentToStudentDto(ClassInstanceMapperImpl.java:162) ~[classes/:na]
ma.company.app.mapper.ClassInstanceMapperImpl.toClassInstanceDto(ClassInstanceMapperImpl.java:37) ~[classes/:na]
    at ma.company.app.mapper.ClassInstanceMapperImpl.studentToStudentDto(ClassInstanceMapperImpl.java:162) ~[classes/:na]
ma.company.app.mapper.ClassInstanceMapperImpl.toClassInstanceDto(ClassInstanceMapperImpl.java:37) ~[classes/:na]
    at ma.company.app.mapper.ClassInstanceMapperImpl.studentToStudentDto(ClassInstanceMapperImpl.java:162) ~[classes/:na]

UPDATE here is my mapper

import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring")
public interface ClassInstanceMapper {
    ClassInstanceDto toClassInstanceDto(ClassInstance classInstance);
    ClassInstance toClassInstance(ClassInstanceDto classInstanceDto);
    List<ClassInstanceDto> toClassInstanceDtos(List<ClassInstance> classInstances);
    List<ClassInstance> toClassInstances(List<ClassInstanceDto> classInstanceDtos);
}

ClassInstanceDto

    @Getter
    @Setter
    public class ClassInstanceDto {
        private Long id;
        private StudentDto student;
        private ClassDto class;
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            ClassInstanceDto that = (ClassInstanceDto) o;
    
            return id.equals(that.id);
        }
    
        @Override
        public int hashCode() {
            return id.hashCode();
        }
}

StudentDto

@Getter
@Setter
public class StudentDto {
    private Long id;
    private String title;
    private ClassInstanceDto ClassInstance;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        StudentDto that = (StudentDto) o;

        return id.equals(that.id);
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }
}

Thanks in advance!

dbh
  • 3
  • 2
  • 2
    the error looks like it's coming from that return's method classInstanceMapper.toClassInstanceDto(result). You should look there – Patrick Aug 18 '22 at 21:31
  • @Patrick but if I remove the part I pointed out in the comment in `createClassInstance`, it works so I guess it's the oneToone – dbh Aug 18 '22 at 21:38
  • 2
    The stack trace looks like part of a `StackOverflowError` because you have an infinite recursive loop. The error is probably in `ClassInstanceMapperImpl` (which you didn't show us), there is probably a circular dependency between classes being mapped, so that when A is mapped it maps B, then when B is mapped it maps A, then B again, A again etc. => Make sure there is no circular dependency. Not an error that has anything to do with JPA, but with the mapper. – Jesper Aug 18 '22 at 21:39
  • @Jesper I don't have `ClassInstanceMapperImpl` I just have `ClassInstanceMapper` I added it in UPDATE section above – dbh Aug 18 '22 at 21:43
  • 1
    The point is that there is a circular dependency between your DTO classes. You need to get rid of this, otherwise MapStruct is going into a recursive loop until a `StackOverflowError` occurs. – Jesper Aug 18 '22 at 21:45
  • It would be helpful to know what the DTO is that this Mapper is outputting – lane.maxwell Aug 18 '22 at 21:52
  • @lane.maxwell I added my DTOs – dbh Aug 18 '22 at 22:02

1 Answers1

2

You have a recursive loop happening during the mapping. I'm not certain about the inner workings of MapStruct, but if it's using ObjectMapper, it would probably honor JsonManageReference and JsonBackReference annotations.

Assuming that ClassInstance is the parent,

@JsonManagedReference
@OneToOne(cascade = CascadeType.ALL)
private Student student;

One the "child" side

@JsonBackReference
@OneToOne(cascade = CascadeType.ALL)
private ClassInstance classInstance;

Do this also for your OneToMany/ManyToOne mappings. Also, I would strongly encourage you not to name your class, Class.

lane.maxwell
  • 5,002
  • 1
  • 20
  • 30
  • Thank you for the answer, but I keep getting the same error – dbh Aug 18 '22 at 22:16
  • 1
    There's plenty of answers for this around StackOverflow, like https://stackoverflow.com/questions/59895166/mapstruct-bidirectional-mapping and https://stackoverflow.com/questions/66401207/use-bidirectional-entity-methods-with-mapstruct. However, your DTOs are so simple, I would just create a method on your domain object that converts it to the DTO :) – lane.maxwell Aug 18 '22 at 22:31
  • 1
    this answer https://stackoverflow.com/questions/59895166/mapstruct-bidirectional-mapping you suggested helped me a lot I can't thank you enough! – dbh Aug 19 '22 at 10:19