Error locating String field in Spring Boot - spring-boot

I'm trying to find a company by its CNPJ(Brazilian corporate tax payer registry number) in a DB (H2), but it's returning an error
{
"timestamp": "2022-03-30T19:30:23.823+00:00",
"status": 404,
"error": "Not Found",
"path": "/companies/cnpj/30101554000146"
}
I've tried other alternatives using:
http://localhost:8080/companies/cnpj/'30.101.554/0001-46', http://localhost:8080/companies/cnpj/"30.101.554/0001-46",
but the error persists. I implemented like this :
#Entity
#Table(name = "company")
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#CNPJ
private String cnpj;
//skipped
}
public interface CompanyRepository extends JpaRepository<Company,Long> {
Optional<Company> findByCnpj(String cnpj);
}
public class CompanyDTO {
private Long id;
private String name;
private String cnpj;
//skipped
}
#Service
#Transactionalpublic class CompanyService {
#Autowired
private CompanyRepository companyRepository;
#Transactional(readOnly = true)
public CompanyDTO findById(Long id) {
Company resultado = companyRepository.findById(id).get();
CompanyDTO dto = new CompanyDTO(resultado);
return dto;
}
#Transactional(readOnly = true)
public CompanyDTO findByCnpj(String cnpf) {
Optional<Company> resultado = companyRepository.findByCnpj(cnpf);
CompanyDTO dto = new CompanyDTO(resultado.get());
return dto;
}
}
#RestController
#RequestMapping(value = "/companies")public class CompanyController {
#Autowired
private CompanyService companyService;
#GetMapping(value = "/{id}")
public CompanyDTO findById(#PathVariable Long id) {
return companyService.findById(id);
}
#GetMapping(value = "/cnpj/{cnpj}")
public CompanyDTO findByCnpj(#PathVariable String cnpj) {
return companyService.findByCnpj(cnpj);
}
}
The expected output would be:
[
{"id": 1,
"nome": "Company 123",
"cnpj": "30.101.554/0001-46"
}
]
UPDATE:
I changed #GetMapping(value = "/cnpj/{cnpj}") to #GetMapping(value = "/cnpj/**") and:
#GetMapping(value = "/cnpj/**")
public CompanyDTO findByCnpj(HttpServletRequest request) {
return companyService.findByCnpj(request.getRequestURI().split(request.getContextPath() + "/cnpj/")[1]);
}
Works for me! Thanks

As explained here, pathParams with slashes can be realy tricky while using spring-boot. This article explains pretty well what to do to avoid getting an error 404 when your pathVariable has a slash.

Related

Spring Controller Returns Object Incompletely

