Hibernate saves empty Spring autobound ojects - spring

I'm using Spring + JSF2 + Hibernate to build a web application. Two relevant classes look like this:
#Entity
#Table(name="people")
#Named
public class Person implements Serializable {
private int id;
private String forename;
#Inject private PhoneNumber mobile_phone;
/* Other properties */
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="mobile_phone_number_id", nullable = true)
public PhoneNumber getMobilePhone() {
return mobile_phone;
}
/* Other getters and setters */
}
#Entity
#Table(name="phone_numbers")
#Named
public class PhoneNumber {
private Integer id;
private String code;
private String number;
/* Other stuff */
}
Dependency injection is handled by Spring (#Named, #Inject). Database persistence is configured through Hibernate annotations (#OneToOne, #JoinColumn). JSF form elements are bound to these classes using EL.
My question is how to tell hibernate no to save autobound PhoneNumber if all its fields are left blank (the user doesn't submit any values)? In other words, I want the Person object to be saved but don't want records with empty strings appear in the phone_numbers table.
And two bonus questions if someone knows.
When does Spring bind the PhoneNumber - at Person creation or when a request for a PhoneNumber comes from JSF?
[SOLVED] I have fields in my form which aren't marked with the required attribute in JSF. Obviously, if such fields are left empty, validation shouldn't be triggered. The problem is that when I configured Hibernate validator, such non-required fields are now validated even when left blank. Is there any way to skip validation for empty fields which aren't marked as required in JSF? I tried to set <prop key="javax.persistence.validation.mode">CALLBACK</prop> in hibernateProperties of SessionFactory bean, but this didn't help.

The second bonus question has the following answer: JSF treats empty fields as empty strings, so one must set
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
in web.xml to interpret empty strings as null. Obviously, an empty string didn't validate well against my regex validation pattern.

Related

Spring MVC: how to avoid duplicating validation at controller and entity

Let's say I have a "Person" #Entity managed via JPA, which has a series of validations applied at entity-level (#NotBlank, #NotNull etc).
#Entity
public class Person {
#NotBlank
private String name;
#Email
private String email;
...
}
For various reasons, we shouldn't directly use an entity as the controller method argument, but rather create a custom "form" (taking the example from https://spring.io/guides/gs/validating-form-input/)
#PostMapping("/person/save")
public String savePerson(#Valid PersonForm personForm, BindingResult bindingResult) {
// map fields to Person entity individually and save
...
}
But now, to make use of Spring's built-in form validation / BindingResult, it appears I have to duplicate all my validation logic on both the Person and PersonForm classes. I don't just want to define them on PersonForm, because there might be other routes in the application to update a Person.
Ideally there would be some way that Spring could lift up the validation constraints on the #Entity and apply them to the form (e.g. if the properties had the same name).
Am I missing something here with validation?

Throw error when properties marked with #JsonIgnore are passed

I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.

Ignoring spring mvc JSR-303 validations for selective entity fields

I have spring4 mvc application to save an Address entity, code bit as follows.
My Controller
#RequestMapping(value = "addAddress", method = POST)
public String registerComplaint(#Valid #ModelAttribute final Address address, final BindingResult resultBinder) {
if (resultBinder.hasErrors())
return "addAddress";
addressService.addAddress(address);
return "redirect:myAddress";
}
My Entity
#Entity
#Table(name = "address")
public class Address {
#NotNull
private String street;
#NotNull
private String pin;
#NotNull
private String createdBy;
........
}
My form conatins only street and pin as field, where as createdBy should be set by me after validating the other form values.
Here the problem is spring JSR303 validation support is validating a field ie createdBy which i don't want to validate by spring mvc.
How can i instruct spring mvc not to validate these kind of optional fields while using #Valid annotation.
Is there any way i can skip fields like this using spring mvc ?
Validation is mainly for user input. Since you will be setting createdBy yourself, just do so before saving it (e.g #PrePersist), or have a new Date as a default value. If you need to enforce a constraint for createBy, you can do so at the schema level.
#Column(nullable=false, ...)
private String createdBy = new Date();
You need to read up on Validation Groups. This lets you use different validators depending on the "scenario"
Use Spring's #Validated annotation to use groups
If you don't protect the createdBy field, a user can change it by altering the POST variables. See DataBinder.setDisallowedFields()
Conceptually, how is a pin related to an address?
It sounds like you want to use a Form Backing Object here (a regular non-JPA POJO made just for a form), and copy values to your real entities.

Entity Objects vs Value Objects - Hibernate and Spring

Okay. I am getting a little confused here...
Lets say I have a class called User.
class User {
// all variables
// all getters and setters
}
Now, I use JSR 303 validation and put #NotNull, #Range, etc here for the variables.
I use this as Form / Command object. Meaning, when a form a submitted, the values are validated and BindingResult gives me errors.
Should this be used as in Entity Object for Hibernate as well? (If so, I need to add other Hibernate annotations like #Entity, #Id, #Column, etc on top of Validation annotations)
When we load the data from Database, do these validations kick in as well? (If yes, what if the data is already existing, and do not confirm to the validations?)
Where do we normally write business validations, like for example, country exists or not in the database, xyz value exists in a different table, etc?
Questions arise here as well:
- User form may not have all the fields that exist in the User class
- Database table User may have more fields or less fields than User class
- Form may have fields from different objects as well, say User and and Order.
How do we handle these?
Trying to wrap my mind around it
No you shouldn't mix entities objects and values objects. Entities objects are for DB mapping and values objects are used in the presentation layer.
To validate an object annoted, you need to use a Validator (commonly used with a DataBinder. Spring validation)
Is it DB constraints or backend validation?
For your last question, that's one of the reason to have 2 differentes objects for your presentation layer and your persistence layer. This way values objects can match what is displayed or input by the user (Form).
Entity object is an object of our plain old java class(POJO) Model/Business class, which needs to be persisted in a database using Hibernate, while Value Type object is an object of another class but it is stored as a part of Entity object within a database table.
#Embeddable
#Data
#AllArgsConstructor
public class Address{
private String country;
private String city;
private String street1;
private String street2;
private postalCode;
}
#Entity
#Data
#AllArgsConstructor
public class Person{
#Id
private Long id;
private String firstName;
private String lastName;
private ing age;
private Address address;
}
}
So after running this code in spring and hibernate project you will see Person table in database is created with it's attributes and Address class attributes.
For more information, I suggest reading this:
[https://www.decodejava.com/hibernate-value-type-object.htm][1]

Using converters in a list with Spring Roo

Now that I found how to use converters in an HTML SELECT in Spring Roo, I am trying to do the same in a list.
I managed to register a Converter in my ApplicationConversionServiceFactoryBean, but now I need to use it as well when displaying a list of my envities. I have the following entity :
#RooJavaBean
#RooToString
#RooEntity
public class Environment {
#NotNull
#Size(min = 2, max = 30)
private String name;
#ManyToOne
private Application application;
}
When displaying it as a list in the generated MVC, it looks like the application is displayed as a toString() and not using the registered converter.
What am I missing ?
You need to push-in refactor the Roo generated converter method to the application conversion factory bean.
Sometimes, by default toString() method is used for the conversion.
Alternatively, you can try pushing in and overriding the toString() method within the entity itself. You will have to remove the #RooToString annotation while doing this.
Cheers!!!

Resources