Problem with form validation in my spring boot project - spring

I was following the 'Spring in Action' book and was creating the TacoCloud application.
I added validation to the Taco class as shown below -
#Data
public class Taco {
#NotNull
#Size(min=5, message="Name must be at least 5 characters long")
private String name;
#Size(min=1, message="You must choose atleast 1 ingredient")
private List<String> ingredients;
}
Then, in the controller class I added code that enables user to customize their taco before placing an order -
#Slf4j
#Controller
#RequestMapping("/design")
public class DesignTacoController {
#GetMapping
public String showDesignForm(Model model) {
List<Ingredient> ingredients = Arrays.asList(
new Ingredient("FLTO", "Flour Tortilla", Type.WRAP),
new Ingredient("COTO", "Corn Tortilla", Type.WRAP),
new Ingredient("GRBF", "Ground Beef", Type.PROTEIN),
new Ingredient("CARN", "Carnitas", Type.PROTEIN),
new Ingredient("TMTO", "Diced Tomatoes", Type.VEGGIES),
new Ingredient("LETC", "Lettuce", Type.VEGGIES),
new Ingredient("CHED", "Cheddar", Type.CHEESE),
new Ingredient("JACK", "Monterrey Jack", Type.CHEESE),
new Ingredient("SLSA", "Salsa", Type.SAUCE),
new Ingredient("SRCR", "Sour Cream", Type.SAUCE));
Type[] types = Ingredient.Type.values();
for (Type type : types) {
model.addAttribute(type.toString().toLowerCase(),
ingredients.stream().filter(c -> c.getType() == type).collect(Collectors.toList()));
}
model.addAttribute("design", new Taco());
return "design-form";
}
#PostMapping
public String processCreation(#Valid Taco taco, Errors errors) {
if(errors.hasErrors()) {
return "design-form";
}
log.info("Taco " + taco);
return "redirect:/orders/current";
}
}
But, when I run the application and submit the form with errors, it gives the following output -
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'design' available as request attribute
I am not being able to resolve this. Any help would be appreciated.

Related

Builder Pattern and Dependecy Injection - how to fix problem

I wanted to create a class based on the builder pattern. Using the static method build. Which would return a properly built object based on initial validation checking whether a given object exists in the database.
#Component
#Data
#Builder
public class GetBookedSeatsRequest {
#Autowired
private MovieRepository movieRepository;
#Autowired
public CinemaRepository cinemaRepository;
#Autowired
public PropertiesMovieRepository propertiesMovieRepository;
private String cinemaName;
private String movieName;
private String movieRoom;
#JsonFormat(pattern="yyyy-MM-dd; HH:mm:ss",shape = JsonFormat.Shape.STRING)
private LocalDateTime localDateTime;
private List<Integer> wantedSeats;
public GetBookedSeatsRequest build(ReservationModel reservationModel) throws CinemaNotFoundException, MovieNotFoundException, PropertyMovieNotFoundException {
boolean cinemaExist = cinemaRepository.existsByCinemaName(reservationModel.getCinemaName());
if (!cinemaExist) {
throw new CinemaNotFoundException("Cinema doesn't exist");
}
boolean movieExist = movieRepository.existsByMovieName(reservationModel.getMovieName());
if (!movieExist) {
throw new MovieNotFoundException("Movie doesn't exist");
}
boolean roomExist = movieRepository.existsByMovieRoom(reservationModel.getMovieRoom());
if (!roomExist) {
throw new MovieNotFoundException("Movie Romm doesn't exist");
}
boolean existData = propertiesMovieRepository.existsByStartTimeOfTheMovie(reservationModel.getDateAndTime());
if (!existData) {
throw new PropertyMovieNotFoundException("This data doesn't exist");
}
// boolean existSeats = movieRepository.existsBySeating(reservationModel.getSeatsToBooked());
// if (!existSeats) {
// throw new MovieNotFoundException("This seats doesn't exist");
// }
GetBookedSeatsRequest correct = GetBookedSeatsRequest.builder()
.cinemaName(reservationModel.getCinemaName())
.movieName(reservationModel.getMovieName())
.movieRoom(reservationModel.getMovieRoom())
.localDateTime(reservationModel.getDateAndTime())
.wantedSeats(reservationModel.getSeatsToBooked())
.build();
return correct;
}
}
#Data
#AllArgsConstructor
public class ReservationModel {
private String cinemaName;
private String movieName;
private String movieRoom;
#JsonFormat(pattern="yyyy-MM-dd; HH:mm:ss",shape = JsonFormat.Shape.STRING)
private LocalDateTime dateAndTime;
private List<Integer> seatsToBooked;
}
But I still got some erros. What am I doing wrong, I am learing Spring Boot. Thanks for help
Description:
Parameter 3 of constructor in com.cinema.booking.aop.GetBookedSeatsRequest required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.

