How do I allow input for only one month-year combination in Java Spring Boot. I need to make sure that user (postman client) can input one recording (randomly generated value) per month of that year. So for example: month: February -> Year: 2020 -> generatedValue = something random. And when user goes to input February 2020 again it throws an exception. I've tried with storing years and months in seperate lists, to check if there's month with that year already present in the database, but to no avail. In RecordService in first if I'm trying to see if there isn't present year that user entered. For example if there is not a year 2021 then the "if" should add that year to a list so i can check the month-year combo. It works, but not what I need because the second if always throws a RuntimeException(), except when I enter a year that hasn't been entered (for example there are years 2020, 2021 and 2022 but there isn't a year 2023, so when user adds for example:
{
"month": "March",
"year": "2023",
"readingTime": "1"
}
the code will work because there isn't a year 2023 in the list, but as soon as they try to enter
{
"month": "May",
"year": "2023",
"readingTime": "1"
}
it will not work because the year is already present, even tho it should because there isn't may-2023 combo in the db. I've tried to have a boolean to check if the combo exists, but that doesn't make any sense to me. So please, please help :) Here is my code.
RecordController.java
@PostMapping("/{client_id}")
public Record add(@PathVariable Long client_id, @RequestBody Record record){
return recordService.add(client_id, record);
}
RecordService.java
public Record add(Long client_id, Record record){
List<String> monthList = months();
List<Integer> yearList = years();
Record recordSave = new Record();
recordSave.setId(client_id);
recordSave.setYear(record.getYear());
recordSave.setMonth(record.getMonth());
recordSave.setReadingTime(record.getReadingTime());
recordSave.setReadingValue(randomGenerate());
//Does not work
if (!yearList.contains(recordSave.getYear())){
yearList.add(recordSave.getYear());
}
//Does not work
if (monthList.contains(recordSave.getMonth()) && yearList.contains(recordSave.getYear())){
throw new RuntimeException();
}
else {
recordRepository.save(recordSave);
}
return null;
}
Record.java
@Data
@Entity
@Table(name = "record")
@JsonIgnoreProperties({"hibernateLazyInitializer","handler","device"})
public class Record implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
String month;
Integer year;
String readingTime;
Float readingValue;
boolean isPresent;
}
RandomGenerateValue & month-year functions
public Float randomGenerate(){
int max = 105, min = 14;
float random_float = (float)Math.floor(Math.random()*(max-min 1) min);
return random_float;
}
public List<String> months(){
List<String> monthList = new ArrayList<>();
monthList.add("January"); monthList.add("February"); monthList.add("March"); monthList.add("April");
monthList.add("May"); monthList.add("June"); monthList.add("July"); monthList.add("August");
monthList.add("September"); monthList.add("October"); monthList.add("November"); monthList.add("December");
return monthList;
}
public List<Integer> years(){
List<Integer> yearList = new ArrayList<>();
yearList.add(2020);
yearList.add(2021);
return yearList;
}
CodePudding user response:
Keep year & month together, as a single value
Your problem seems to stem from thinking of the year and the month as separate values. Instead, combine them. Think in terms of year-month, a single unit.
YearMonth
class
Java includes a class for this concept. Its name is appropriate: YearMonth
, in the java.time package.
YearMonth ym = YearMonth.of ( 2023 , 3 ) ;
Or use the Month
enum, to ensure valid values.
YearMonth ym = YearMonth.of ( 2023 , Month.MARCH ) ;
You can represent your slots for each month and year by using a Map
. Keep the keys in sorted order by using a NavigableMap
.
NavigableMap< YearMonth , UUID > map = new TreeMap<>() ;
map.put(
YearMonth.of ( 2023 , Month.MARCH ) ,
UUID.randomUUID()
);
map.put(
YearMonth.of ( 2023 , Month.FEBRUARY ) ,
UUID.randomUUID()
);
Test if a value has already been put.
boolean alreadyPut2023March = map.containsKey( YearMonth.of ( 2023 , Month.MARCH ) ) ;
ISO 8601
For text purposes, use standard ISO 8601 format: YYYY-MM
The java.time classes use ISO 8601 formats by default when parsing/generating text.
String iso8601Output = YearMonth.of ( 2023 , Month.MARCH ).toString() ;
2023-03
Such a string could be used for storage in your database, assuming your database does not offer a year-month data type. I don't know of any database that does offer such a data type. So text will suffice.
Notice that when sorted alphabetically, the values are also sorted chronologically.
Your JSON would look like this:
{
"year-month": "2023-03",
"readingTime": "1"
}