I have normal Java POJO class, which is not an entity class, named Student. I am using spring boot validator. within my REST controller when, for a request just a student object will be returned. I have set different validation in the Student class. But the validation is not working. I have given the age limit 10 to 30. But it is creating Object with 35 years old. Is there any way to make this validation work? Note : i am doing this as test, if this validation work, i will use it within my main project.
Student
package com.mkyong;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
#Validated
public class Student {
#NotEmpty
private String id;
#NotEmpty
private String name;
#Min(10)
#Max(30)
private int age;
public Student() {
}
public Student(String id, String name, #Min(10) #Max(30) int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge( #Min(10) #Max(30) int age) {
this.age = age;
}
}
**Rest Controller: **
public class BookController {
// Find
#GetMapping("/student")
Student studentCreate() {
Student student = new Student("","",35);
student.setAge(36);
return student;
}
}
Your entity will be validated automatically in case that your request body argument will be annotated with #Valid annotation
You can validate your entities manually by using: javax.validation.Validator
Following your case:
Inject Validator in BookController.
call Validator#validate using your DTO as a parameter.
Check if there are viloations
public class BookController {
#Autowired
Validator validator;
// Find
#GetMapping("/student")
Student studentCreate() {
Student student = new Student("","",35);
student.setAge(36);
Set<ConstraintViolation<Student>> result = validator.validate(student);
if (!result.isEmpty()) {
//do here whatever you want with each validation violation.
}
return student;
}
}
For more details check official docs: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation
Note:
Depends on your version of spring boot, org.springframework.boot:spring-boot-starter-validation dependency might be missing from your dependencies, so double check if it exists.
Related
I tried to migrate my application from Hibernate 5.4.30.Final to 6.1.6.Final, database H2 2.1.214. I observed a different behaviour regarding generics when using a CriteriaQuery. I have stripped it down to a testcase (which does not make any sense but shows the problem). In Hibernate 5 the following query to a generic field name runs fine whereas Hibernate 6 throws an Exception.
CriteriaBuilder cb = eMgr.getCriteriaBuilder();
CriteriaQuery<String> cr = cb.createQuery(String.class);
Root<Person> person = cr.from(Person.class);
cr.select(person.<String> get("name"));
TypedQuery<String> query = eMgr.createQuery(cr);
Exception:
Converting `org.hibernate.query.QueryTypeMismatchException` to JPA `PersistenceException` : Specified result type [java.lang.String] did not match Query selection type [java.lang.Object] - multiple selections: use Tuple or array
Here are my sample class definitions:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
#Entity
public class GenericPerson<T>
{
#Id
#GeneratedValue(generator = "increment")
private long id;
private T name;
public GenericPerson() { }
public GenericPerson(T name) { this.name = name;}
public T getName() { return this.name; }
public void setName(T name) { this.name = name; }
public long getId() { return this.id;}
public void setId(long id) { this.id = id; }
}
#Entity
public class Person extends GenericPerson<String>
{
public Person() { }
public Person(String name) { super(name); }
}
Hibernate 5 seems to handle generics differently to Hibernate 6 but I could not find any hint in the migration document. Why fails the test case with Hibernate 6?
The solution is to use the cast method for the generic field:
cr.select(person.get("name").as(String.class));
I am new spring boot developer and i am trying to develope and rest api . when I do it ,I get and issues that my api return two duplicated response in postman .But i haven't code anythiong to get duplicated valuese in my code . the one of duplicate values is my model clase variable and athor one is table's attribute name .
below response in postman
model class
public class person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY )
private Long id;
#Column(name = "name")
private String Name ;
#Column(name ="surname")
private String Surname;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getSurname() {
return Surname;
}
public void setSurname(String surname) {
Surname = surname;
}
}
repository
#Repository
public interface personRepository extends JpaRepository<person,Long> {
}
controller
#RestController
#RequestMapping("/person")
public class personController {
#Autowired
private personRepository repository;
public personController(personRepository repository) {
this.repository = repository;
}
#GetMapping("/view/list/person")
private List<person> viewperson() {
return repository.findAll();
}
#PostMapping("/insert/person")
private person savePerson(#RequestBody person obj) {
return repository.save(obj);
}
#DeleteMapping("/delete/{id}")
private void delete(#PathVariable Long id) {
repository.deleteById(id);
}
}
application.properties
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialec
t
The problem is that you're not following the proper conventions in your naming strategy.
Due to this, Jackson doesn't know that your getters (getSurname(), getName()) are referencing the fields Surname and Name. That's why it serializes both your fields and your getters separately to JSON.
To fix this, you can follow the Java naming conventions and use a lowercase letter for the first character of your fields.
For example:
#Column(name = "name")
private String name; // Change this
#Column(name ="surname")
private String surname; // Change this
This will change your JSON output to:
{
"id": 1,
"name": "bryan",
"surname": "Nicky"
}
If you want to keep your JSON with capital letters, you can use the #JsonProperty annotation:
#JsonProperty("Name") // Add this
#Column(name = "name")
private String name;
#JsonProperty("Surname") // Add this
#Column(name ="surname")
private String surname;
Unrelated to your question, but according to those naming conventions, your classes should start with a capital (eg. Person, PersonController, PersonRepository, ...).
i am creating a simple spring boot project when i run the project ran into the problem with Spring Boot Ambiguous mapping. Cannot map method. i have create two various controller those are student and course controller.i completly attached the error below.
i attached the full
**gub link here** https://github.com/raguram1986/SpringSecuritys
Full Error i attached below
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'studentController' method
com.example.studentmanagement.Controller.StudentController#saveStudent(Student)
to {POST [/save]}: There is already 'courseController' bean method
com.example.studentmanagement.Controller.CourseController#saveCourse(Course) mapped.
Controller
#Controller
public class StudentController {
#Autowired
private StudentService service;
#GetMapping("/Student")
public String viewHomePage(Model model) {
List<Student> liststudent = service.listAll();
// model.addAttribute("liststudent", liststudent);
System.out.print("Get / ");
return "Student";
}
#GetMapping("/addStudent")
public String add(Model model) {
List<Student> liststudent = service.listAll();
model.addAttribute("liststudent", liststudent);
model.addAttribute("student", new Student());
return "addstudent";
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveStudent(#ModelAttribute("student") Student std) {
service.save(std);
return "Student";
}
#RequestMapping("/edit/{id}")
public ModelAndView showEditStudentPage(#PathVariable(name = "id") int id) {
ModelAndView mav = new ModelAndView("addstudent");
Student std = service.get(id);
mav.addObject("student", std);
return mav;
}
#RequestMapping("/delete/{id}")
public String deleteStudentPage(#PathVariable(name = "id") int id) {
service.delete(id);
return "Student";
}
Student
#Entity
public class Student {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String stname;
private String course;
private int fee;
public Student() {
}
public Student(Long id, String stname, String course, int fee) {
this.id = id;
this.stname = stname;
this.course = course;
this.fee = fee;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getStname() {
return stname;
}
public void setStname(String stname) {
this.stname = stname;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public int getFee() {
return fee;
}
public void setFee(int fee) {
this.fee = fee;
}
StudentRepository
#Repository
public interface StudentRepository extends JpaRepository<Student, Long>{
}
Service
#Service
public class StudentService
{
#Autowired
private StudentRepository repo;
public List<Student> listAll() {
return repo.findAll();
}
public void save(Student std) {
repo.save(std);
}
public Student get(long id) {
return repo.findById(id).get();
}
public void delete(long id) {
repo.deleteById(id);
}
}
In your StudentController you have the endpoint /save
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveStudent(#ModelAttribute("student") Student std) {
service.save(std);
return "Student";
}
But you haven't included the CourseController class in your question, which is mentioned in the error.
If you have defined an endpoint /save in that CourseController, then you have to rename it. Otherwise, when you invoke /save which controller needs to be invoked cannot be determined.
Add #RequestMapping above StudentController as below
#Controller
#RequestMapping("/students")
public class StudentController {
...
}
You are getting exception because there is already a mapping defined for path
/save
without being any controller mapping, so the first with root mapping is considered, but the next time it encounters same mapping it is already registered hence it is complaining. For clarity i'd suggest to add #RequestMapping to CourseController as well.
So now your course controller also becomes:
#Controller
#RequestMapping("/courses")
public class CourseController {
...
}
The best practice is to always add a request mapping at class level as well, like in your case, add a mapping like "/students" for SutdentController and "/course" for CourseController itself and then all other methods will be under that i.e. "/student/save" and then you will not face this issue anymore.
please help me with this one. I have a very simple crud project with a student table in oracle DB(ID,name,age,email) in springboot and all i want to know is get a student with a method in the jpa repository that calls a stored procedured. when run the project i got an error PLS-00221 is not a procedure or is undefined.
--stored procedure
CREATE OR REPLACE FUNCTION
findstudentbyid
RETURN STUDENT%ROWTYPE
IS
estudiante STUDENT%ROWTYPE;
BEGIN
SELECT *
INTO estudiante
FROM STUDENT
WHERE ID=1;
RETURN estudiante;
END findstudentbyid;
//Entity in sprongboot project
#Entity
#Table
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(
name = "findstudentbyid",
procedureName = "findstudentbyid"
)
})
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
//Private variables
private Long ID;
private String name;
private Number age;
private String email;
//Constructor
protected Student(){}
public Student(String name , Number age , String email){
this.name = name;
this.age = age;
this.email = email;
}
public Long getID() {
return ID;
}
public String getName() {
return name;
}
public Number getAge() {
return age;
}
public String getEmail() {
return email;
}
}
//JPA CRUD REPOSITORY
public interface StudentRepository extends CrudRepository<Student,Long>{
#Procedure(name = "findstudentbyid")
Iterable<Student> findstudentbyid();
}
You create a function but not a stored procedure.
These objects are different for DB and JPA, try to change your create function to create procedure or change the procedure's call to the function's call signature.
Also, see here for more info about JPA and function call.
I'm writing a simple auditing framework with aspectj, which allows me to audit the fields of a class which are annotated with an #Audit annotation.
As value the #Audit annotation expects an array of field names to be watched
Example Usage:
#Audit({"name","phoneNumber"})
class User {
private String name;
private String phoneNumber;
public getName(){
return name;
};
public setName(String name){
this.name=name;
}
}
How does the Aspect look that watches the assignment of fields that are annotated like in the above example?
Here the stub of my first try:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
public #interface Audit {
String[] value()
}
#Aspect
class AuditAspect {
#Pointcut("????")
public void markedFieldWasModified(){}
#AfterReturning("markedFieldWasModified()")
public void addFieldToModifiedFields(JoinPoint jp, AuditableEO eo){
eo.addModifiedField(jp.getSignature().getName());
}
// inter Type declarations
public interface IAuditableEO {
public Iterator<String> modifiedFields();
public boolean modified();
public boolean addModifiedField(String field);
};
}
according to https://eclipse.org/aspectj/doc/next/quick5.pdf
you should be able to do set(* *.*) && #target(Audit)
you then have to check the joinpoint if an auditable field is being modified.
How about not over-engineering the whole thing and directly annotating fields instead of classes? You can also skip the IAuditableEO interface IMO, I cannot see why it would be useful. Here is a simple example similar to yours, just with the aspect in code-style syntax (I prefer it to annotation-style syntax for clarity, but you can easily convert it by yourself):
Audit annotation for fields (not classes):
package de.scrum_master.app;
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface Audit {}
User class with a sample main method:
package de.scrum_master.app;
public class User {
private int id;
#Audit private String name;
#Audit private String phoneNumber;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPhoneNumber() { return phoneNumber; }
public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
public static void main(String[] args) {
User user = new User();
user.setId(11);
user.setName("John Doe");
user.setPhoneNumber("+49-1111-23456789");
System.out.println("User(" + user.getId() + ", " + user.getName() + ", " + user.getPhoneNumber() + ")");
}
}
Audit aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Audit;
public aspect AuditAspect {
pointcut fieldModification() : set(#Audit * *);
after() : fieldModification() {
System.out.println(thisJoinPointStaticPart);
}
}
Sample output:
set(String de.scrum_master.app.User.name)
set(String de.scrum_master.app.User.phoneNumber)
User(11, John Doe, +49-1111-23456789)
As you can see, only the annotated fields are caught, not the ID field. This permits for fine-granular auditing on a per-field basis. Furthermore in the advide you have everything you need if you want to record anything in and audit database: field type and name, class name and so forth.