Micronaut - different validation for different operations - java

I have an JavaDTO like:
public class myDTO {
private String name;
private Integer age;
}
I want to do different validation in Micronaut by an CREATE operation and by an UPDATE operation. In Spring you can define different 'groups' therefore. See here stackoverflow link or here external link. So this looks like:
public class myDTO {
#Null(groups = OnCreate.class)
#NotNull(groups = OnUpdate.class)
private String name;
#Null(groups = OnCreate.class)
#NotNull(groups = OnUpdate.class)
private Integer age;
}
Is there something similiar for micronaut there?

I believe this is not Spring functionality but more how beans are validated from the javax bean validator.
You need to use Hibernate Validator where javax.persistence.validation.group.pre-update are applicable.
Default Micronaut bean validation is not using Hibernate Validator.
Try to add hibernate validator as dependency.
https://micronaut-projects.github.io/micronaut-hibernate-validator/latest/guide/index.html

Related

One way mapping in Dozer using custom converter

Please note: while I would accept an XML-based solution if that's truly the only way to accomplish what I'm looking for, I would greatly prefer a solution using Dozer's Java API.
I am new to Dozer and am trying to figure out how to use its API. It seems to default to field-level mappings (if the field names match) and to allow for custom mappers and converters in the event that field-level mapping (based on field name) is either not possible or not logical for your application needs.
I have a situation where my app will take a DTO, say, ReportedIssue (an issue reported by a user and sent to my application over HTTP), and an Issue entity (a data entity that will be persisted to a MySQL DB).
Here are my two objects:
#Data
public class ReportedIssue {
private String typeRefId;
private String reporterRefId;
private String info;
}
#Entity
#Table(name = "issues")
#Data
public class Issue {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "issue_ref_id")
private String refId;
#Column(name = "issue_tracking_number")
private String trackingNumber;
#OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "issue_type_id", referencedColumnName = "issue_type_id")
private IssueType type;
#Column(name = "issue_reported_on")
private Date reportedOn;
#OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "issue_reporter_id", referencedColumnName = "account_id")
private Account reporter;
#Column(name = "issue_info")
private String info;
}
So in the application frontend, a user can report an issue. The frontend sends a JSON version of a ReportedIssue to the backend, where that JSON is deserialized into a ReportedIssue DTO bean. Then I need Dozer to convert my ReportedIssue into an Issue entity that I can then easily save to my MySQL DB.
Here is my best attempt:
public class ReportedIssueConverter extends DozerConverter<ReportedIssue, Issue> {
private AuthService authService;
public ReportedIssueConverter(AuthService authService, Class<ReportedIssue> prototypeA, Class<Issue> prototypeB) {
super(prototypeA, prototypeB);
this.authService = authService;
}
public ReportedIssueConverter(Class<ReportedIssue> prototypeA, Class<Issue> prototypeB) {
super(prototypeA, prototypeB);
}
#Override
public Issue convertTo(ReportedIssue source, Issue destination) {
Issue issue = new Issue();
issue.setRefId(UUID.randomUUID().toString());
issue.setType(IssueUtils.determineType(source));
issue.setReportedOn(DateTimeUtils.nowInUTC());
issue.setReporter(authService.currentUser());
issue.setInfo(destination.getInfo());
return issue;
}
#Override
public ReportedIssue convertFrom(Issue source, ReportedIssue destination) {
throw new UnsupportedOperationException("we currently don't map from issues to reported issues");
}
}
Several concerns here. For one, is such a custom converter even necessary? Or is there a "better" (more standards compliant or using generally-accepted Dozer practices) way to use the Dozer API to perform this conversion? But mainly, this DozerConverter seems to be intended for bi-directional mapping use cases. Whereas, in my application, I will never have an Issue instance and need to map it back to a ReportedIssue DTO instance. So I only need one-way mapping from ReportedIssue --> Issue. Am I using Dozer correctly by throwing an UnsupportedOperationException or is there another interface or API trick I can use to only leverage the one-way mapping I need?
It could actually be done without a custom converter using custom getter methods in your dto class corresponding to fields in Issue. Dozer works by mapping each field in destination class by trying to invoke the getter method of the corresponding name in the source class.
public class ReportedIssue {
// fields.......
public String getRefId() {
UUID.randomUUID().toString()
}
public IssueType getType() {
IssueUtils.determineType(this);
}
// similarly create getters for other required fields.
}
But for reporter field in Issue, you need an AuthService object. I would suggest writing a static method as below:
public static Issue getIssue(AuthService auth, ReportedIssue dto) {
Issue issue = //map using dozer
issue.setReporter(authService.currentUser());
return issue;
}
Gauntham answer will work. Another option:
Implement a com.github.dozermapper.core.BeanFactory
Your custom BeanFactory can handle
Issue issue = new Issue();
issue.setRefId(UUID.randomUUID().toString());
issue.setReportedOn(DateTimeUtils.nowInUTC());
issue.setReporter(authService.currentUser());
Then depending on your preferences, this could also go into the bean factory
issue.setType(IssueUtils.determineType(source));
Or you could handle that separately in the mapping. Something would need to know how to call IssueUtils, so that is either 1) a customer converter or 2) a change to the DTO or entity to have the functionality through a getter or setter.
Finally, this line would be handled in the Dozer Java API mapping
issue.setInfo(destination.getInfo());
Personally, I like Dozer's com.github.dozermapper.core.loader.api.BeanMappingBuilder where you can explicitly tell it how to map 2 beans, specify the bean factory to use and the custom converter for a specific field.
mapping(ReportedIssue.class, Issue.class, oneWay(), wildcard(true), beanFactory(IssueBeanFactory.class.getName()).fields("this", "type", customConverter(IssueTypeConverter.class)
oneWay(), wildcard(boolean), and beanFactory(String) are found in Dozer's TypeMappingOptions and customConverter(Class.class) is found in Dozer's FieldMappingOptions.
oneWay() makes the mapping work only in the direction specified in the BeanMappingBuilder.
wildcard(true) tells Dozer to automatically map matching fields (this is default behavior).

How to call service based classes from entity class in spring boot

This is an entity class of my Multi-tenancy project
#Entity
public class Customer {
private String uniqueCustomerId;
private String firstName;
private String lastName;
#Convert(converter = CryptoConverter.class)
private String mobile;
private String mobileShavalue;
}
In my CryptoConverter.java I am checking whether I have to encrypt this attribute or not, from the configuration and encryption logic written in CryptoConverter.java. Also, if the configuration tells that i have to encrypt the mobile attribute then in that case i have to also store the sha value(sha1 or sha2 from config) for attribute mobileShavalue.
One thing came in my mind to achieve this, is through using #PrePersist in my entity class and again getting configuration from PrePersist method to validate whether I have to store mobile sha value or not and writing shavalue logic there.
But when I do this I am getting below error
#Autowired
EncryptionConfigService encryptionConfigService;
#PrePersist
private void doSomeCode(){
encryptionConfigService.callNewMethod();
}
Caused by: org.hibernate.MappingException: Could not determine type for: com.loylty.tms.service.EncryptionConfigService, at table: Customer, for columns: [org.hibernate.mapping.Column(encryption_config_service)]
Annotate the autowired service with #Transient to make the ORM ignore it during serialization
#Transient
#Autowired
EncryptionConfigService encryptionConfigService;
#PrePersist
private void doSomeCode(){
encryptionConfigService.callNewMethod();
}

How to exclude validation properties only on the mvc layer (and keep for database layer)?

I want to use the same bean for both database persistance, and for a webservice json request.
Problem: I have a property that should never be set by the webservice user, but filled by the application before persistance. The field in the db is marked as not null, so the domain validation is: #NotNull.
But if I add this, then spring-mvc will also validate the incoming json request and throw an exception if id is not set (but which is desired).
Question: how can I exclude certain validation properties only on the mvc layer?
#Entity
public class Person {
#Id
#NotNull
#JsonIgnore //has no effect
private String id; //to be set by application before persistance
private String name;
private int age;
}
#RestController
public class PersonController {
#PostMapping
public void post(Person p) {
}
}
The requirement you've described is to be able to define different constraints for the same bean depending on the respective context. In the MVC layer you want id to be nullable and persistence layer you want to it to be not-null.
You can implement this requirement by using Validation Groups.
Your first introduce a new interface for the persistence validation case lets say:
public interface PersistenceGroup {
}
Then you change your bean to:
#Entity
public class Person {
#Id
#NotNull(groups = PersistenceGroup.class)
private String id; //to be set by application before persistance
private String name;
private int age;
}
#RestController
public class PersonController {
#PostMapping
public void post(Person p) {
}
}
This has the effect that #NotNull is only validated for the given group.Because by default the javax.validation.groups.Default group is used for validation, your MVC will not check for #NotNull. Now we tell Hibernate to use your group when its BeanValidationEventListener is activated in case of persisting, updating or deleting your entity. This can be done by setting the corresponding of the following properties (see docs):
javax.persistence.validation.group.pre-persist
javax.persistence.validation.group.pre-update
javax.persistence.validation.group.pre-remove
Example for your use case:
<hibernate-configuration>
<session-factory>
...
<property name="javax.persistence.validation.group.pre-persist">
javax.validation.groups.Default,your.package.PersistenceGroup
</property>
<property name="javax.persistence.validation.group.pre-update">
javax.validation.groups.Default,your.package.PersistenceGroup
</property>
...

Using #JsonView with Spring Hateoas

If I use #JsonView on a POJO and on a controller in Spring, my HATEOAS links are hidden. I can understand why this happens, because the _links property isn't annotated with the correct view class, but it's not the behaviour I need in this case. Is there a way to always include the _links property, regardless of using a view class or not?
My POJO looks something like:
#Entity(name = "groups")
#SequenceGenerator(name = "groups_groupid_seq", sequenceName = "groups_groupid_seq")
public class Group extends ResourceSupport {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "groups_groupid_seq")
#JsonView(Views.Summary.class)
private long groupID;
#JsonView(Views.Full.class)
private String name;
/*
Getters & setters ...
*/
}
And my controller looks something like:
#RestController
#RequestMapping(path = "/group")
#ExposesResourceFor(Group.class)
public class GroupApiController {
#JsonView(Views.Summary.class)
#RequestMapping(path = "/", method = RequestMethod.GET)
public Iterable getPermittedGroups(
Authentication authentication) {
// load groups...
}
}
My HATEOAS configuration:
#Configuration
#EnableAspectJAutoProxy
#EnableEntityLinks
#EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class HateoasConfig {
//
}
I don't have to use #JsonView, so I'm happy to use an alternative. But I do need to be able to return different views on the same class, from different controller methods, otherwise I could use #JsonIgnore.
Is there a way to always include the _links property, regardless of
using a view class or not?
Using Spring Boot
Set the spring jackson mapper configuration
spring.jackson.mapper.default-view-inclusion=true
Within your application.properties
Using Spring #Configuration Classes
This topic is well covered in "Configuring Object Mapper in Spring"
However, there are currently issues in particular with HATEOAS that prevent this from working. In particular is the open ticket "Support for HATEOAS-Links in Json-Views"

Will adding an annotation break Java Serialization?

I am in the process of rewriting a very old java app to Spring Boot and Hibernate 5. Part of this task requires that I replace our XML configuration (both Spring and Hibernate) with annotations.
I have the following question. Let's assume that the application contains a class as such:
public class MyObject implements Serializable {
private static final long serialVersionUID = 81848571841847187L;
private String id;
private String name;
//getters and setters...
}
This class Serialized across a network, and is included in a "common" jar, which classers must include, in order to deserialize on their end.
Let's assume that I add a few Hibernate and JPA annotations to the class
#Table(...)
#Entity
public class MyObject implements Serializable {
private static final long serialVersionUID = 81848571841847187L;
#Id
#Column(...)
private String id;
#Column(...)
private String name;
//getters and setters...
}
My question is: if the caller (who deserializes the above Object) does not have those annotations in his classpath, will serialization fail?
Only Annotations with RETENTION=RUNTIME used in byte code, but Serialization works with object fields, not with classes.
but its important to understand that Annotations can be used by custom serializer.
for example this is how #Transient exclusion is implemented.
so the next thing is to check what type of Serialization mechanism is used.
elad

Categories