Spring boot consume 2 rest and merge some fields

Im new to Spring Boot and got a problem were i need to consume 2 remote Rest services and merge the results. Would need some insight on the right approach.
I got something like this:
{"subInventories":[
{"OrganizationId": 0,
"OrganizationCode":"",
"SecondaryInventoryName":"",
"Description":""},...{}...],
{"organizations":[
{"OrganizationId":0,
"OrganizationCode":"",
"OrganizationName":"",
"ManagementBusinessUnitId":,
"ManagementBusinessUnitName":""}, ...{}...]}
and need to make it into something like this:
{"items":[
{"OrganizationId":0,
"OrganizationCode":"",
"OrganizationName":"",
"ManagementBusinessUnitId":0,
"ManagementBusinessUnitName":"",
"SecondaryInventoryName":"",
"Description":""},...{}...]
got 2 #Entitys to represent each item, Organizations and Inventories with the attributtes like the JSON fields.
EDIT
Currently trying to get matches with Java8 stream()
#GetMapping("/manipulate")
public List<Organization> getManipulate() {
List<Organization> organization = (List<Organization>)(Object) organizationController.getOrganization();
List<SubInventories> subInventories = (List<SubInventories>)(Object) getSuvInventories();
List<Organization> intersect = organization.stream().filter(o -> subInventories.stream().anyMatch(s -> s.getOrganizationId()==o.getOrganizationId()))
.collect(Collectors.toList());
return intersect;
}
found this searching but i got many classes and I don't know if it would be better to just for each organization get the subinventories and put them in a list of maps like
List<Map<String,Object> myList = new ArrayList<>();
//Loops here
Map<String,Object> a = new HashMap<>();
a.put("OrganizationID", 1231242415)...
myList.add(a)
Quite lost in what the right approach is.
EDIT2
Here the classes I'm using.
Organizations
#Entity
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Organization implements Serializable{
//#JsonObject("OrganizationId")
#Id
private Long OrganizationId;
private Long ManagementBusinessUnitId;
private String OrganizationCode,OrganizationName,ManagementBusinessUnitName;
public Organization() {
}
//getters setters
}
SubInventories
#Entity
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
#JsonIgnoreProperties(ignoreUnknown = true)
public class SubInventories implements Serializable{
#Id
private Long OrganizationId;
private String OrganizationCode,SecondaryInventoryName,Description;
public SubInventories() {
}
//getters and setters
}
Wrapper to unwrapp consume
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Wrapper {
//#JsonProperty("items")
private List<Object> items;
public Wrapper() {
}
public List<Object> getOrganization() {
return items;
}
public void setOrganization(List<Object> organization) {
this.items = organization;
}
}
OrganizationController
#RestController
public class OrganizationController {
#Autowired
private RestTemplate restTemplate;
#Autowired
private Environment env;
#GetMapping("/organizations")
public List<Object> getOrganization() {
return getOrganizationInfo();
}
private List<Object> getOrganizationInfo() {
String url = env.getProperty("web.INVENTORY_ORGANIZATIONS");
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
builder.queryParam("fields", "OrganizationId,OrganizationCode,OrganizationName,ManagementBusinessUnitId,ManagementBusinessUnitName");
builder.queryParam("onlyData", "true");
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(env.getProperty("authentication.name"),env.getProperty("authentication.password"));
HttpEntity request = new HttpEntity(headers);
ResponseEntity<Wrapper> temp = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, request, new ParameterizedTypeReference<Wrapper>() {});
List<Object> data = temp.getBody().getOrganization();
return data;
}
}
SubInventoryController
#RestController
public class SubInventoryController {
#Autowired
private RestTemplate restTemplate;
#Autowired
private Environment env;
#GetMapping("/sub")
public List<Object> getSuvInventories() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("this is private :(");
builder.queryParam("onlyData", "true");
builder.queryParam("expand", "subinventoriesDFF");
builder.queryParam("limit", "999999");
builder.queryParam("fields", "OrganizationId,OrganizationCode,SecondaryInventoryName,Description");
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(env.getProperty("authentication.name"),env.getProperty("authentication.password"));
headers.set("REST-Framework-Version", "2");
HttpEntity request = new HttpEntity(headers);
ResponseEntity<Wrapper> subInventories = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, request, new ParameterizedTypeReference<Wrapper>() {});
List<Object> data = subInventories.getBody().getOrganization();
return data;
}
}
where I'm right now
#RestController
public class MainController {
#Autowired
private RestTemplate restTemplate;
#Autowired
private Environment env;
#Autowired
private OrganizationController organizationController;
#Autowired
private SubInventoryController subInventoryController;
#GetMapping("/manipulate")
public Map<Organization, List<SubInventories>> getManipulate() {
List<Organization> organizations = (List<Organization>)(Object) organizationController.getOrganization();
List<SubInventories> subInventories = (List<SubInventories>)(Object) subInventoryController.getSuvInventories();
Map<Organization,List<SubInventories>> result = new HashMap<Organization,List<SubInventories>>();
for(Organization organization : organizations) {
List<SubInventories> subInventoryMatched = (List<SubInventories>) subInventories.stream().filter( s -> s.getOrganizationId()== organization.getOrganizationId()).collect(Collectors.toList());
result.put(organizations.get(0), subInventoryMatched);
}
return result;
}
}
From what I understand I need to make a wrapper class for each POJO cause the response looks like this
/organizations
{
"items": [
{
"OrganizationId": 1,
"OrganizationCode": "adasd",
"OrganizationName": "Hotel Bahía Príncipe Sunlight Costa Adeje",
"ManagementBusinessUnitId": 131231,
"ManagementBusinessUnitName": "asdasfdas"
},
{
"OrganizationId": 2,
"OrganizationCode": "adadas",
"OrganizationName": "Hadasd",
"ManagementBusinessUnitId": 1231,
"ManagementBusinessUnitName": "aewfrqaew"
}]}
and /subInventories
{
"items": [
{
"OrganizationId": 1,
"OrganizationCode": "asada",
"SecondaryInventoryName": "adfasdfasdgf",
"Description": "pub"
},
{
"OrganizationId": 2,
"OrganizationCode": "asgfrgtsdh",
"SecondaryInventoryName": "B LOB",
"Description": "pub2"
}
]}
If used the generic one with Object I get a java.lang.ClassCastException: java.util.LinkedHashMap incompatible with com.demo.model.Organization in the stream().filter and for the merge of the fields another class to get the desired
{
"items": [
{
"OrganizationId": 1,
"OrganizationCode": "asdas",
"OrganizationName": "adsadasd",
"ManagementBusinessUnitId": 1,
"ManagementBusinessUnitName": "asdasdf",
"SecondaryInventoryName": "sfsdfsfa",
"Description": "pub1"
}]}
Tons of classes if i get lots of POJO
I assume the following from the information you provide:
You have two Datatypes (Java classes). They should be merged together to one Java class
You have to load this data from different sources
Non of the classes are leading
I can provide you some example code. The code is based on the previos adoptions. This will give you an idea. It's not a simple copy and paste solution.
At first create a class with all fields you want to include in the result:
public class Matched {
private Object fieldA;
private Object fieldB;
// Some getter and Setter
}
The Basic idea is that you load your data. Than find the two corresponding objects. After that do your matching for each field.
public List<Matched> matchYourData() {
// load your data
List<DataA> dataAList = loadYourDataA();
List<DataB> dataBList = loadYourDataB();
List<Matched> resultList = new ArryList<>();
for (dataA: DataA) {
DataB dataB = dataBList.stream()
.filter(data -> data.getId() == dataA.getId())
.findFirst().orElseThrow();
// Now you have your data. Let's match them.
Matched matched = new Matched();
matched.setFieldA(dataB.getFieldA() == dataA.getFieldA() ? doSomething() : doSomethingElse());
// Set all your fields. Decide for everyone the matching strategy
resultList.add(matched);
}
return resultList;
}
This is a quite simple solution. Of course you can use Tools like Mapstruct for mapping purpose. But this depends on your environment.

