Spring, JSR 303 - Validation groups on nested fields - spring

Here is the example, better than words :
public class Person{
private String name;
// 1 ) #Valid(FirstAdress.class) -> The attribute value is undefined for the annotation type Valid
// 2 ) #Validated(FirstAdress.class) -> The annotation #Validated is disallowed for this location
#Valid
private Adress firstAdress;
// 1 ) #Valid(SecondAdress.class)
// 2 ) #Validated(SecondAdress.class)
#Valid
private Adress secondAdress;
}
And
public class Adress {
#Size(min = 5, groups = { FirstAdress.class })
#Size(min = 10, groups = { SecondAdress.class })
private String city;
public interface FirstAdress{
}
public interface SecondAdress{
}
}
So, for the object firstAdress, the field city must be at least 5 characters, and for the secondAdress, the city must be at least of 10 characters (just an example).
The solution 1) and 2) in the code are not working/available yet, and I would like to know if what I want to do is possible, or if there is an other way to do it.
Thanks a lot !

I tried :
#Valid
#ConvertGroup(to = FirstAdress.class)
private Adress firstAdress;
#Valid
#ConvertGroup(to = SecondAdress.class)
private Adress secondAdress;
And for now it seems working ! I will update this post if finally something went wrong with this solution :)

Related

How to sum multiple fields of a list of objects using stream in java

I'm trying to get the sums of two fields from a Model class. and return it using using a pojo but kept getting syntax errors. What I am trying to achieve is similar to the highest voted answer in This: Summing multiple different fields in a list of objects using the streams api? but I got syntax error. Here is my model:
public class BranchAccount {
#NotNull(message = "Account balance is required")
private Double accountBalance;
#NotNull(message = "Profit is required")
private Double profit;
#Temporal(TemporalType.TIMESTAMP)
private Date dateCreated;
#Temporal(TemporalType.TIMESTAMP)
private Date lastUpdated;
}
My Pojo:
public class ProfitBalanceDto {
private Double accountBalance;
private Double profit;
}
My code to get sum of accountBalance and profit from BranchAccount:
public ProfitBalanceDto getAllBranchAccount() {
List<BranchAccount> branchAccounts = branchAccountRepository.findAll();
branchAccounts.stream()
.reduce(new ProfitBalanceDto(0.0, 0.0), (branchAccount1, branchAccount2) -> {
return new ProfitBalanceDto(
branchAccount1.getAccountBalance() + branchAccount2.getAccountBalance(),
branchAccount1.getProfit() + branchAccount2.getProfit());
});
return null;
}
My errors:
Please am I doing wrong?
PS: I want to use stream for this.
As #Holger mentioned in his comment, you can map to ProfitBalanceDto before reducing
public ProfitBalanceDto getAllBranchAccount2() {
List<BranchAccount> branchAccounts = branchAccountRepository.findAll();
return branchAccounts.stream()
.map(acc -> new ProfitBalanceDto(acc.getAccountBalance(), acc.getProfit()))
.reduce(new ProfitBalanceDto(0.0, 0.0),
(prof1, prof2) -> new ProfitBalanceDto(prof1.getAccountBalance()+ prof2.getAccountBalance(),
prof1.getProfit() + prof2.getProfit()));
}
If you are using Java 12 or higher using the teeing collector might be a better option
public ProfitBalanceDto getAllBranchAccount() {
List<BranchAccount> branchAccounts = branchAccountRepository.findAll();
return branchAccounts.stream()
.collect(Collectors.teeing(
Collectors.summingDouble(BranchAccount::getAccountBalance),
Collectors.summingDouble(BranchAccount::getProfit),
ProfitBalanceDto::new));
}

Limit the number of the same request param in spring

