Spring MVC validation: minimum one field required. At least one field required - java

I do a search by criteria with an DTO entity for filter in the front-end of my application:
public class MyFilter implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
#Enumerated(EnumType.STRING)
private AccessType accessType;
private List<MyType> userType;
private List<OfficeLocation> officeLocation;
private List<Language> languages;
private String country;
}
and getters and setters.
In my controller:
#RequestMapping
public ModelAndView list(#ModelAttribute("filter") MyFilter myFilter, BindingResult result) {
final ModelAndView mav = new ModelAndView("list");
// validate
MyFilterValidator.validate(myFilter, result);
mav.addObject("filter", myFilter);
if (result.hasErrors()) {
return mav;
}
// ...
return mav;
}
I want to validate the search form filter by a validator class:
public class MyFilterValidator implements org.springframework.validation.Validator {
#Override
public void validate(Object object, Errors errors) {
final MyFilter myFilter = (MyFilter) object;
if (myFilter == null) {
errors.reject("error.one.field.required");
} else {
if (StringUtils.isEmpty(myFilter.getName()) && myFilter.getAccessType() == null
&& myFilter.getUserType() == null && myFilter.getLanguages() == null
&& StringUtils.isEmpty(myFilter.getCountry())
&& myFilter.getOfficeLocation() == null) {
errors.reject("error.one.field.required");
}
}
}
#Override
public boolean supports(Class inClass) {
return MyFilter.class.equals(inClass);
}
}
I need to validate if one field is filled, minimum one field of my Filter class is filled. How can I do that in a simple way?
I need to check each attribute : StringUtils.isEmpty or .size()<=0, ... ?
Is it possible to iterate over each property and check if one of them is not null?
To know if one field is fill?

If you need this test very often, then it would be worth to implement a small function that inspect some annotated fields of the DAO by reflection.
public DAO {
public String nevermind;
#AtLeastOneOfThem
public String a;
#AtLeastOneOfThem
public String b;
}
/**
* Return True if at least on of the fields annotated by #AtLeastOneOfThem
* is not Empty.
* THIS IS PSEUDO CODE!
*/
public static boolean atLeastOneOfThemIsNotEmpty(Object o) {
for(Field field : getFieldsAnnotatedWith(AtLeastOneOfThem.class, o) {
if (field.get() != null && !field.get().empty()) {
return true;
}
}
return false;
}
If this is too much work, then it would be the fastet way to implment the check in the tradtional handwritten way.

Related

How to get the field returned by a method invocation

I am writing a custom SonarQube rule for java, where I want to check an object is created with an argument having a specific annotation.
the file i am testing against
class MyClass {
public void doSomething() {
final var v = new Dto();
new MyObject(v.value1()); // Compliant since value1 has #MyAnnotation
new MyObject(v.value2()); // Noncompliant
}
public static class MyObject {
private final String value;
public MyObject(String value) {
this.value = value;
}
}
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
}
public static class Dto {
#MyAnnotation
private String value1;
private String value2;
public String value1() {
return this.value1;
}
public String value2() {
return this.value2;
}
}
}
the check
public class MyObjectCheck extends IssuableSubscriptionVisitor {
#Override
public List<Kind> nodesToVisit() {
return Collections.singletonList(Kind.NEW_CLASS);
}
#Override
public void visitNode(Tree tree) {
NewClassTree ctor = (NewClassTree) tree;
if(!ctor.identifier().symbolType().name().contains("MyObject")) { //to change
return;
}
if(ctor.arguments().size() == 1) {
final ExpressionTree expressionTree = ctor.arguments().get(0);
if(expressionTree.is(Kind.METHOD_INVOCATION)) {
MethodInvocationTree methodInvocation = (MethodInvocationTree) expressionTree;
}
}
}
}
from the methodInvocation, I can manage to call methodSelect to have a MethodInvocationTree but then I can't figure how to go to the field returned by the method.
I had to make concession where I consider the class of the method invoked being a POJO or a java record. This way I was able to fetch the linked field and annotations :
String methodName = methodInvocationTree.symbol().name();
final Symbol.TypeSymbol methodClass = (Symbol.TypeSymbol) methodInvocationTree.symbol().owner();
final List<SymbolMetadata.AnnotationInstance> annotations = methodClass.lookupSymbols(methodName).iterator().next().metadata().annotations();