Trying to insert Json into Neo4j

Everyone I am new to neo4j and I am trying to enter Json into Neo4j but I am getting Match statement instead of create. Earlier I tried something myself and when When I inserted Json message only as
{"name":"john","dept":"Science"}
it went without a glitch but everytime I want try to add numeric data it gets error.
2020-03-10 13:21:59.793 INFO 94817 --- [ntainer#0-0-C-1] o.n.o.drivers.http.request.HttpRequest : Thread:
29, url: http://localhost:7474/db/data/transaction/92, request: {"statements":[{"statement":"UNWIND {rows}
as row **MATCH** (n) WHERE ID(n)=row.nodeId SET n:`UsersInfo` SET n += row.props RETURN row.nodeId as ref,
ID(n) as id, {type} as type","parameters":{"type":"node","rows":[{"nodeId":23,"props":{"name":"raj",
"dept":"science","age":11}}]},"resultDataContents":["row"],"includeStats":false}]}
These are my classes
KafkaConfiguration
#EnableKafka
#Configuration
public class KafkaConfiguration {
#Bean
public ConsumerFactory<String, Users> userConsumerFactory(){
Map<String, Object> config = new HashMap<>();
config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
config.put(ConsumerConfig.GROUP_ID_CONFIG, "group_json");
config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
return new DefaultKafkaConsumerFactory<>(config, new StringDeserializer(),
new JsonDeserializer<>(Users.class));
}
#Bean
public ConcurrentKafkaListenerContainerFactory<String, Users> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, Users> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(userConsumerFactory());
return factory;
}
}
KafkaConsumer class
Service
public class KafkaConsumer {
#Autowired
public Neo4jservice neo4jService;
#KafkaListener(topics = "UsersJson", groupId = "group_id", containerFactory = "kafkaListenerContainerFactory")
public void consume(Users users) {
System.out.println("Consumed message: " + users);
UsersInfo usern = new UsersInfo();
usern.setAge(users.getAge());
usern.setDept(users.getDept());
usern.setId(users.getId());
usern.setName(users.getName());
neo4jService.saveIntoStudentsTable(usern);
}
}
Neo4jService
#Service
public class Neo4jservice {
#Autowired
private UsersRepo userRepo;
public UsersInfo saveIntoStudentsTable(UsersInfo users) {
UsersInfo usern = userRepo.save(users);
return (usern);
}
}
UsersRepo
#Repository
public interface UsersRepo extends Neo4jRepository<UsersInfo, Long>{
}
Users class
public class Users {
private Long id;
private String name;
private String dept;
private Integer age;
**getters,setters and toString method here**
}
Likewise UsersInfo class
#NodeEntity
public class Users {
#Id
private Long id;
private String name;
private String dept;
private Integer age;
**getters,setters and toString method here**
}
Any help will be greatly appreciated. Thanks
You are setting also the id value of the User class.
This will make Spring Data Neo4j and the Neo4j Object Graph Mapper that is used for the persistence think that the entity already exists.
In this case it will MATCH on an existing id(n) and update the properties as you can see in the logs instead of CREATE a new node.

