diff --git a/spring-mvc-simple-2/pom.xml b/spring-mvc-simple-2/pom.xml
index 74302cff78..2fe93f1fae 100644
--- a/spring-mvc-simple-2/pom.xml
+++ b/spring-mvc-simple-2/pom.xml
@@ -18,6 +18,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.projectlombok
+ lombok
+
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/SpringListValidationApplication.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/SpringListValidationApplication.java
new file mode 100644
index 0000000000..f16d5f877f
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/SpringListValidationApplication.java
@@ -0,0 +1,17 @@
+package com.baeldung.validation.listvalidation;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@ComponentScan(basePackages = "com.baeldung.validation.listvalidation")
+@Configuration
+@SpringBootApplication
+public class SpringListValidationApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringListValidationApplication.class, args);
+ }
+
+}
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraint.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraint.java
new file mode 100644
index 0000000000..5d6e2213f1
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraint.java
@@ -0,0 +1,18 @@
+package com.baeldung.validation.listvalidation.constraint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+@Constraint(validatedBy = CustomConstraintValidator.class)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CustomConstraint {
+
+ String message() default "Invalid movie name.";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+}
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraintValidator.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraintValidator.java
new file mode 100644
index 0000000000..9f18a0c218
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraintValidator.java
@@ -0,0 +1,17 @@
+package com.baeldung.validation.listvalidation.constraint;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class CustomConstraintValidator implements ConstraintValidator {
+
+ @Override
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ if (value == null || value.isEmpty()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+}
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/controller/MovieController.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/controller/MovieController.java
new file mode 100644
index 0000000000..6265fd50d0
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/controller/MovieController.java
@@ -0,0 +1,54 @@
+package com.baeldung.validation.listvalidation.controller;
+
+import java.util.List;
+
+import javax.validation.Valid;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
+
+import com.baeldung.validation.listvalidation.model.Movie;
+import com.baeldung.validation.listvalidation.repository.MovieRepository;
+
+@Validated
+@RestController
+@RequestMapping("/movie")
+public class MovieController {
+
+ private final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
+
+ @Autowired
+ private MovieRepository movieRepository;
+
+ @RequestMapping(method = RequestMethod.POST)
+ public void add(@RequestBody @Valid Movie movie, BindingResult bindingResult) {
+ if (bindingResult.hasErrors()) {
+ StringBuilder builder = new StringBuilder();
+ bindingResult.getAllErrors()
+ .forEach(error -> builder.append(" " + error.getDefaultMessage()));
+ LOGGER.error(builder.toString());
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, bindingResult.toString());
+ }
+ movieRepository.add(movie);
+ }
+
+ @RequestMapping(value = "/batch", method = RequestMethod.POST)
+ public void addAll(@RequestBody List<@Valid Movie> movie) {
+ movieRepository.addAll(movie);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{name}")
+ public Movie get(@PathVariable String name) throws Exception {
+ return movieRepository.get(name);
+ }
+}
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/exception/ConstraintViolationExceptionHandler.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/exception/ConstraintViolationExceptionHandler.java
new file mode 100644
index 0000000000..067a0e08ac
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/exception/ConstraintViolationExceptionHandler.java
@@ -0,0 +1,30 @@
+package com.baeldung.validation.listvalidation.exception;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class ConstraintViolationExceptionHandler {
+ private final Logger LOGGER = LoggerFactory.getLogger(ConstraintViolationExceptionHandler.class);
+
+ @ExceptionHandler(ConstraintViolationException.class)
+ public ResponseEntity handle(ConstraintViolationException constraintViolationException) {
+ Set> violations = constraintViolationException.getConstraintViolations();
+ StringBuilder builder = new StringBuilder();
+ for (ConstraintViolation> violation : violations) {
+ builder.append(violation.getMessage());
+
+ }
+ LOGGER.error(builder.toString());
+ return new ResponseEntity(builder.toString(), HttpStatus.BAD_REQUEST);
+ }
+}
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Actor.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Actor.java
new file mode 100644
index 0000000000..e6215f00c4
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Actor.java
@@ -0,0 +1,22 @@
+package com.baeldung.validation.listvalidation.model;
+
+import java.util.UUID;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class Actor {
+
+ private String id;
+ private String name;
+
+ public Actor(String name) {
+ this.id = UUID.randomUUID()
+ .toString();
+ this.name = name;
+ }
+}
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Movie.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Movie.java
new file mode 100644
index 0000000000..7ab4bfc34e
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Movie.java
@@ -0,0 +1,41 @@
+package com.baeldung.validation.listvalidation.model;
+
+import java.util.List;
+import java.util.UUID;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.baeldung.validation.listvalidation.constraint.CustomConstraint;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class Movie {
+
+ private String id;
+
+ @CustomConstraint
+ private String name;
+
+ @Size(min = 2, message = "Atleast 2 genres should be provided.")
+ private List genres;
+
+ @NotNull(message = "Actor list cannot be null.")
+ @NotEmpty(message = "Actor list cannot be empty.")
+ private List actors;
+
+ public Movie(String name, List genres, List actors) {
+ this.id = UUID.randomUUID()
+ .toString();
+ this.name = name;
+ this.genres = genres;
+ this.actors = actors;
+ }
+
+}
diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/repository/MovieRepository.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/repository/MovieRepository.java
new file mode 100644
index 0000000000..67cfa6daca
--- /dev/null
+++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/repository/MovieRepository.java
@@ -0,0 +1,57 @@
+package com.baeldung.validation.listvalidation.repository;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.validation.listvalidation.model.Actor;
+import com.baeldung.validation.listvalidation.model.Movie;
+
+@Repository
+public class MovieRepository {
+ private final Logger LOGGER = LoggerFactory.getLogger(MovieRepository.class);
+ static List moviesData;
+
+ static {
+ moviesData = new ArrayList<>();
+
+ Actor a1 = new Actor("Actor1");
+
+ Movie m1 = new Movie("MovieABC", Arrays.asList("Drama"), Arrays.asList(a1));
+ moviesData.add(m1);
+
+ Movie m2 = new Movie("MovieDEF", Arrays.asList("Drama"), Arrays.asList(a1));
+ moviesData.add(m2);
+
+ }
+
+ public void add(Movie movie) {
+ if (get(movie.getName()) == null) {
+ moviesData.add(movie);
+ LOGGER.info("Added new movie.");
+ }
+ }
+
+ public Movie get(String name) {
+ Movie movie = null;
+ for (Movie m : moviesData) {
+ if (name.equalsIgnoreCase(m.getName())) {
+ movie = m;
+ LOGGER.info("Found movie with name " + name + ":" + movie);
+ }
+ }
+
+ return movie;
+ }
+
+ public void addAll(List movies) {
+ for (Movie movie : movies) {
+ add(movie);
+ }
+ }
+
+}
diff --git a/spring-mvc-simple-2/src/test/java/com/baeldung/validation/listvalidation/MovieControllerIntegrationTest.java b/spring-mvc-simple-2/src/test/java/com/baeldung/validation/listvalidation/MovieControllerIntegrationTest.java
new file mode 100644
index 0000000000..226e609d5f
--- /dev/null
+++ b/spring-mvc-simple-2/src/test/java/com/baeldung/validation/listvalidation/MovieControllerIntegrationTest.java
@@ -0,0 +1,65 @@
+package com.baeldung.validation.listvalidation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+import com.baeldung.validation.listvalidation.model.Actor;
+import com.baeldung.validation.listvalidation.model.Movie;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = SpringListValidationApplication.class)
+@AutoConfigureMockMvc
+public class MovieControllerIntegrationTest {
+
+ @Autowired
+ private MockMvc mvc;
+
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ @Test
+ public void given1Genre_whenAddingMovie_thenThrowBadRequest() throws Exception {
+ Actor actor = new Actor("Actor1");
+ Movie movie = new Movie("Movie1", Arrays.asList("Drama"), Arrays.asList(actor));
+ mvc.perform(MockMvcRequestBuilders.post("/movie")
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(movie)))
+ .andExpect(MockMvcResultMatchers.status()
+ .isBadRequest());
+ }
+
+ @Test
+ public void givenWithoutActor_whenAddingMovieList_thenThrowBadRequest() throws Exception {
+ List actors = new ArrayList<>();
+ Movie movie = new Movie("Movie2", Arrays.asList("Action", "Thriller"), actors);
+ mvc.perform(MockMvcRequestBuilders.post("/movie/batch")
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(Arrays.asList(movie))))
+ .andExpect(MockMvcResultMatchers.status()
+ .isBadRequest());
+ }
+
+ @Test
+ public void givenEmptyMovieName_whenAddingMovie_thenThrowBadRequest() throws Exception {
+ Actor actor = new Actor("Actor1");
+ Movie movie = new Movie("", Arrays.asList("Drama", "History"), Arrays.asList(actor));
+ mvc.perform(MockMvcRequestBuilders.post("/movie")
+ .contentType(MediaType.APPLICATION_JSON_UTF8)
+ .content(objectMapper.writeValueAsString(movie)))
+ .andExpect(MockMvcResultMatchers.status()
+ .isBadRequest());
+ }
+
+}