Validation for required boolean attributes doesn't work - java

I'm using OpenAPI 2.0 contract for my Rest service, written in Yaml, and I'm using swagger-codegen to generate the POJOs. I activated the useBeanValidation and performBeanValidation, so that the generated code (in JAVA) include the validation annotations...
In my contract I have a field of type boolean that I marked as required:
cancellationInd:
description: "indicates if this is a cancellation"
type: boolean
required: true
and the generated code is as follow:
/**
* indicates if this is a cancellation.
* #return cancellationInd
**/
#NotNull
#ApiModelProperty(required = true, value = "indicates if this is a cancellation")
public Boolean isCancellationInd() {
return cancellationInd;
}
public void setCancellationInd(Boolean cancellationInd) {
this.cancellationInd= cancellationInd;
}
The problem is that in my JsonREquest to the serviec, when I used
cancellationInd: null, it passes.... I was expecting some HttpStatus_400
because of the required field. Same thing when field is emtpy instead of null...
{
...
"timstamp": "2019-04-29T13:23:56.123-04:00",
"cancellationInd": "false",
...
}
It looks like the reason is that when the attribute is of type boolean, the generated one in java is Boolean (which is a wrapper, not primitive, and thus accepts a null)... am I right? Is this is a bug?
Any suggestions please?

Related

Hibernate Paramater Value did not match expected type with Enum

I'm using hibernate criteria to create a basic select equal expression on an Enumeration field.
The class
#Column
#Enumerated(EnumType.STRING)
private StateEnum state;
The expression
Object value = StateEnum.PENDING;
this.criteriaBuilder.equal(this.root.get("state"), value);
I'm using value as Object because in real code I have a String and need to convert this String to the field instance with reflection.
After some debug I checked that:
Hibernate QueryParameterBindingValidator has some validation, and the important here is:
else if (expectedType.isInstance(value)) {
In my case, this expression is returning false, but checking with IntelliJ tools expectedType is StateEnum:
And value is StateEnum also:
But the isInstance comparation return false
With one base test:
public class Test {
public static void main(String[] args) {
Class clazz = StateEnum.class;
Object value = StateEnum.PENDING;
if(clazz.isInstance(value)) System.out.println("isInstance");
else System.out.println("not isInstance");
}
}
Return "isInstance" because validation is true.
If I force the method return to true on hibernate validation class than hibernate return resultSet like a charm, anyone has any idea why this is happening?
Some version informations:
Hibernate-core: 5.4.15
Spring-boot: 2.3.0.RELEASE
spring-boot-starter-data-jpa: 2.3.0.RELEASE
This error means the types are different.
Unfortunately you didn't post a screenshot of debug output from value.class on
else if (expectedType.isInstance(value)) {
line
Are those enums perhaps of the same name, but from different packages?
Note, for other people with a this kind of exception - most common way to get this kind of error is defining query method with wrong type of parameter, for example a String.

Map null values to default using builder with MapStruct

I want to map field from Source to Target class, and if the source value is null, I would like to convert it to default value based on the data type ("" for strings, 0 for numeric types etc.). For setting the values, I am not using regular setters, but builder (with protobuf, so the names of the methods is newBuilder() and build()).
class Source {
private final String value; // getter
}
class Target {
private final String value;
public static Builder newBuilder() {return new Builder()}
public static class Builder {
public static setValue() {/*Set the field*/}
public static Target build() {/*Return the constructed instance*/}
}
My mapper looks like this:
#Mapper(
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT
)
public interface TargetMapper {
Target map(Source source);
}
The generated mapper implementation with this code calls target.setValue(source.getValue()), instead of performing the null check and setting default value if source returns null. The interesting part is when I add the following annotation to the map method, the null check is present in the implementation.
#Mapping(source="value", target="value", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
Is this a bug in MapStruct with builders, or am I missing some configuration to be ably to set the null mapping as a default policy, instead of duplicating it on all field mappings?
EDIT: For some reason, adding nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS to the class level #Mapper annotation adds the null check, but does not explicitly set the value, just skips the call to setValue. For protobuf, this is okay, since this functionality is in the library, but for other implementations the field would remain null.
#Mapping(source="value", target="value", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
applies to update method (so methods that have the #MappingTarget annotated parameter
There's no real counterpart for regular methods:
1. NullValueMappingStragegy applies to the bean argument itself.
2. NullValueCheckStragegy does perform a check on bean properties, but does not return a default.
Naming is not really brilliant and it has a long history. We still have the intention to align this one day.
A solution would be to use an Object factory creating the builder target object and pre-populate it with default values and then let MapStuct override these one day.
Perhaps you could do something like this:
#Mapper(
// to perform a null check
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS
)
public interface TargetMapper {
Target map(Source source);
}
// to create a pre-defined object (defaults set a-priori). Not sure
// whether this works with builders.. just try
#ObjectFactory
default Target.Builder create() {
Target.Builder builder = Target.newBuilder();
builder.setValueX( "someDefaultValue" );
return builder;
}

How to handle optional keys in JSON response with Java

"app":{
"icon":{
"icon":"TOP_RATED"
},
"message":{
"_type":"TextSpan",
"text":"Top Rated"
}
}
I keep seeing the following code in one of the projects that I have inherited. The JSON response above is parsed as follows
// itemObject has the entire json response
// appObject is a POJO with icon, type fields
String icon= JsonPath.with(itemObject).getAsString("icon/icon");
appObject.setIcon(icon);
String type = "";
try {
type = JsonPath.with(itemObject).getAsString("message/_type");
catch(IllegalArgumentException e) {
// do nothing if type is not found in response
} finally {
// set type to empty string if it's not found
appObject.setType(type);
}
In the scenario, when _type doesn't exist for a specific app, would it be best to surround it with a try/catch block as shown above? It just seems wrong to use try/catch/finally block to process business logic instead of error handling. What is a better way to do the same and can Java 8 Optional help with this?
I find the org.json package simple and straightforward. It is found here. The org.json.JSONObject class, for example, contains the public boolean has(String key) method, which is used to check if a certain key exists.
Returns true if this object has a mapping for name. The mapping may be NULL.
You can check this way where 'HAS' - Returns true if this object has a mapping for name. The mapping may be NULL.
if (json.has("status")) {
String status = json.getString("status"));
}
if (json.has("club")) {
String club = json.getString("club"));
}
You can also check using 'isNull' - Returns true if this object has no
mapping for name or if it has a mapping whose value is NULL.
if (!json.isNull("club"))
String club = json.getString("club"));
http://developer.android.com/reference/org/json/JSONObject.html#has(java.lang.String)

