Home > front end >  How do you fetch an object from MySQL that contains a property with a value of another object in Spr
How do you fetch an object from MySQL that contains a property with a value of another object in Spr

Time:11-01

I have a few models that has relationship to each other within this object I am tryin to save and fetch from the database, but I can't seem to get it to work properly. The object contains a property that also contains another object. My approach was to save to all related models and for those models, I would also save this current model to them as well so it shows relation on the database table.

Below the models for the app.

@Entity
@Table(name = "daily_entry")
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class DailyEntry {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "daily_entry_id", unique = true)
    private Long id;
    @Column(unique = true)
    private LocalDate date;
    private int weight;

    @ManyToOne
    @JoinColumn(name="user_id")
    private User user;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "daily_macros_id", unique = true)
    private DailyMacros dailyMacros;

    @OneToMany(mappedBy = "dailyEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Exercise> exercise = new ArrayList<Exercise>();
}
@Entity
@Table(name = "daily_macros")
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class DailyMacros {
    @Id
    @GeneratedValue
    @Column(name = "daily_macros_id")
    private Long id;
    private int calories;
    private int protein;
    private int fat;
    private int carbs;
    
    @OneToOne(mappedBy = "dailyMacros", cascade = CascadeType.ALL, orphanRemoval = true)
    private DailyEntry dailyEntry;
}
@Setter
@Getter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Table(name= "exercises")
public class Exercise {

    @Id
    @GeneratedValue
    @Column(name = "exercise_id")
    private Long id;
    private String name;
    private int sets;
    private int reps;
    private int weight;

    @ManyToOne
    @JoinColumn(name = "daily_entry_id", unique = true)
    private DailyEntry dailyEntry;
}

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;
    @Column(nullable = false)
    private String firstName;
    @Column(nullable = false)
    private String lastName;
    private String role = "user";
    private String token;
    @Column(unique = true, nullable = false)
    protected String username;
    @Column(unique = true ,nullable = false)
    private String emailAddress;
    @Column(nullable = false)
    private String password;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "macros_goal_id")
    private MacrosGoal macrosGoal;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<DailyEntry> dailyEntry = new ArrayList<>();
}

Im tryin to save the model of DailyEntry to the database. User is the owner of DailyEntry DailyEntry owns MacrosGoal (@OneToOne) and Exercise (@OneToMany). How exactly can I save and fetch this model into the database?

This is what I've tried - For my PutMapping, I get this error : Cannot call sendError() after the response has been committed

My GetMapping just returns a empty response although http status code 200

DailyEntryController file

@RequestMapping(path = "/api/v1/")
@RequiredArgsConstructor
public class DailyEntryController {

    @Autowired
    DailyEntryService dailyEntryService;

    @GetMapping("/getDailyEntry")
    public ResponseEntity<DailyEntry> getDailyEntry(@RequestParam("username")  String username, @RequestParam String date) throws ResponseStatusException {
        DailyEntry dailyEntry = dailyEntryService.getDailyEntry(username, date);
        return new ResponseEntity<>(dailyEntry, HttpStatus.OK);
    }

    @PutMapping("/addDailyEntry")
    public ResponseEntity<DailyEntry> addDailyEntry(@RequestBody DailyEntry dailyEntry, @RequestParam("username") String username) throws ResponseStatusException {
        DailyEntry dailyEntryInfo = dailyEntryService.createDailyEntry(dailyEntry, username);
        return new ResponseEntity<>( dailyEntryInfo, HttpStatus.OK);
    }

}
@Slf4j
@Service
public class DailyEntryServiceImpl implements DailyEntryService {

    @Autowired
    DailyEntryRepository dailyEntryRepository;
    @Autowired
    ExerciseRepository exerciseRepository;
    @Autowired
    UserRepository userRepository;
    @Autowired
    DailyMacrosRepository dailyMacrosRepository;

    @Override
    public DailyEntry addExercise(Exercise exercise) {
        return null;
    }