There are three classes (Course, Lesson, User).
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "usr")
#Data
public class User extends RepresentationModel<User> implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String username;
private String password;
#ElementCollection(targetClass = ERole.class, fetch = FetchType.EAGER)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
private Set<ERole> roles;
}
#Data
#Entity
#NoArgsConstructor
public class Lesson extends RepresentationModel<Lesson> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String startTime;
private String endTime;
private String dayOfWeek;
#ManyToOne
private User teacher;
}
#EqualsAndHashCode(callSuper = true)
#Data
#Entity
public class Course extends RepresentationModel<Course> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date startDate;
private Date endDate;
private String name;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<User> teachers;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<User> students;
private String description;
#ManyToMany(cascade = CascadeType.ALL)
private Set<Lesson> lessons;
}
And also RestController (CoursesController). When accessing the server at /courses, I get the correct server response with all fields
.
#RestController
#RequestMapping("/courses")
public class CoursesController {
private final CourseService courseService;
private final UserService userService;
private final LessonService lessonService;
#Autowired
public CoursesController(CourseService courseService, UserService userService, LessonService lessonService) {
this.courseService = courseService;
this.userService = userService;
this.lessonService = lessonService;
}
#GetMapping
#Operation(
summary = "getAllCourses",
description = "Returns all available courses"
)
public ResponseEntity<Page<Course>> getAllCourses(#PageableDefault(sort = "id", size = 5) Pageable pageable) {
try {
Page<Course> coursePage = courseService.findAll(pageable);
for (Course course : coursePage.getContent())
course.add(linkTo(methodOn(CoursesController.class).getCourse(course.getId().toString())).withSelfRel());
return ResponseEntity.ok(courseService.findAll(pageable));
}
catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
#GetMapping("/{course-id}")
#Operation(
summary = "getCourse",
description = "Returns course by ID"
)
public ResponseEntity<Course> getCourse(#PathVariable ("course-id") String courseId) {
try {
Course course = courseService.getCourseById(courseId);
course.add(linkTo(methodOn(CoursesController.class).getCourse(courseId)).withSelfRel());
return ResponseEntity.ok(course);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
Why, when requesting a course by ID (GET /courses/{id}), does Spring return an incomplete object (despite the fact that I manually added several teachers, students and lessons)?
I need to get all the fields of my object.
My CourseRepository below.
#Repository
#Transactional
public interface CourseRepository extends JpaRepository<Course, Long> {
}
My CourseService below.
#Service
public class CourseService {
private final CourseRepository courseRepository;
private final LessonRepository lessonRepository;
private final UserRepository userRepository;
#Autowired
public CourseService(CourseRepository courseRepository, LessonRepository lessonRepository, UserRepository userRepository) {
this.courseRepository = courseRepository;
this.lessonRepository = lessonRepository;
this.userRepository = userRepository;
}
public Page<Course> findAll(Pageable pageable) {
return courseRepository.findAll(pageable);
}
public Course createCourse(CourseDto courseDto) {
Course course = new Course(courseDto.getStartDate(), courseDto.getEndDate(), courseDto.getName(), courseDto.getDescription());
return courseRepository.saveAndFlush(course);
}
public Optional<Course> getCourseById(String id) {
return courseRepository.findById(Long.parseLong(id));
}
public Course updateCourse(CourseDto courseDto, String id) {
Course course = courseRepository.findById(Long.parseLong(id)).get();
course.setStartDate(courseDto.getStartDate());
course.setEndDate(courseDto.getEndDate());
course.setName(courseDto.getName());
course.setDescription(courseDto.getDescription());
return courseRepository.saveAndFlush(course);
}
public Page<Lesson> getLessonsByCourse(String courseId, Pageable pageable) {
Course course = courseRepository.findById(Long.parseLong(courseId)).get();
return new PageImpl<>(new ArrayList<>(course.getLessons()), pageable, course.getLessons().size());
}
public Course addLesson(String courseId, LessonDto lessonDto) {
Course course = courseRepository.findById(Long.parseLong(courseId)).get();
Lesson lesson = new Lesson();
lesson.setStartTime(lessonDto.getStartTime());
lesson.setEndTime(lessonDto.getFinishTime());
lesson.setDayOfWeek(lessonDto.getDayOfWeek());
lesson.setTeacher(userRepository.getUserById(lessonDto.getTeacherId()));
lessonRepository.saveAndFlush(lesson);
System.out.println(lesson);
course.getLessons().add(lesson);
return courseRepository.saveAndFlush(course);
}
public void deleteCourse(String id) {
courseRepository.deleteById(Long.parseLong(id));
}
}
Which I would (or might) expect as well. I would links to be generated for those additional relationshps (at least normally with Spring Data RESt handling this is what would happen). I wonder what happens if you ditch the RepresentationModel from your JPA model and just expose Course then. As stated you don't really want your JPA and HATEOAS stuff to be intertwined. You want to have a specialized projection/dto to expose. WHy does it work for your findAll. well you aren't adding links to it (although you think it does but your findAll executes twice!).
Removed RepresentationModel from User class.
Thx to #M.Deinum

ModelMapper issue with mapping basic POJOS

I have 2 basic POJOs that i use to build a json object :
public class ProductCreateRequestModel {
private String name;
private double price;
private int qty;
private String imgPath;
private CategoryRequestCreateProductModel category;
}
public class CategoryRequestCreateProductModel {
private String name;
private String categoryKeyId;
}
Basically it allow me to use a simple json like this one :
{
"name": "Pizza,
"price": 344.0,
"qty": 15,
"imgPath": "new/pathImage",
"category": {
"categoryKeyId": "23ume70Fu6yqyGUWfQkW110P4ko3gZ",
"name": "Starter"
}
}
I want to send this JSON and persist datas and i expect an object in return that i build with this POJO:
public class ProductRest {
private String productKeyId;
private String name;
private double price;
private int qty;
private String imgPath;
private CategoryRest category;
}
In my controller i just have to call a method which use PostMapping
#PostMapping(
consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE },
produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }
)
public ProductRest createProduct(#RequestBody ProductCreateRequestModel productCreateRequestModel) throws Exception {
ProductRest returnValue = new ProductRest();
if(productCreateRequestModel.getName().isEmpty() || productCreateRequestModel.getPrice() <= 0)
throw new ApplicationServiceException(ErrorMessages.MISSING_REQUIRED_FIELDS.getErrorMessage());
ModelMapper modelMapper = new ModelMapper();
ProductDto productDto = modelMapper.map(productCreateRequestModel, ProductDto.class);
ProductDto createdProduct = productService.createProduct(productDto);
returnValue = modelMapper.map(createdProduct, ProductRest.class);
return returnValue;
}
My service layer is actually doing nothing special :
#Override
public ProductDto createProduct(ProductDto productDto) {
return productDto;
}
My DTO layer contains the following fields :
#Getter #Setter
public class ProductDto implements Serializable {
// ommit this member and do not generate getter / setter
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
private Long id;
private String productKeyId;
private String name;
private double price;
private int availableQty;
private String imgPath;
private CategoryDto category = new CategoryDto();
}
#Getter #Setter
public class CategoryDto implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
private long id;
private String categoryKeyId;
private String name;
private CategoryDto parentCategory;
private List<CategoryDto> subCategories;
private String parentCategoryKeyId;
private Long parentCategoryId;
}
While trying to run this basic code I obtain an error message :
java.lang.NumberFormatException: For input string: "23ume70Fu6yqyGUWfQkW110P4ko3gZ"