Custom sequence generator for Hibernate 5

I want to create a custom sequence generator in Hibernate 5 to create a sequence per table in Postgresql. Using Hibernate 4 I did the following in my dialect:
/**
* Get the native identifier generator class.
*
* #return TableNameSequenceGenerator.
*/
#Override
Class<?> getNativeIdentifierGeneratorClass() {
TableNameSequenceGenerator
}
/**
* Creates a sequence per table instead of the default behavior of one sequence.
* From <a href='http://www.hibernate.org/296.html'>http://www.hibernate.org/296.html</a>
*/
static class TableNameSequenceGenerator extends SequenceGenerator {
/**
* {#inheritDoc}
* If the parameters do not contain a {#link SequenceGenerator#SEQUENCE} name, we
* assign one based on the table name.
*/
#Override
void configure(final Type type, final Properties params, final Dialect dialect) {
Boolean sequencePerTable = Holders.config.getProperty(SEQUENCE_PER_TABLE, Boolean, true)
if (sequencePerTable) {
if (!params.getProperty(SEQUENCE)) {
String tableName = params.getProperty(TABLE)
String schemaName = params.getProperty('schemaName')
if (schemaName) {
params.setProperty(SCHEMA, schemaName)
}
if (tableName) {
params.setProperty(SEQUENCE, "seq_${tableName}")
}
}
}
super.configure(type, params, dialect)
}
}
You can see the full code here: https://github.com/kaleidos/grails-postgresql-extensions/blob/master/src/main/groovy/net/kaleidos/hibernate/PostgresqlExtensionsDialect.groovy#L53
I'm trying to migrate to Hibernate 5 but I don't know how to configure the previous behavior. I've modified the code to extends from SequenceStyleGenerator because now SequenceGenerator has been deprecated but my code is never executed. I think this has to do with the fact that the method getNativeIdentifierGeneratorClass has also been deprecated.
I've been looking for a way to create a custom sequence but all examples I've found are focused on annotate my domain class with the sequence generator. What I'm looking for is a way to define all the sequences in a global way.
As Graeme noted (https://github.com/grails/grails-core/issues/10234) the default name of the sequence has changed in Hibernate 5, so adding
id generator: 'sequence', params: [sequence_name: 'book_seq']
to the mapping block does the trick. The problem is that it's necessary to add that to every domain class.
I'm still looking for a way to define that setting globally, maybe setting prefer_sequence_per_entity to true for every entity.
UPDATE: Finally we found a workaround to define a sequence per table globally. Just add the following to the file application.groovy:
grails.gorm.default.mapping = {
id generator: 'org.hibernate.id.enhanced.SequenceStyleGenerator', params: [prefer_sequence_per_entity: true]
}
Thanks a lot Ivan! I tried only this configuration, which worked too.
grails.gorm.default.mapping = {
id params: [prefer_sequence_per_entity: true]
}

Interpolate validation-specific parameters in bean validation message

I have a custom bean validator which checks if a given field on an entity is unique for some conditions. If the validation fails, the message should include a field (e.g. the ID) of the already existing entity. So for example the message should be:
"Product 42 already has such a value defined, choose a unique value."
Is this possible using bean validation?
AFAICS, the message format may include parameters, such as:
"Length must be between {min} and {max}."
But this can only reference the "static" attributes of the validation annotation, in this case:
#Size(min=1, max=16)
private String name;
In my case, the value is only known within isValid of my custom validator.
You are right!, And for what you want!, you can build constraint violation message inside the isValid() method. For this the constraints Annotation should be specific for particular class on which it has been applied and it should be a class level validation constraints. Inside isValid before returning false on failure of validation you can create message consisting value of class instance. For example:
#check class Test{ int id; #validations...on fields}.
public boolean isValid(Test value, ConstraintValidatorContext context)
{
// your check logic
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("It should be different for(custom message) .."+ value.id).addConstraintViolation();
return false; // based on constraint filure.
}
But i think you want to do this with Field level annotations! I don't have idea about that looking forward to your results.
It's not really the nicest solution, but what we ended up doing was adding something like the following to our top-level exception handling code:
String getConstraintViolationMessages(ConstraintViolationException e) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<?> violation : e.getConstraintViolations()) {
sb.append(getMessage(violation));
sb.append("\n");
}
sb.setLength(sb.length() - 1);
return sb.toString();
}
String getMessage(ConstraintViolation<?> violation) {
String key = violation.getMessageTemplate();
String messageFormat = localize(key);
Object entity = violation.getRootBean();
String identifier;
if (entity instanceof PrimaryKeyed) {
identifier = String.valueOf(((PrimaryKeyed) entity).getId());
} else {
identifier = entity.toString();
}
return MessageFormat.format(messageFormat, identifier);
}
Note that PrimaryKeyed is a custom interface that is used on our entities. We also have some other interfaces and custom handling not shown above.

Categories

Resources