Home > database >  Spring Data/JPA and errorType : object references an unsaved transient instance - save the transient
Spring Data/JPA and errorType : object references an unsaved transient instance - save the transient

Time:07-19

enter image description here

I have this error with JPA when I update a Person with my repository appUserRepository.save(): " 2022-07-16 10:27:30.080 ERROR 4792 --- [nio-7777-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; WeightRecord; nested exception is java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.quentingenet.openweighttracker.entity.WeightRecord] with root cause " I've looked at posts on stackoverflow regarding this error but can't find any solutions. I also tried the cascadeType.ALL. Just bellow you can see in Class 'WeightRecordController' the post method 'saveWeight'. The 'personRepository.save(personToUpdate);' create error when it's called.

      @Entity
        @Table(name = "person")
        public class Person implements Serializable{
            
            private static final long serialVersionUID = 1L;
        
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Column(name="id_person")
            private Long idPerson;
        
            @ElementCollection
            private List<WeightRecord> userPersonnalWeightsRecord = new ArrayList<WeightRecord>();
            
            @OneToOne( cascade =  CascadeType.ALL, fetch = FetchType.EAGER)
            @JoinColumn(name = "id_initial_data")
            private InitialData userInitData;
            
            @OneToOne( cascade =  CascadeType.ALL, fetch = FetchType.EAGER)
            @JoinColumn(name = "id_user")
            private AppUser appUserPerson;
        
            @OneToOne(cascade =  CascadeType.ALL, fetch = FetchType.EAGER)
            @JoinColumn(name = "id_weight_record")
            private WeightRecord weightRecord;
    
    
    
        @Entity
        @Table(name = "weight_record")
        public class WeightRecord implements Serializable{
        
            private static final long serialVersionUID = 1L;
        
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Column(name = "id_weight_record")
            private Long idWeightRecord;
        
            @Column(name = "weight_record_date", nullable = true)
            private LocalDate weightRecordDate;
        
            @Min(1)
            @Max(635)
            @Column(name = "weight_kg_record", nullable = true, precision = 1)
            private Double weightKgRecord;
        
            @Min(1)
            @Max(99)
            @Column(name = "percent_fat_mass", nullable = true, precision = 1)
            private Double percentFatMass;
        
            @Min(1)
            @Max(99)
            @Column(name = "percent_muscular_mass", nullable = true, precision = 1)
            private Double percentMuscularMass;
        
            @Min(1)
            @Max(99)
            @Column(name = "percent_body_water", nullable = true, precision = 1)
            private Double percentBodyWater;
        
            @Min(1)
            @Max(99)
            @Column(name = "percent_bone_mass", nullable = true, precision = 1)
            private Double percentBoneMass;
    
    
    @Table(name = "initial_data")
    @Entity
    public class InitialData implements Serializable{
    
        private static final long serialVersionUID = 1L;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id_initial_data")
        private Long idInitialData;
    
        @Min(5)
        @Max(636)
        @Column(name = "goal_weight", nullable = true, precision = 1)
        private Double goalWeight;
    
        @Min(25)
        @Max(300)
        @Column(name = "body_size", nullable = true)
        private Integer bodySize;
    
        // @Size(min = 3, max = 5, message = "Please choose between MAN or WOMAN")
        @Column(name = "sex", nullable = true)
        private String sex;
    
        @Min(1917)
        @Max(2022)
        @Column(name = "year_birth", nullable = true)
        private Integer yearBirth;

@Entity
@Table(name = "app_users")
public class AppUser implements Serializable{

    private static final long serialVersionUID = 1L;

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

    @NotEmpty
    @Size(min = 3, max = 36)
    @Column(name = "appUsername", nullable = false, unique = true)
    private String appUsername;

    @NotEmpty
    @Size(min = 4, max = 255)
    @Column(name = "password", nullable = false)
    private String password;



@RestController
@RequestMapping("/weights")
public class WeightRecordController {

    @Autowired
    WeightRecordServiceImpl weightRecordServiceImpl;

    @Autowired
    WeightRecordRepository weightRecordRepository;

    @Autowired
    AppUserRepository appUserRepository;

    @Autowired
    PersonRepository personRepository;

    private final Logger logger = LoggerFactory.getLogger(WeightRecordController.class);

    public Long getAppUserConnectedId(Principal principal) {
        // TODO user/principal is null... so exception is raise with message "User not
        // found"
        if (!(principal instanceof UsernamePasswordAuthenticationToken)) {
            throw new RuntimeException(("User not found"));
        }
        logger.info("USER IS PRESENT IN DATABASE FROM FUNCTION 'getAppUserConnectedId()'");
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) principal;
        AppUser appUserFinded = appUserRepository.findByAppUsername(token.getName());
        return appUserFinded.getIdUser();
    }



    // TODO
    @PostMapping
    public ResponseEntity<WeightRecord> saveWeight(@RequestBody WeightRecord weightRecord, Principal principal) {
        logger.info("POST /weights");
        Long appUserConnectedId = this.getAppUserConnectedId(principal);
        Optional<Person> personUserToSave = personRepository.findById(appUserConnectedId);
        logger.info("PERSON FINDED IN DATABASE");
        if (personUserToSave.isPresent()) {
            logger.info("USER IS PRESENT IN DATABASE FROM saveWeightController");
            weightRecordServiceImpl.saveWeightRecord(weightRecord);
            logger.info("WEIGHT RECORD IS SAVED NOW");
        } else {
            return new ResponseEntity<WeightRecord>(HttpStatus.BAD_REQUEST);
        }

        Person personToUpdate = personRepository.findById(appUserConnectedId).orElseThrow();
        List<WeightRecord> personWeightsList = personToUpdate.getUserPersonnalWeightsRecord();
        personWeightsList.add(weightRecord);
        personToUpdate.setUserPersonnalWeightsRecord(personWeightsList);
        logger.info("WEIGHT UPDATED IN PERSONNAL USER LIST WITH ID n°{}",appUserConnectedId);
        //TODO : FIX PROBLEM just bellow
        personRepository.save(personToUpdate);
        logger.info("PERSON WITH ID N°{} IS UPDATED NOW.", personToUpdate.getIdPerson());

        return new ResponseEntity<WeightRecord>(weightRecord, HttpStatus.CREATED);
    }

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {

}

CodePudding user response:

i

In fact the problem concerned entities mapping. Instead of @ElementCollection i use now @OneToMany for the user's weights list and it's ok. I have imported a screenshoot of my Entities after this change. Thanks for help ! Just bellow my new entity Person :

    @Entity
    @Table(name = "person")
    public class Person implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id_person")
        private Long idPerson;
    
        @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        @JoinColumn(name = "id_initial_data")
        private InitialData userInitData;
    
        @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        @JoinColumn(name = "id_user")
        private AppUser appUserPerson;
        
        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        private List<WeightRecord> weightsList;
        /*Getters and setters*/
  • Related