    @Override
    public DailyEntry getDailyEntry(String username, String date) {
        DailyEntry entry = null;
        Optional<User> userFromDatabase = userRepository.findByUsername(username);
        User user = userFromDatabase.get();


        LocalDate localDate = LocalDate.parse(date);
        List<DailyEntry> dailyEntryList =  user.getDailyEntry();


        for(DailyEntry e : dailyEntryList) {
            if(e.getDate() == localDate){
                entry = e;
            }
        }

        return entry;
        
    }


    @Override
    public DailyEntry createDailyEntry(DailyEntry dailyEntry, String username) {
        DailyEntry entry = new DailyEntry();
        // Find user
        Optional<User> userFromDatabase = userRepository.findByUsername(username);
        User user = userFromDatabase.get();

        entry.setUser(user);
        entry.setDate(LocalDate.parse(dailyEntry.getDate().toString()));
        entry.setDailyMacros(dailyEntry.getDailyMacros());
        entry.setWeight(dailyEntry.getWeight());
        entry.setExercise(dailyEntry.getExercise());

        DailyMacros dailyMacros = dailyEntry.getDailyMacros();
        dailyMacros.setDailyEntry(entry);
        dailyMacrosRepository.save(dailyMacros);

        List<Exercise> exercise = dailyEntry.getExercise();
        for (Exercise e : exercise) {
            e.setDailyEntry(entry);
            exerciseRepository.save(e);
        }

        List<DailyEntry> dailyEntryList = user.getDailyEntry();
        dailyEntryList.add(entry);
        user.setDailyEntry(dailyEntryList);
        userRepository.save(user);

        return entry;
    }

I also tried with Query inside repository, but I may have done it wrong.

CodePudding user response:

You should probably add @Transactional to createDailyEntry. I don't really understand what's happening in createDailyEntry but I think it should look something like this:

    @Override
    @Transactional
    public DailyEntry createDailyEntry(DailyEntry dailyEntry, String username) {
       User user = userRepository
            .findByUsername(username)
            .get();

       dailyEntryRepository.save(dailyEntry);

       user.getDailyEntry().add(dailyEntry);
       dailyEntry.setUser(User);

        DailyMacros dailyMacros = dailyEntry.getDailyMacros();
        dailyMacros.setDailyEntry(dailyEntry);
        dailyMacrosRepository.save(dailyMacros);
 
        dailyEntry.getExcercise()
            .forEach( e -> {
                e.setDailyEntry(dailyEntry);
                exerciseRepository.save(e);
            });
        // I don't think you need this because user is already managed
        // and userFromDatabase.get() will throw an exception
        // if the user does not exist.
        // userRepository.save(user);

        return dailyEntry;
    }

I think getDailyEntry doesn't work because you are using e.getDate() == localDate and it's always going to be false.

One solution is to add a method to the DailyEntryRepository that accept username and date:


    interface DailyEntryRepository extends JpaRepository<DailyEntry, Long> {
        @Query("select de from DailyEntry de where de.user.username = :username and de.date = :localDate")
        DailyEntry findByUsernameAndDate(@Param("username") String username, @Param("localDate") LocalDate date);
    }

    ...


    @Override
    public DailyEntry getDailyEntry(String username, String date) {
        LocalDate localDate = LocalDate.parse(date);
        return dailyEntryRepository.findByUsernameAndDate(username, localdate);
    }

This solution will run a query and load only the entry you need.

But, if you need to validate the username, this should work:

    @Override
    public DailyEntry getDailyEntry(String username, String date) {
        User user = userRepository
            .findByUsername(username)
            .get();

        LocalDate localDate = LocalDate.parse(date);
        for(DailyEntry e : user.getDailyEntry()) {
            if (localDate.equals(e.getDate)){
                // No need to continue the loop, you've found the entry
                return entry;
            }
        }

        // daily entry not found
        return null;
    }

Another thing that's missing is that in your model you are not handling the bidirectional association during convertion to JSON. The solution is to use I end up using @JsonManagedReference and @JsonBackReference where you have the association.

  • Related