I have the GET request like
/api/v1/data?name=aaa&name=bbb&name=cccc&name=dd&name....
I want to limit the number of 'name' param, it must be no more than 100 (configurable in properties file)
Here is my controller
public Data getDataByNames(#RequestParam(value = "name") List<String> names) {
return userService.getDataByNames(names);
}
How can I do that? Thanks
Update: yeah, I can check in service layer:
if(names.size() < 100) {...} but it seem not professional
If you don't need to max number of elements to be configurable, you can use annotation #Size.
Step 1. Define a wrapper for your request param
public class NameWrapper {
#Size(max = 100)
#NotEmpty
private List<String> name;
//getters and setters
}
Step 2. Add the wrapper as a parameter to your controller method
public Data getDataByNames(#Valid NameWrapper nameWrapper) {
If you want to be from configurable from application properties, you should define your own custom argument resolver.

Sorting on #Transient column in Spring Data Rest via PagingAndSortingRepository

Our application uses PagingAndSortingRepository to serve our REST API. This works great, but we ran into a specific edge case that we can't seem to solve:
We have a alphanumeric field that has to be sortable (e.g. SOMETHING-123). One possible solution was to use something like a regex inside the database query's order by. This was ruled out, as we wanted to stay database independant. Thus we split up the column into two columns.
So before we had an Entity with 1 String field:
#Entity
public class TestingEntity {
#Id
#GeneratedValue
private long id;
private String alphanumeric
}
And now we have an Entity with 2 additional fields and the old field made #Transient which is filled at #PostLoad:
#Entity
public class Testing {
#Id
#GeneratedValue
private long id;
#Transient
public String alphanumeric;
#PostLoad
public void postLoad(){
this.alphanumeric = this.alphabetic + "-" + this.numeric;
}
public void setAlphanumeric(String alphanumeric) {
int index = alphanumeric.indexOf("-");
this.alphabetic = alphanumeric.substring(0, index);
this.numeric = Long.parseLong(alphanumeric.substring(index + 1));
}
#JsonIgnore
private String alphabetic;
#JsonIgnore
private Long numeric;
}
This is working great and the additional fields do not get exposed. However the sorting on the field "alphanumeric" does obviously not work anymore. The simplest solution would be to make this request:
localhost:8080/api/testingEntity?sort=alphanumeric,asc
and internally rewrite it to the working request:
localhost:8080/api/testingEntity?sort=alphabetic,asc&sort=numeric,asc
What is the best way to tackle this issue?

Doing JSR-303 validation in logical order

I have such field in my domain model class validation constraints:
#Column(nullable = false, name = "name")
#NotEmpty(groups = {Envelope.Insert.class, Envelope.Update.class})
#Size(min = 3, max = 32)
private String name;
When this field is empty ("") or null, validator produces both "cannot be empty" and "size must be between..." error messages. I understand it, but when I show this validation error to the client, it seems quite odd (because when something is null / empty it cannot fulfill size requirement, it's not a logical).
Is there some way how to tell Spring to do validation in proper order? If is not #NotEmpty then do not check #Size, and when #NotEmpty is fulfilled check #Size.
According to Hibernate official document:
By default, constraints are evaluated in no particular order and this
regardless of which groups they belong to. In some situations,
however, it is useful to control the order of the constraints
evaluation. In order to implement such an order one would define a new
interface and annotate it with #GroupSequence defining the order in
which the groups have to be validated.
At first, create two interface FirstOrder.class and SecondOrder.class and then define a group sequence inside OrderedChecks.java using #GroupSequence annotation.
public interface FirstOrder {
}
public interface SecondOrder {
}
#GroupSequence({FirstOrder.class, SecondOrder.class})
public interface OrderedChecks {
}
Finally, add groups in your bean constraints annotations.
#Column(nullable = false, name = "name")
#NotEmpty(groups = {FirstOrder.class, Envelope.Insert.class, Envelope.Update.class})
#Size(min = 3, max = 32, groups=SecondOrder.class)
private String name;
The following example is taken from the JSR-303 docs
public class Address {
#NotEmpty(groups = Minimal.class)
#Size(max=50, groups=FirstStep.class)
private String street1;
#NotEmpty(groups=SecondStep.class)
private String city;
#NotEmpty(groups = {Minimal.class, SecondStep.class})
private String zipCode;
...
public interface FirstStep {}
public interface SecondStep {}
#GroupSequence({Firststep.class, SecondStep.class})
public interface Total {}
}
and calling the validator like this
validator.validate(address, Minimal.class, Total.class);

Hibernate tuple criteria queries

I am trying to create a query using hibernate following the example given in section 9.2 of chapter 9
The difference with my attempt is I am using spring MVC 3.0. Here is my Address class along with the method i created.
#RooJavaBean
#RooToString
#RooEntity
#RooJson
public class Address {
#NotNull
#Size(min = 1)
private String street1;
#Size(max = 100)
private String street2;
private String postalcode;
private String zipcode;
#NotNull
#ManyToOne
private City city;
#NotNull
#ManyToOne
private Country country;
#ManyToOne
private AddressType addressType;
#Transient
public static List<Tuple> jqgridAddresses(Long pID){
CriteriaBuilder builder = Address.entityManager().getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
Root<Address> addressRoot = criteria.from( Address.class );
criteria.multiselect(addressRoot.get("id"), addressRoot.get("street1"), addressRoot.get("street2"));
criteria.where(builder.equal(addressRoot.<Set<Long>>get("id"), pID));
return Address.entityManager().createQuery( criteria ).getResultList();
}
}
The method called jqgridAddresses above is the focus. I opted not to use the "Path" because when I say something like Path idPath = addressRoot.get( Address_.id ); as in section 9.2 of the documentation, the PathAddress_.id stuff produces a compilation error.
The method above returns an empty list of type Tuple as its size is zero even when it should contain something. This suggests that the query failed. Can someone please advise me.
OK so i made some minor adjustments to my logic which is specific to my project, however, the following approach worked perfectly. Hope it hepls someone in need !
#Transient
public static List<Tuple> jqgridPersons(Boolean isStudent, String column, String orderType, int limitStart, int limitAmount){
CriteriaBuilder builder = Person.entityManager().getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
Root<Person> personRoot = criteria.from(Person.class );
criteria.select(builder.tuple(personRoot.get("id"), personRoot.get("firstName"), personRoot.get("lastName"), personRoot.get("dateOfBirth"), personRoot.get("gender"), personRoot.get("maritalStatus")));
criteria.where(builder.equal( personRoot.get("isStudent"), true));
if(orderType.equals("desc")){
criteria.orderBy(builder.desc(personRoot.get(column)));
}else{
criteria.orderBy(builder.asc(personRoot.get(column)));
}
return Address.entityManager().createQuery( criteria ).setFirstResult(limitStart).setMaxResults(limitAmount).getResultList();
}

Resources