JSR 303: Is it possible to validate whole graph automatically? - java

Is it possible to validate whole graph automatically ? By full graph I mean the object that is being validated and all its fields that are beans too.
Or I have to traverse through them manually as shown below ?
Currently I do something like this
Set<ConstraintViolation<OrderProxy>> violationsOrder =
validator.validate(order, Default.class, ClientGroup.class);
Set<ConstraintViolation<OrganizationProxy>> violationsOrg =
validator.validate(order.getSender(), Default.class, ClientGroup.class);
Set<ConstraintViolation<PersonProxy>> violationsPerson =
validator.validate(order.getSender().getPerson(),
Default.class, ClientGroup.class);

You can annotate any fields that you want validated with #Valid and when validating your main object, it will also validate the fields.
This works for example:
public class Order
#Valid
protected Header header;
#Valid
protected List<Detail> details;
}
You would need to call
Set<ConstraintViolation<Order>> violationsOrder = validator.validate(order, Default.class, ClientGroup.class);
to get all violations (also those for the fields). You would then have to parse the causes using getPropertyPath() to get the exact validation source, if you need it.
javax.validation.Path rp = violation.getPropertyPath();

Related

ModelMapper DTO-->Entity. How to skip unconditionally all fields not mapped

I have two classes (entity and DTO)
public class Deliver {
private Long id;
private String uri;
private Instant moment;
private DeliverStatus status; // enum PENDING,ACCEPTED,REJECTED
private String feedback; // feedback about received task
private Integer correctCount; // nr of correct questions
private Enrollment enrollment;
private Lesson lesson;
// constructors, getters and setters..
public class DeliverRevisionDto {
private DeliverStatus status;
private String feedback;
private Integer correctCount;
// constructors, getters and setters..
The goal is pretty simple, update the entity fields conveyed by Dto class I have the following code at Service layer (Spring Boot version 2.4.4):
#Service
public class DeliverService {
#Autowired
private DeliverRepository deliverRepository;
#Autowired
private ModelMapper modelMapper;
#Transactional
public void saveRevision(Long id, DeliverRevisionDto dto) {
Deliver deliver = deliverRepository.getOne(id);
System.out.println("BEFORE MAPPING: " + deliver.toString()); // # debug purpose
deliver = modelMapper.map(dto, Deliver.class);
// # debug purpose
TypeMap<DeliverRevisionDto, Deliver> tm = modelMapper.getTypeMap(DeliverRevisionDto.class, Deliver.class);
List<Mapping> list = tm.getMappings();
for (Mapping m : list)
{
System.out.println(m);
}
System.out.println("AFTER MAPPING: " + deliver.toString()); // # debug purpose
deliverRepository.save(deliver);
}
}
The console output is:
BEFORE MAPPING: Deliver [id=1, uri=``https://github/someone.com``, moment=2020-12-10T10:00:00Z, status=PENDING, feedback=null, correctCount=null, enrollment=com.devsuperior.dslearnbds.entities.Enrollment#7e0, lesson=com.devsuperior.dslearnbds.entities.Task#23]`
`PropertyMapping[DeliverRevisionDto.correctCount -> Deliver.correctCount]`
`PropertyMapping[DeliverRevisionDto.feedback -> Deliver.feedback]`
`PropertyMapping[DeliverRevisionDto.status -> Deliver.status]`
`AFTER MAPPING: Deliver [id=null, uri=null, moment=null, status=ACCEPTED, feedback=Muito bem cabra, tarefa aceita., correctCount=5, enrollment=null, lesson=null]
The mapping of the 3 fields in DTO is done correctly, BUT all the other fields of my entity are set to null. I know that I can skip fields according http://modelmapper.org/user-manual/property-mapping/
The problem is that I don´t want to couple the code with specific field names/getters/setters, that´s the reason I´m using ModelMapper. I wonder if there is any configuration that, upon mapping the modelmapper object says "Hey, the TARGET class have way more fields than the SOURCE class, I will left them untouched unconditionally (meaning I don´t need to say what fields are).
I'm trying to map fields between 2 classes with different set of fields (some are the same), and when I map the class with smaller set of fields to the one with bigger set of fields, the mapper set fields that don´t match with "null", I want these fields untouched (with original values) without I telling which one they are, after all, the mapper knows which ones match.
ModelMapper documentation is not the best part of that framework. Let us see what happens in your code.
Here you fetch the entity to be updated from the repo:
Deliver deliver = deliverRepository.getOne(id);
and log it having all the fields as should be. However this line:
deliver = modelMapper.map(dto, Deliver.class);
does a re-assignment to your variable deliver. This method creates a new instance of Deliver class and assigns it to variable deliver so discarding the entity fetched from repo.
This new instance will have all the fields that are not existing or not set in DTO null.
This is the API doc that my IDE provides, fotr these two different methods:
String org.modelmapper.ModelMapper.map(Object source, Class destinationType)
Maps source to an instance of destinationType. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destinationType then one is created.
Versus
void org.modelmapper.ModelMapper.map(Object source, Object destination)
Maps source to destination. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destination.getClass() then one is created.
It might not be clearly stated that the first method actually creates a new instance based on the type (Class) passed but it should be clear that ModelMapper cannot alter some arbitrary variable just by knowing the type. You need to pass the variable to alter as method parameter.

Rest controller - Tyr to reduce the number of endpoint with parameters but get Ambiguous mapping error

I want to implement some simple endpoint in spring, trying to be as much Restful as possible and reduce the number of URL to use. Here are the GET url I want to call: (this is a simplified version)
GET /users
GET /users?id=123
GET /users?username=xyz
I used this controller:
#GetMapping()
public #ResponseBody
OutputUserDTO getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && !id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && !username.isEmpty()) {
return userService.getUserByUsername(username);
}
throw new MissingParameterException("...some message...");
}
#GetMapping()
public #ResponseBody
List<OutputUserDTO> getUsers() {
return userService.getUsers();
}
Of course I get an error, that is Ambiguous mapping.
I thought to always return a List so that I can merge the 2 endpoints and, in case you pass some parameters, return a Singleton... even though I don't know if it's a correct practice.
Or else, create one endpoint for each parameter, GET /users/{userId}, GET /users/{username}, ... but I don't like it neither (If I have 10 different way to get a user then I'll have to implement 10 endpoints :S)
What are some good practices in this case??
Thanks.
Replace MissingParameterException with return userService.getUsers();, and get rid of the other method, you know, the one with exactly the same mapping as the first method.
To make that work, you'd have to change return type to Object, which is not going to be a problem, since it's the actual object returned that controls the effect of #ResponseBody, not the declared type.
#GetMapping()
#ResponseBody
public Object getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && ! id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && ! username.isEmpty()) {
return userService.getUserByUsername(username);
}
return userService.getUsers();
}
FYI: #ResponseBody is a method-level annotation, so it should be listed before any keyword modifiers.
The Java Language Specification, section 8.3.1. Field Modifiers, says:
FieldModifier:
(one of)
Annotation public protected private
static final transient volatile
[...]
If two or more (distinct) field modifiers appear in a field declaration, it is customary, though not required, that they appear in the order consistent with that shown above in the production for FieldModifier.
It should be like #GetMapping("/users") on respective method
http://www.appsdeveloperblog.com/pathvariable-spring-mvc/
I suppose that the reason for that is, in getUserByParameter, both parameters are optional but if both the parameters are not passed it will conflict with your second getMapping.
more over, what is returned changes in the three scenarios. scenario 1 returns a list of DTOs while scenarios 2 & 3 return a single DTO
i dont think you can handle all three scenarios using your request path /users unless you want to wrap even a single DTO in a list, in which case you can simply merge your two methods. call getUsers() when both parameters are missing, in other cases, do what you currently do but wrap the response in a list.
if you want to keep them separate and return DTO or List, you should probably separate them out into /users and /user by specifying #GetMapping("/user") on method one and #GetMapping("/users") on method two
hope this helps

Custom JSON entity parsing in Google App Engine endpoint

I have to transform a very peculiar JSON payload into POJOs manually. I thought I could put the JSON string into a String entity:
#ApiMethod(
name = "postSomething",
path = "postSomething/{id}",
httpMethod = ApiMethod.HttpMethod.POST
)
public void postSomething(#Named("id") Integer id, HttpServletRequest request, String data) {
//Parse data here...
}
When I do that, I get an error: MissingParameterNameException: Missing parameter name. Parameter type (class java.lang.String) is not an entity type and thus should be annotated with #Named.
I tried to use an #ApiTransformer but I get a similar error.
Could you please give me an example of parsing the JSON content manually?
The error message says that String data needs to have an #Named annotation, similar to Integer id.
I worked around this issue by using a Collections class instead of String and manual parsing:
#ApiMethod(
name = "postSomething",
path = "postSomething/{id}",
httpMethod = ApiMethod.HttpMethod.POST
)
public void postSomething(#Named("id") Integer id, HttpServletRequest request, HashMap<String,String> data) {
//Parse each item of data here...
}
From this, I can parse each item inside the data. The values contain a hierarchy of either other collections (List for an array, Map for a JSON entity) or String for an actual value. So by doing this I don't need to use any other JSON parsing library such as Jackson.
String is not an #Entity object so it can't be passed as a parameter (a data parameter) to the endpoints API without proper annotation (like #Name or #Nullable). Either you must remove it from the method declaration or annotate it with #Name or #Nullable.

Best practice for storing global data in J2EE App using Hibernate

I'm looking for the best solution to store Java EE application's global data using Hibernate. It will consist of key value pairs. Example:
projectStarted = "10-11-11"
developerNumber = 3
teamLeader = "John"
As you see, all of this entries have different types.
For now I see two options:
1) Create GlobalData entity. Each field of it will be represented as unique column in the table and will contain unique setting. This way I have no problems with type casting, but I would like to avoid it in case where there will be big amount of settings.
2) Create Setting entity. Each of it will contain two fields: key(Primary key) and value and will be represented as unique record in the table. This is preferable solution, but It's seems to me that I will get a lot of type casting, because settings can be any type.
So basically, I'm looking for the way to implement second solution without getting a lot of troubles from different types. Can anybody help me?
Thanks.
Edit 1.
Yeah, thanks Christian. Just got similar idea.
What if I will have Settings entity, which will be like:
#Entity
#Table(name = "settings")
public class Setting {
#Column
private String key;
#Column
private String value;
#Column
private String converterClassFullName; //example by.lugovsky.MyConverter
//Getters, setters
}
And GlobalData class.
public class GlobalData {
private Date projectStarted;
private int developerNumber;
private String teamLeader;
Set<Setting> settings;
//Getters and setters for all, except settings.
}
So basically my idea is to convert Setting entity before persisting/updating/ etc. I can do this in my DAO, but I was wondering, if I could annotate GlobalData class with #Entity annotation as well without creating new table. This way I can set OneToMany annotation to Setting's set and Perform conversions in the internal #PrePersist etc. methods.
Will Hibernate allow me to do this?
Thanks again
You could store a Converter-Class into the db and the let it run through the given converter for a property before using the value. JSF offers Converter API:
public interface Converter{
public Object getAsObject(FacesContext fc, UIComponent component, String value) throws ConverterException;
public String getAsString(FacesContext fc, UIComponent component, Object obj) throws ConverterException;
}
If you have a schema with
name: String
value: String
converter: Class
then you could do something like this:
PropertyEntry pe = // Get from OR-Mapper
Converter c = (Converter) pe.getConverter().newInstance();
Object o = c.getAsObject(null, null, pe.getValue());
// use the object o instead of value
For even more coolness you could also define a field in the class which will not be persisted which you could use to hold the converted value within the object.

Need help with binding Set with Spring MVC form

I have been trying for last 3 days still i am not able to solve my problem
I have Person Class
#SuppressWarnings("rawtypes")
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
#JoinColumn(name="person_id")
public Set<Book> books = new HashSet<Book>();
class Book
book_id
person_id
In my JSP form i have
<c:forEach items="${BookList}" var="var1" varStatus="counter">
<input type="checkbox" name="books[${counter.index}].book_id" value="${var1.book_id}" >${var1.book_name}</input>
</c:forEach>
I am inserting the books in table depending upon the check boxes
The book list is populated from refrenceData model.
COntroller
#RequestMapping(value = "/persons/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Create new Person and add to model
// This is the formBackingOBject
model.addAttribute("personAttribute", new Person());
// This will resolve to /WEB-INF/jsp/addpage.jsp
return "hibernate/addpage";
}
#RequestMapping(value = "/persons/add", method = RequestMethod.POST)
public String add(#Valid #ModelAttribute("personAttribute") Person person, BindingResult result) {
logger.debug("Received request to add new person");
if (result.hasErrors())
return "hibernate/addpage";
else
personService.add(person);
// This will resolve to /WEB-INF/jsp/addedpage.jsp
return "hibernate/addedpage";
}
Now if i have single Book object then this works ok and data is entered in DB but if i have set then it says invalid property book[1]
After searching a lot on SO and Google i leart that i have two option
PropertyEditor
AutoPopulatingList
I don't know how to use them in my case. Can anyone help me , where do i have to use them and how to use it
Look at this question Bind objects in a Set collection
You need to use another type of Collection. I'd recommend to use a List instead of a Map. When you send from the form a parameter with a name like:
name="books[0].book_id"
SpringMVC will look in the property called books (which is a Set for you) and then it will try to get the first element by doing books.get(0). Set don't have a get because Set has not an order.
For the implementation of the list you can use AutoPopulatingList. It is an implementation of a lazy List which will create an object if it doesn't exist. For example if you invoke books[0].id and you haven't added a book in the position 0 of the list it will throw a NullPointerException, but if you use AutoPopulatingList it will create a new Book and addd it in that position if that position is empty.
public List<Book> books = new AutoPopulatingList<Book>(new ElementFactory<Book>() {
#Override
public Book createElement(final int index) throws ElementInstantiationException {
//call the constructor as you need
return new Book();
}
});
if you you are going to instanciate it with the default constructor of Book (that is Book()), you can use a syntax like this one:
public List<Book> books = new AutoPopulatingList<Book>(Book.class);
When I have such complicated form i honestly prefer to use JSON and submit it using AJAX.
{"person":{"id":1,"books":[{"person_id":2,"book_id":3},{"person_id":2,"book_id":6},{"person_id":3,"book_id":4}]}
#RequestMapping(value = "/persons/add", method = RequestMethod.POST)
#ResponseBody
public String add(#RequestBody Person person){
//ad your business logic
}
Your code will be validate by de-serializer and you will be able to save it.
You can reed more about that in this post:
http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/
Binding of Set isn't possible with Spring MVC as Sets do not have indexes to work with. Although you can iterate through sets in JSP and show the results.
The solutions might be -
Use another type of collection like List.
Wrap your Set in a POJO, use your Set for showing its containing values in JSP. Once you want to post the form containing your selection, add new property in your POJO which is String(or similar) and provide this property as your PATH in JSP tag, which will get the selection data from JSP. Then in backend code, fill your set with this value.
In case, your POJO is the also an Entity for your database creation using Hibernate, simply put #Transient on top of it. Hibernate will ignore this property while creating table.

Categories

Resources