Copying nested objects with references to each other

I have two tables in database having one to many relationship.
while I fetch the table User, I want to copy the data in the User (with the data related to Vehicle) to another object UserDuplicate (and VehicleDuplicate).
I tried using BeanUtils.copyProperties but the nested references still refer to old object.
I want to know what is the way to copy the nested objects.
Thanks.
import java.util.Set;
public class User {
private Set<Vehicle> vehs = new HasHSet();
public Set<Vehicle> getVehs() {
return vehs;
}
public void setVehs(Set<Vehicle> vehs) {
this.vehs = vehs;
}
}
class Vehicle {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
class UserDuplicate {
private Set<VehicleDuplicate> vehDup=new HasHSet();
public Set<VehicleDuplicate> getVehDup() {
return vehDup;
}
public void setVehDup(Set<VehicleDuplicate> vehDup) {
this.vehDup = vehDup;
}
}
class VehicleDuplicate {
private UserDuplicate userDup;
public UserDuplicate getUserDup() {
return userDup;
}
public void setUserDup(UserDuplicate userDup) {
this.userDup = userDup;
}
}
I like to use copy constructors in these cases:
class UserDuplicate {
...
UserDuplicate(User user) {
...
if (user.getVehs() != null) {
vehDup = new HashSet<>();
for (Vehicle v: user.getVehs()) {
vehDup.add(new VehicleDuplicate(this, v));
}
}
}
...
class VehicleDuplicate {
...
VehicleDuplicate(UserDuplicate userDup, Vehicle veh) {
this.userDup = userDup;
...
}
One approach could be to use a mapper to copy what you need to the duplicated object, this approach can have a lighter footprint on your code...
You can for example use jackson-databind
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
UserDuplicate duplicated = mapper.readValue(mapper.writeValueAsString(source), UserDuplicate.class);
// set the correct duplicate user in the duplicated vehicule
duplicated.getVehDup().forEach(x -> x.setUserDup(duplicated));
This will serialize the source object into JSON and then deserialize it into an instance of the duplicated object.
Because you are saying that your schemas are more or less the same you can take care of the annotations provided by Jackson for example to ignore some field.
static class User {
#JsonIgnore // Allows to ignore attributes from stadging to production
private String iDontWantToCopyThis = "blablabla";
private Set<Vehicle> vehs;
public Set<Vehicle> getVehs() {
return vehs;
}
public void setVehs(Set<Vehicle> vehs) {
this.vehs = vehs;
}
}
Doing this the iDontWantToCopyThis field won't be copied into the duplicated object.
Because your Vehicle contains a reference to the user you need to annotate with #JsonIgnore to avoid the recursivity during the deserialization.
static class Vehicle {
#JsonIgnore
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
And because the name of the vehicule set is different into the UserDuplicate class you must use the annotation #JsonProperty("vehs") to let the mapper know how to match the datas.
static class UserDuplicate {
#JsonProperty("vehs") // need to specify the source name into the json used to load the user duplicated
private Set<VehicleDuplicate> vehDup;
public Set<VehicleDuplicate> getVehDup() {
return vehDup;
}
public void setVehDup(Set<VehicleDuplicate> vehDup) {
this.vehDup = vehDup;
}
}
If you have data transformations too complex to be processed just by the annotations you can also create custom serializer or deserializer...

Spring Custom Annotation Validation with multiple field

A little greedy question here, hope this one could also help others who want to know more about annotation validation
I am currently studying Spring, and for now, I am planning to try out the customize annotated validation.
I have searched a lot and now I know there are mainly two kinds of validations, one is used for the controller, and the other is the annotation method using #Valid
So here's my scenario:
Suppose I have two or more fields which can be null when they are ALL NULL.
But only when one of those fields contains any value except an empty string, those fields are required to have input. And I had two ideas but didn't know how to implement them correctly.
Here's the Class Example:
public class Subscriber {
private String name;
private String email;
private Integer age;
private String phone;
private Gender gender;
private Date birthday;
private Date confirmBirthday;
private String birthdayMessage;
private Boolean receiveNewsletter;
//Getter and Setter
}
Suppose I want that the birthday and confirmBirthday field need to be both null or the oppose, I may want to annotate them using one annotation for each of them and looks like this:
public class Subscriber {
private String name;
private String email;
private Integer age;
private String phone;
private Gender gender;
#NotNullIf(fieldName="confirmBirthday")
private Date birthday;
#NotNullIf(fieldName="birthday")
private Date confirmBirthday;
private String birthdayMessage;
private Boolean receiveNewsletter;
//Getter and Setter
}
So i do need to create the validation Annotation like this:
#Documented
#Constraint(validatedBy = NotNullIfConstraintValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD })
public #interface NotNullIf {
String fieldName();
String message() default "{NotNullIf.message}";
Class<?>[] group() default {};
Class<? extends Payload>[] payload() default {};
}
And After that i will need to create the Validator itself:
public class NotNullIfConstraintValidator implements ConstraintValidator<NotNullIf, String>{
private String fieldName;
public void initialize(NotNullIf constraintAnnotation) {
fieldName = constraintAnnotation.fieldName();
}
public boolean isValid(String value, ConstraintValidatorContext context) {
if(value == null) {
return true;
};
//TODO Validation
return false;
}
}
So how can it be achievable?
For another idea using the same Class as an example which said that i want birthday, confirmBirthday and birthdayMessdage can only be null or the oppose at the same time.
I may require to use the class annotated validation this time for cross-field validation.
Here's how i suppose to annotate the class:
#NotNullIf(fieldName={"birthday", "confirmBirthday", "birthdayMessage"})
public class Subscriber {
//Those field same as the above one
}
So when one of that field is not null, the rest of them also needs to be entered on the client size.
Is it Possible?
I have read this article: How to access a field which is described in annotation property
But I still confusing on how the annotation validation works from those elements I listed above.
Maybe I need some detail explanation on that code or even worse I may need some basic concept inspection.
Please Help!
For this you can use a type level annotation only because a field level annotation has no access to other fields!
I did something similar to allow a choice validation (exactly one of a number of properties has to be not null). In your case the #AllOrNone annotation (or whatever name you prefer) would need an array of field names and you will get the whole object of the annotated type to the validator:
#Target(ElementType.TYPE)
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = AllOrNoneValidator.class)
public #interface AllOrNone {
String[] value();
String message() default "{AllOrNone.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class AllOrNoneValidator implements ConstraintValidator<AllOrNone, Object> {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private String[] fields;
#Override
public void initialize(AllOrNone constraintAnnotation) {
fields = constraintAnnotation.value();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
long notNull = Stream.of(fields)
.map(field -> PARSER.parseExpression(field).getValue(value))
.filter(Objects::nonNull)
.count();
return notNull == 0 || notNull == fields.length;
}
}
(As you said you use Spring I used SpEL to allow even nested fields access)
Now you can annotate your Subscriber type:
#AllOrNone({"birthday", "confirmBirthday"})
public class Subscriber {
private String name;
private String email;
private Integer age;
private String phone;
private Gender gender;
private Date birthday;
private Date confirmBirthday;
private String birthdayMessage;
private Boolean receiveNewsletter;
}
Consider adding compile-time validation for the field names. For example, in #Arne answer the strings "birthday" and "confirmBirthday" are not guaranteed to match actual field names at compile time. If you want to add that functionality, here's an example from my code for a slightly different example that assumes there are exactly two fields. The purpose is to assert that two fields are ordered... For example, it could be used for "beginDate" and "endDate".
public class OrderedValidator extends AbstractProcessor implements ConstraintValidator<Ordered, Object>
{
private String field1;
private String field2;
private Messager messager;
public void initialize(Ordered constraintAnnotation)
{
this.field1 = constraintAnnotation.field1();
this.field2 = constraintAnnotation.field2();
}
#Override
public synchronized void init(ProcessingEnvironment processingEnv)
{
super.init(processingEnv);
messager = processingEnv.getMessager();
}
#SuppressWarnings("unchecked")
public boolean isValid(Object value, ConstraintValidatorContext context)
{
Object field1Value = new BeanWrapperImpl(value).getPropertyValue(field1);
Object field2Value = new BeanWrapperImpl(value).getPropertyValue(field2);
boolean valid = true;
if (field1Value != null && field2Value != null)
{
if (field1Value.getClass().equals(field2Value.getClass()))
{
valid = ((Comparable) field1Value).compareTo((Comparable) field2Value) <= 0;
}
}
return valid;
}
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
{
for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Ordered.class))
{
if (annotatedElement.getKind() != ElementKind.CLASS)
{
messager.printMessage(Diagnostic.Kind.ERROR, "Only classes can be annotated with " + Ordered.class.getSimpleName());
return true;
}
TypeElement typeElement = (TypeElement) annotatedElement;
List<? extends Element> elements = typeElement.getEnclosedElements();
boolean field1Found = false;
boolean field2Found = false;
for (Element e : elements)
{
if (e.getKind() == ElementKind.FIELD && field1 != null && field1.equals(e.getSimpleName()))
{
field1Found = true;
}
else if (e.getKind() == ElementKind.FIELD && field2 != null && field2.equals(e.getSimpleName()))
{
field2Found = true;
}
}
if (field1 != null && !field1Found)
{
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find field named " + field1);
return true;
}
if (field2 != null && !field2Found)
{
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find field named " + field2);
return true;
}
}
return false;
}
}