Spring Boot #JsonIgnore

Can I use the JsonIngore in a RestController Method? if I put the #JsonIgnore in my VO he will ignore this prop in all request, but I want to ignore only in some request: sample:
I have
public class Pedido{
private Long id;
private Date day;
private List<Item> items;
}
public class Item {
private Long id;
private String nome;
}
#RestController
#RequestMapping("/pedido")
public class PedidoController{
#GetMapping(value = "/")
public List<Pedido> findAll(){
//HERE I dont need to return the List<Item>
}
#GetMapping(value = "/{id}")
public Pedido findById(#PathVariable Long id){
//HERE I need to return the List<Item>
}
}
#JsonView is the right solution for you. Here is an example, and code snippet from the link as below
public class View {
interface Summary {}
}
public class User {
#JsonView(View.Summary.class)
private Long id;
#JsonView(View.Summary.class)
private String firstname;
#JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
}
#RestController
public class MessageController {
#Autowired
private MessageService messageService;
#JsonView(View.Summary.class)
#RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
#RequestMapping("/{id}")
public Message getMessage(#PathVariable Long id) {
return messageService.get(id);
}
}

springboot exceptionhandling without controller class

How to handle exception handling in Spring Boot 1.5.4 without controller class? Currently, I have only entity & repository class as below.
Task.class: (entity)
#Entity
#Table(name = "task")
public class Task implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Length(min = 1)
private String name;
public Task() {
}
public Task(String name) {
this.name = name;
}
public Task(Long id, String name) {
this.name = name;
}
public long getId() {
return id;
}
public String getName(){
return name;
}
}
Repository.class:
public interface TaskRepository extends PagingAndSortingRepository<Task, Long> {
}
POST method: return 200 ok
http://localhost:8080/tasks
{
"name" : "test"
}
But,
{
"name" : ""
}
returns 500 , instead of 400 error.
Pls let me know, if any way to handle this exception without a controller class.
You could use a global #ExceptionHandler with the #ControllerAdvice annotation. Basically, you define which Exception to handle with #ExceptionHandler within the class with #ControllerAdvice annotation, and then you implement what you want to do when that exception is thrown.
Like this:
#ControllerAdvice(basePackageClasses = RepositoryRestExceptionHandler.class)
public class GlobalExceptionHandler {
#ExceptionHandler({ValidationException.class, JsonParseException.class})
public ResponseEntity<Map<String, String>> yourExceptionHandler(Exception e) {
Map<String, String> response = new HashMap<String, String>();
response.put("message", "Bad Request");
return new ResponseEntity<Map<String, String>>(response, HttpStatus.BAD_REQUEST);
}
}
See also: http://www.ekiras.com/2016/02/how-to-do-exception-handling-in-springboot-rest-application.html