How to send Java collections containing subclasses to spring controller

I'm trying to send collections to my spring MVC controller:
#RequestMapping("/postUsers.do")
public #ResponseBody ResponseDTO postUsers(#ModelAttribute("mapperList") MapperList mapperList) {
//prints {"users":null}
System.out.println(new ObjectMapper().writeValueAsString(mapperList));
return new ResponseDTO();
}
this is the code posting my users :
public ResponseDTO postUsers(ArrayList<User> users) {
ResponseDTO serverResponse = null;
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestMethod("POST");
// prints {"users":[{"property1":"x","property1":y}]}
System.out.println(objectMapper.writeValueAsString(new MapperList(users)));
objectMapper.writeValue(connection.getOutputStream(), objectMapper.writeValueAsString(new MapperList(users)));
//blabla ...
}
and this is the object containing my list :
public class MapperList implements Serializable {
private static final long serialVersionUID = 8561295813487706798L;
private ArrayList<User> users;
public MapperList() {}
public MapperList(ArrayList<User> users) {
this.setUsers(users);
}
public ArrayList<User> getUsers() {
return users;
}
public void setUsers(ArrayList<User> users) {
this.users = users;
}
}
and this is the users type to post:
public abstract class User implements Serializable {
private static final long serialVersionUID = -1811485256250922102L;
private String property1;
private String property2;
public User() {}
public User(String prop1, String prop2) {
// set properties
}
// getters and setters
}
the problem is, when I output the value of the users's array before to post it to the controller, I got the following json value :
{"users":[{"property1":"x","property1":y}]}
but in the controller, when I print what I get from the request body, I only get :
{"users":null}
I also tryed with the annotation #RequestBody instead of #ModelAttribute("mapperList") and a JSONException is displayed :
*A JSONObject text must begin with '{' at 1 [character 2 line 1]\r\n*
My array list of users contains only one user that should be displayed. I don't understand why this doesn't work...
Thanks for any help !
You can chnage your MapperList class definition as public class MapperList extends ArrayList<User>{ ..} you dont need to define any instance variable like private ArrayList users inside MapperList class. Use #Requestbody annotation. You will be able to use MapperList as a ArrayList
Try to use:
public class MapperList{
private List<User> users;
//setter and getter
//toString
}
public class User{
private String property1;
private String property2;
//getter + setter
}
json:
{"users":[{"property1":"x", "property2":"y"}]}
in controller use #RequestBody. In that case Jackson will map your json to ArrayList of users.
#ResponseStatus(HttpStatus.OK)
#RequestMapping("/postUsers.do")
public #ResponseBody ResponseDTO postUsers(#RequestBody MapperList users) {
System.out.println(users);
return null;
}
no need to get objectMapper in that case. Don't forget to set content-type in request header to application/json. It required by Spring to handle #RequestBody processing.
If not working try to change MapperList:
List<User> users = new ArrayList<User>();
On the server side keep the #RequestBody annotation:
public #ResponseBody ResponseDTO postUsers(#RequestBody MapperList mapperList)
...
But this line causes problems:
objectMapper.writeValue(
connection.getOutputStream(),
objectMapper.writeValueAsString(new MapperList(users))
);
First it converts the object to JSON and then again uses objectMapper to JSON-encode the string into output stream. Try the following instead:
connection.getOutputStream().write(
objectMapper.writeValueAsString(new MapperList(users))
.getBytes("UTF-8")
);
or directly output to stream:
objectMapper.writeValue(
connection.getOutputStream(),
new MapperList(users))
);
Zbynek gave me part of the answer. Indeed
objectMapper.writeValue(
connection.getOutputStream(),
objectMapper.writeValueAsString(new MapperList(users))
);
doesn't work properly in my case
But moreover, my User class was an abstract class, with many type of User as subclasses. so the #RequestBody annotation couldn't work without specified the object type in the Json.
I used the following annotations on User class to make it working :
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = SubClassA.class, name = "a"),
#JsonSubTypes.Type(value = SubClassB.class, name = "b")
})
Thanks a lot for all your answers.

How to display custom error message for #Valid in non-primitive datatype

I am doing a spring MVC project and I wanted to display an error to the front page if value for employeelist is not selected
Here is my domain class
#Valid
private HashMap<Long, String> employeelist = new HashMap<Long, String>();
I have Controller, jsp page and also .properties file in place. All my error messages are being displayed except this. Help!!!
Assuming that employeeList is part of another object, you can use the Hibernate-Validator #NotEmpty annotation. For example:
Your object
public class YourObject {
#NotEmpty(message = "Must specify at least one employee")
private HashMap<Long, String> employeelist = new HashMap<Long, String>();
}
Your controller
#Controller
public class YourController {
#RequestMapping(value = "/", method = RequestMethod.POST)
public void save(#Valid YourObject yourObject) {
// use your object here
}
}
You need to create custom JSR-303 validator and like in this tutorial and there you can define your own validation message.

Resources