Which pattern to use to avoid code duplication with object value transformer

I want to get rid of the following code duplication within the MyFacadeBean. Consider the following situation:
public class FacadeBean implements Facade {
#EJB
private CrudService crudService;
#Inject
private FirstAssembler firstAssembler;
#Inject
private SecondAssembler secondAssembler;
#Inject
private ThirdAssembler thridAssembler;
#Inject
private FourthAssembler fourthAssembler;
#Override
public void save(FirstValue value) {
FirstEntity entity = this.firstAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
#Override
public void save(SecondValue value) {
SecondEntity entity = this.secondAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
#Override
public void save(ThirdValue value) {
ThirdEntity entity = this.thirdAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
#Override
public void save(FourthValue value) {
FourthEntity entity = this.fourthAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
}
public interface MyFacade {
void save(FirstValue value);
void save(SecondValue value);
}
With the CrudService:
public interface CrudService {
void persist(Object entity);
}
#Stateless
#Local(CrudService.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public class CrudServiceBean implements CrudService {
public static final String PERSISTENCE_UNIT_NAME = "my_persistence_unit";
private EntityManager entityManager;
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public void persist(Object entity) {
this.entityManager.persist(entity);
}
}
With the following assemblers:
public class FirstAssembler extends AbstractAssembler<FirstEntity> {
public FirstEntity transformToEntity(FirstValue value) {
if (value == null)
return null;
FirstEntity entity = new FirstEntity();
transformAbstractValueToAbstractObject(value, entity);
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
}
public class SecondAssembler extends AbstractAssembler<SecondEntity> {
public SecondEntity transformToEntity(SecondValue value) {
if (value == null)
return null;
SecondEntity entity = new SecondEntity();
transformAbstractValueToAbstractObject(value, entity);
entity.setTransactionType(value.getTransactionType());
entity.setValueDate(value.getValueDate());
return entity;
}
}
public abstract class AbstractAssembler<T extends AbstractEntity> {
protected void transformAbstractValueToAbstractObject(AbstractValue value, T object) {
object.setUniqueId(value.getUniqueId());
object.setNominalAmountValue(value.getNominalAmountValue());
}
}
With the following entities:
#Entity
public class FirstEntity extends AbstractEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
private Long id;
#Column(name = "START_DATE")
#Temporal(TemporalType.DATE)
private Date startDate;
#Column(name = "FIXED_RATE")
#Digits(integer = 1, fraction = 10)
private BigDecimal fixedRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public BigDecimal getFixedRate() {
return fixedRate;
}
public void setFixedRate(BigDecimal fixedRate) {
this.fixedRate = fixedRate;
}
}
#Entity
public class SecondEntity extends AbstractEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
private Long id;
#Column(name = "VALUE_DATE")
#Temporal(TemporalType.DATE)
private Date valueDate;
#Column(name = "TRANSACTION_TYPE")
#Enumerated(EnumType.STRING)
private TransactionType transactionType;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getValueDate() {
return valueDate;
}
public void setValueDate(Date valueDate) {
this.valueDate = valueDate;
}
public TransactionType getTransactionType() {
return transactionType;
}
public void setTransactionType(TransactionType transactionType) {
this.transactionType = transactionType;
}
}
#MappedSuperclass
public abstract class AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "TRANSACTION_NOM_AMOUNT_VALUE")
#Digits(integer = 18, fraction = 5)
#Min(0)
private BigDecimal nominalAmountValue;
public BigDecimal getNominalAmountValue() {
return nominalAmountValue;
}
public void setNominalAmountValue(BigDecimal nominalAmountValue) {
this.nominalAmountValue = nominalAmountValue;
}
}
I tried the following approach:
public class FacadeBean implements Facade {
#Inject
private Assembler assembler;
#Inject
private AssemblerFactory assemblerFactory;
#Override
public <T extends AbstractValue> void save(T value) {
Assembler assembler = assemblerFactory.createAssembler(value);
AbstractEntity entity = assembler.transformToEntity(value);
this.crudService.persist(entity);
}
}
Problems are the AssemblerFactoryImpl and the AssemblerImpl in which I have to do instanceOf checks and castings...
Another idea would be to let the value know which transformer to use (or how to transform). But I want the value to be "dumb".
#Glenn Lane
public AbstractValue save(AbstractValue value) {
AbstractAssembler<AbstractValue, AbstractEntity> assembler = new FirstAssembler();
AbstractEntity entity = assembler.transformToEntity(value);
AbstractValue result = assembler.transformToValue(entity);
return result;
}
does not work, because of
Type mismatch: cannot convert from FirstAssembler to AbstractAssembler
I'm posting this as a separate answer, since I don't really think there's anything wrong with having a save method for every AbstractValue type.
First we'll establish your base value class for this example. I'm using an interface just so we don't muddy the waters. Your AbstractValue interface:
public interface AbstractValue
{
int getUniqueId();
double getNominalValue();
<T> T accept(AbstractValueVisitor<T> visitor);
}
And the "visitor interface":
public interface AbstractValueVisitor<T>
{
T visit(FirstValue value);
T visit(SecondValue value);
T visit(ThirdValue value);
T visit(FourthValue value);
}
I know you don't want intelligence baked into AbstractValue, but we are going to add one specification... that all concrete implementations of AbstractValue (all four) implement the accept method exactly this way:
#Override
public <T> T accept(AbstractValueVisitor<T> visitor)
{
return visitor.visit(this);
}
So that method is implemented four times: in all four value classes, exactly the same way. Because the visitor interface is aware of all concrete implementations, the appropriate method will be called for each particular value type. All three of these parts put together is the "visitor pattern".
Now we'll make an entity factory. Its job is to create the appropriate AbstractEntity when provided an AbstractValue:
public class AbstractEntityFactory
implements AbstractValueVisitor<AbstractEntity>
{
private static final AbstractEntityFactory INSTANCE;
static
{
INSTANCE = new AbstractEntityFactory();
}
// Singleton pattern
private AbstractEntityFactory()
{
}
public static AbstractEntity create(AbstractValue value)
{
if (value == null)
{
return null;
}
AbstractEntity e = value.accept(INSTANCE);
e.setNominalValue(value.getNominalValue());
e.setUniqueId(value.getUniqueId());
return e;
}
#Override
public AbstractEntity visit(FirstValue value)
{
FirstEntity entity = new FirstEntity();
// Set all properties specific to FirstEntity
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
#Override
public AbstractEntity visit(SecondValue value)
{
SecondEntity entity = new SecondEntity();
// Set all properties specific to SecondEntity
entity.setTransactionType(value.getTransactionType());
entity.setValueDate(value.getValueDate());
return entity;
}
#Override
public AbstractEntity visit(ThirdValue value)
{
ThirdEntity entity = new ThirdEntity();
// Set all properties specific to ThirdEntity
return entity;
}
#Override
public AbstractEntity visit(FourthValue value)
{
FourthEntity entity = new FourthEntity();
// Set all properties specific to FourthEntity
return entity;
}
}
Now your facade implementation takes an AbstractValue, and you got that one save method you're looking for:
public class FacadeBean implements Facade
{
#EJB
private CrudService crudService;
#Override
public void save(AbstractValue value)
{
AbstractEntity entity = AbstractEntityFactory.create(value);
crudService.persist(entity);
}
}
Because your AbstractValue now follows the visitor pattern, you can do all sorts of polymorphic behavior. Such as:
public class AbstractValuePrinter implements AbstractValueVisitor<Void>
{
private final Appendable out;
public AbstractValuePrinter(Appendable out)
{
this.out = out;
}
private void print(String s)
{
try
{
out.append(s);
out.append('\n');
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
}
#Override
public Void visit(FirstValue value)
{
print("I'm a FirstValue!");
print("Being a FirstValue is groovy!");
return null;
}
#Override
public Void visit(SecondValue value)
{
print("I'm a SecondValue!");
print("Being a SecondValue is awesome!");
return null;
}
#Override
public Void visit(ThirdValue value)
{
print("I'm a ThirdValue!");
print("Meh.");
return null;
}
#Override
public Void visit(FourthValue value)
{
print("I'm a ThirdValue!");
print("Derp.");
return null;
}
}
In this example, this visitor isn't returning anything... it's "doing" something, so we'll just set the return value as Void, since it's non-instantiatable. Then you print the value simply:
// (value must not be null)
value.accept(new AbstractValuePrinter(System.out));
Finally, the coolest part of the visitor pattern (in my opinion): you add FifthValue. You add the new method to your visitor interface:
T visit(FifthValue value);
And suddenly, you can't compile. You must address the lack of this handling in two places: AbstractEntityFactory and AbstractValuePrinter. Which is great, because you should consider it in those places. Doing class comparisons (with either instanceof or rinde's solution of a class-to-factory map) is likely to "miss" the new value type, and now you have runtime bugs... especially if you're doing 100 different things with these value types.
Anyhoo, I didn't want to get into this, but there you go :)
Use a generic method with a bound type parameter in order to spare yourself the repetition:
public <T extends AbstractValue> T save(T value) {...}
Within the method body, you'll be able to reference the argument value with all methods pertaining to AbstractValue.
Notes
Since your save methods seem to be overrides in this example, you might need to change the design of the parent class or interface too.
You could also use a generic class to start with (instead of a generic method in a non-necessarily generic class), depending on your use case.
I think a problem in your code is that the generic type of AbstractAssembler is that of the output of the transform method, not the input. If you change it as follows:
public abstract class AbstractAssembler<T extends AbstractValue> {
protected void transformAbstractValueToAbstractObject(AbstractEntity entity, T value) {
entity.setUniqueId(value.getUniqueId());
entity.setNominalAmountValue(value.getNominalAmountValue());
}
public abstract AbstractEntity transformToEntity(T value);
}
Then you can change the FacadeBean to the following.
public class FacadeBean {
#EJB
private CrudService crudService;
final Map<Class<?>, AbstractAssembler<?>> knownAssemblers;
FacadeBean() {
knownAssemblers = new LinkedHashMap<>();
knownAssemblers.put(FirstValue.class, new FirstAssembler());
knownAssemblers.put(SecondValue.class, new SecondAssembler());
// add more assemblers here
}
public <T extends AbstractValue> void save(T value, Class<T> type) {
#SuppressWarnings("unchecked") // safe cast
final AbstractAssembler<T> assembler =
(AbstractAssembler<T>) knownAssemblers.get(type);
final AbstractEntity entity = assembler.transformToEntity(value);
this.crudService.persist(entity);
}
}
Notice that I changed the signature of the save(..) method such that we have the type of the object that needs to be saved. With this type we can simply lookup the right assembler that should be used. And because the assembler is now generic on its input type, we can do a safe cast (be careful to keep your map consistent).
This implementation avoids duplication of code as you only need one save method. The use of the instanceof operator is prevented by changing the generic type of AbstractAssembler and storing all assemblers in a map.
The assemblers can look like this:
public class FirstAssembler extends AbstractAssembler<FirstValue> {
#Override
public FirstEntity transformToEntity(FirstValue value) {
final FirstEntity entity = new FirstEntity();
// do transformational stuff
super.transformAbstractValueToAbstractObject(entity, value);
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
}
public class SecondAssembler extends AbstractAssembler<SecondValue> {
#Override
public SecondEntity transformToEntity(SecondValue value) {
final SecondEntity entity = new SecondEntity();
// do transformational stuff
super.transformAbstractValueToAbstractObject(entity, value);
return entity;
}
}
Note: I'm not familiar with Java beans so you probably have to adapt the code a little if you want to use the #Injected assemblers instead of calling the constructors directly.
You're getting close to gold-plating here, but there is a bit of reduction you can do, specifically the null-check and calling the common field-setting method from each extension.
public abstract class AbstractAssembler<V extends AbstractValue, E extends AbstractEntity>
{
public final E transformToEntity(V value)
{
if (value == null)
{
return null;
}
E entity = createEntity(value);
entity.setUniqueId(value.getUniqueId());
entity.setNominalAmountValue(value.getNominalAmountValue());
return entity;
}
/**
* #return
* Appropriate entity object, with the fields not common to all AbstractEntity
* already set
*/
protected abstract E createEntity(V value);
}
And then the extended assembler:
public class FirstAssembler extends AbstractAssembler<FirstValue, FirstEntity>
{
#Override
protected FirstEntity createEntity(FirstValue value)
{
FirstEntity entity = new FirstEntity();
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
}
If you really want a single factory class to handle all your values/entities, I would look into the visitor pattern, enhanced with a generic type parameter on the visitor interface (and the entity/value accept methods return a type based on the visiting interface). I won't show an example here simply because I don't think it's warranted in your case.
You can have one save method from the point of view of the classes that save those values, but you still have to implement three individual save methods.
Implement a class with all three save methods. For example:
public class ValuePersister {
#Inject
private Assembler1 assembler1;
#Inject
private Assembler2 assembler2;
#Inject
private Assembler3 assembler3;
public Value1 save(Value1 value1, CrudService crudService) {
Entity1 entity1 = assembler1.transformToObject(value1);
crudService.persist(entity1);
return assembler1.transformToValue(entity1);
}
public Value2 save(Value2 value2, CrudService crudService) {
Entity2 entity2 = assembler2.transformToObject(value2);
crudService.persist(entity2);
return assembler2.transformToValue(entity2);
}
public Value3 save(Value3 value3, CrudService crudService) {
Entity3 entity3 = assembler3.transformToObject(value3);
crudService.persist(entity3);
return assembler3.transformToValue(entity3);
}
}
Add an abstract method to AbstractValue:
public abstract AbstractValue save(ValuePersister valuePersister, CrudService crudService);
Implement that method in each class that extends AbstractValue:
#Override
public AbstractValue save(ValuePersister valuePersister, CrudService crudService) {
return valuePersister.save(this, crudService);
}
Inject ValuePersister and implement your original generic save method:
#Inject
private ValuePersister valuePersister;
#Override
public AbstractValue save(AbstractValue value) {
return value.save(valuePersister, crudService)
}

Validate object field by conditions using Spring 3

I have a complex object that contains two BigDecimal field
public class Test
{
private BigDecimal property1;
private BigDecimal property2;
//setter and getter method
}
Now when user entered property1 in spring form It should be 10% of property2.
This validation i have to do using spring validation framework.
public class TestValidator implements Validator {
public boolean supports(Class clazz) {
return Test.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
Test t = (Test) obj;
if(t.getProperty1()!=0.1*t.getProperty2()){
e.rejectValue("Test", "property1 not 10% of property2");
}
}
}

Categories