Using with JSF - HIbernate validation fails (javax.validation.ConstraintViolationException)

I'm using JSF, Spring and Hibernate. Post model has Hibernate annotated attributes:
#Entity
#Table(name = "posts")
public class Post implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id")
private Long id;
private int version;
#Column(name = "title")
#NotEmpty(message = "Title should not be empty")
private String title;
... getters/setters
}
When I try to create a new Post in the corresponding bean(disregard syntax proper à pretty-faces) :
#Component
#ManagedBean
#RequestScoped
#URLMappings(mappings = {
#URLMapping(id = "posts", pattern = "/posts/", viewId = "/faces/posts/list.xhtml"),
#URLMapping(id = "new", pattern = "/posts/new", viewId = "/faces/posts/new.xhtml")
})
public class PostBean {
#Autowired
private PostService postService;
private List<Post> posts;
private Post post = new Post();
public List<Post> getPosts() {
return postService.findAll();
}
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public String create(Post post) {
this.post = postService.save(post);
return "pretty:posts";
}
}
PostRepository:
public interface PostRepository extends CrudRepository<Post, Long> {
public Post findByTitleIgnoreCase(String title);
}
PostServiceImpl:
#Service("postService")
#Repository
#Transactional
public class PostServiceImpl implements PostService {
#Autowired
private PostRepository postRepository;
#Override
#Transactional(readOnly = true)
public List<Post> findAll() {
return Lists.newArrayList(postRepository.findAll());
}
#Override
#Transactional(readOnly = true)
public Post findById(Long id) {
return postRepository.findOne(id);
}
#Override
#Transactional(readOnly = true)
public Post findByTitleIgnoreCase(String title) {
return postRepository.findByTitleIgnoreCase(title);
}
#Override
#Transactional
public Post save(Post post) {
return postRepository.save(post);
}
#Override
public void destroy(Post post) {
postRepository.delete(post);
}
}
I get the javax.validation.ConstraintViolationException. Strange is that if remove Hibernate annotation and put required="true" directly into the JSF page aside to the title textfield, it works.
<h:inputText id="title" value="#{postBean.post.title}" required="true"/>
Any idea ? You can find the project code source at my github repo
Thank you.
#NotNull is a JSR303 validation, #NotEmpty - is the one by Hibernate, that is the only difference. Even after changing for #NotNull I still get the same error.
[update] By the way, I removed #ManagedBean annotation, it is not needed anymore.
[update-2] #AVolpe: It will change nothing, just move the message to be displayed aside of the title textfield. The error happens when calling:
public String create(Post post) {
this.post = postService.save(post);
return "pretty:posts";
}
Because this error the version 2.2.6 of JSF don't work well with BeanValidation, update to the version 2.2.7 or downgrade to 2.2.4.
See:
This answer
Fixed by upgrading jsf version up 2.2.7 as suggested by AVolpe.

Resources