polymorphic dto in a controller - java

I have folowed this way to use polymorphic dto in a spring controller
https://bwgjoseph.com/polymorphic-dto-using-java-record-with-jackson
#JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, defaultImpl = HomeAddressDto.class)
#JsonSubTypes({
#Type(HomeAddressDto.class),
#Type(OfficeAddressDto.class)
})
public interface Address {
}
public record HomeAddressDto(String street, String postalCode, String unit) implements Address {}
public record OfficeAddressDto(String building, String street, String postalCode, String unit) implements Address {}
That work well but in the controller
#RestController
#RequestMapping("/deduction/addresses")
public class DeductionAddressController {
#PostMapping()
public Address create(#RequestBody Address address) {
if (address instanceof HomeAddressDto) {
System.out.println("home");
}
if (address instanceof OfficeAddressDto) {
System.out.println("office");
}
return address;
}
}
instanceof is used, is there a way to avoid that?

You could create a new service with similar polymorphism, for example
public class DeductionAddressController {
#Autowired
private AddressService addressService;
#PostMapping()
public Address create(#RequestBody Address address) {
addressService.processAddress(address);
return address;
}
}
where AddressService looks like
#Service
public class AddressServiceImpl implements AddressService {
#Override
public void processAddress(HomeAddressDto homeAddress) {
System.out.println("home");
// process HomeAddressDto...
}
#Override
public void processAddress(OfficeAddressDto homeAddress) {
System.out.println("office");
// process OfficeAddressDto...
}
}

I think this might be what you're looking for.
Abstract (Parent) Class:
//I'm assuming this discriminator works
#JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, defaultImpl = HomeAddressDto.class)
#JsonSubTypes({
#Type(HomeAddressDto.class),
#Type(OfficeAddressDto.class)
})
public abstract class Address {
public enum AddressType {
HOME("home"), OFFICE("office");
// set the rest up
}
public abstract AddressType getType();
}
Home Address Class:
public record HomeAddressDto(String street, String postalCode, String unit) extends Address {
#Overrides
public AddressType getType(){
return AddressType.HOME;
}
}
Office Address Class:
public record OfficeAddressDto(String building, String street, String postalCode, String unit) extends Address {
#Overrides
public AddressType getType(){
return AddressType.OFFICE;
}
}
CONTROLLER:
#RestController
#RequestMapping("/deduction/addresses")
public class DeductionAddressController {
#PostMapping()
public Address create(#RequestBody Address address) {
Log.info(address.getType());
...
...
return address;
}
}

Related

How to implement a Custom DataType in Hibernate?

We would like to type various properties in Java.
e.g. the e-mail address
But now I get the message all the time:
Could not set field value [test#test.de] value by reflection : [class customer.email] setter of customer.email;
Can not set dataType.EmailAddress field customer.email to java.lang.String
How should I proceed?
#Entity
public class customer {
#Id
#GeneratedValue
private Long id;
private String name;
private EmailAddress email;
}
public class EmailAddress {
public String value;
public EmailAddress(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public boolean setValue(String s) {
this.value = s;
return true;
}
public String mailbox() ...
public String host() ...
public String tld() ...
}
Getter and Setter from HibernateDefaultType not called.
EDIT:
At the end. I want to store a String in the database with the email-Address. In Java I want the EmailAddress Object.
it is much easier. An AttributeConverter make it very easy.
https://thorben-janssen.com/jpa-attribute-converter/
Thank you very much
EDIT:
Here is the Code:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class EmailAddressConverter implements AttributeConverter<EmailAddress, String> {
#Override
public String convertToDatabaseColumn(EmailAddress emailAddress) {
return emailAddress.value;
}
#Override
public EmailAddress convertToEntityAttribute(String s) {
return new EmailAddress(s);
}
}
And in the Entity:
#Convert(converter = EmailAddressConverter.class)
private EmailAddress email;
Here is some example of making your own custom type.
public class EmailAddressDescriptor extends AbstractTypeDescriptor<String> {
protected EmailAddressDescriptor() {
super(String.class, new ImmutableMutabilityPlan<>());
}
#Override
public String toString(String value) {
return null;
}
#Override
public String fromString(String string) {
return null;
}
#Override
public <X> X unwrap(String value, Class<X> type, WrapperOptions options) {
return null;
}
#Override
public <X> String wrap(X value, WrapperOptions options) {
return null;
}
#Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
return null;
}
}
Then you would make the Email address class with all your methods
public class EmailAddress extends AbstractSingleColumnStandardBasicType<String> {
private String value;
public EmailAddress() {
super(new VarcharTypeDescriptor(), new EmailAddressDescriptor());
}
#Override
public String getName() {
return "EmailAddress";
}
#Override
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) throws HibernateException {
return null;
}
}
public String mailbox() ...
public String host() ...
public String tld() ...
How you would use it with your entity will be something like this
#Entity
#TypeDef(name = "emailAddress", typeClass = EmailAddress.class)
public class customer {
#Id
#GeneratedValue
private Long id;
private String name;
#Type (type = "emailAddress")
private EmailAddress emailAddress;
}
Hope this helps

How to return an instance of Inherited class from parent abstract class

I'd like to solve such problem. I have some abstract class and a concrete class with setters that return the instance of that class:
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
private Integer id;
public Integer getId() {
return id;
}
public BaseEntity setId(Integer id) {
this.id = id;
return this;
}
}
next abstract:
#MappedSuperclass
public abstract class NamedEntity extends BaseEntity {
private String name;
public String getName() {
return name;
}
public NamedEntity setName(String name) {
this.name = name;
return this;
}
}
and finally a concrete class:
#Entity
public class Person extends NamedEntity {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
I'd like to use this kind of builder but in current setup it's not working due to different return types of parent setters
public Person build() {
Person person = new Person()
.setId(1); //return BaseEntity instead of Person
.setName("name") //returns NamedEntity instead of Person
.setAddress("foo"); //return Person!
return person;
}
of course ther's a workaround with overriden setters but.... can it be done other way using generics?
#Override
public Person setId(Integer id) {
super.setId(id);
return this;
}
#Override
public Person setName(String name) {
super.setName(name);
return this;
}
Thanks for all the sugestions
I know the builder pattern, but in this particular case is the same workaround as overriding the methods setId and setName
The point here is:
it is possible that setId method will return the instance of child class the method is called from
let's say I'd like to put a complex object to my builder (why not?):
public class Person extends NamedEntity {
private String address;
... getters/setters
public Builder builder() {
return new Builder();
}
public final static class Builder {
private final Person person;
private Long id;
private String name;
private String address;
private Builder() {
this.person = new Person();
}
public Builder withId(Long id) {
person.setId(id);
return this;
}
..... other setters
public Builder withDto(PersonDTO dto) {
person
.setId(dto.getId())
.setName(dto.getName())
.setAddress(dto.getAddress()
}
public Person build() {
return person;
}
}
}
as you may guess the person.setId returns instance of BaseEntity
You can use the same trick as enums (Enum), a generic type parameter for the child class.
#MappedSuperclass
public abstract class BaseEntity<E extends BaseEntity<E>> implements Serializable {
private Integer id;
public Integer getId() {
return id;
}
protected final E getThis() {
return this;
}
public E setId(Integer id) {
this.id = id;
return getThis();
}
}
#MappedSuperclass
public abstract class NamedEntity<E extends NamedEntity<E>> extends BaseEntity<E> {
private String name;
public String getName() {
return name;
}
public E setName(String name) {
this.name = name;
return getThis();
}
}
For child classes of Person you need not continue with this pattern.
#Entity
public class Person extends NamedEntity<Person> {
private String address;
public String getAddress() {
return address;
}
public Person setAddress(String address) {
this.address = address;
return this;
}
}
Now you can do_
Person einstein = new Person()
.setId(76)
.setName("Albert")
.setAddress("Princeton, New Jersey");
The alternative is a Builder pattern, however it has the same inheritance problem, and you might end up with *.Builder classes inheriting from parent Builder classes.
I would even say it is not worth this boiler plate code, just for a fluent API (chained calls). The criteria API for instance does hardly need using created objects, and the passed values for the setters must come from some code too.
Also setters implies the classes are mutable. It would be much nicer if most fields were immutable. With entity classes unrealistic, but setters are an ugly initialisation. When possible use constructors/builders without setters.
You can implement the Builder pattern by introducing a nested class Builder with a set of self-returning methods (i.e. returning an instance of Builder) which can be chained in a fluent way.
Method Builder.build() should return an instance of Person.
Note that you setters of your entities can be void.
That's how implementation might look like:
public class Person extends NamedEntity {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public static class Builder {
private Person person;
public Builder() {
this.person = new Person();
}
public Builder name(String name) {
person.setName(name);
return this;
}
public Builder address(String address) {
person.setAddress(address);
return this;
}
public Builder id(Integer id) {
person.setId(id);
return this;
}
public Person build() {
return person;
}
}
}
Usage example:
Person person = new Person.Builder()
.name("Alice")
.address("Wonderland")
.id(1)
.build();
Note:
There could be multiple ways to obtain an instance of Builder. You can introduce in the Person class a static method builder() returning a new Builder, or static methods like withName(String), withId(Integer) might also be handy (for inspiration have a look at User class from Spring Security).
When dialing with immutable objects, Builder class should have all the field of the target class duplicated instead of keeping the reference to the target object. And in such case, method build() would be responsible for constructing an instance of the target type.

AutoValue Negative Check

Can an AutoValue class do a negative check similar to how it checks for Missing required properties?
Maybe it can support validation by annotations?
#AutoValue
#JsonSerialize(as = ExampleRequest.class)
#JsonDeserialize(builder = ExampleRequest.Builder.class)
public abstract class ExampleRequest {
#JsonProperty("name")
public abstract String name();
#NegativeCheck // is there something available ?
#JsonProperty("version")
public abstract long version();
public static ExampleRequest.Builder builder() {
return Builder.builder();
}
#AutoValue.Builder
#JsonIgnoreProperties(ignoreUnknown = true)
public abstract static class Builder {
#JsonCreator
public static ExampleRequest.Builder builder() {
return new AutoValue_ExampleRequest.Builder();
}
#JsonProperty("name")
public abstract ExampleRequest.Builder name(String name);
#JsonProperty("version")
public abstract ExampleRequest.Builder version(long version);
public abstract ExampleRequest build();
}
}
I can solve it like below
but I was wondering if there is support for some built-in validation using tags so I don't have to write custom validation.
#AutoValue
#JsonSerialize(as = ExampleRequest.class)
#JsonDeserialize(builder = ExampleRequest.Builder.class)
public abstract class ExampleRequest {
#JsonProperty("name")
public abstract String name();
#JsonProperty("version")
public abstract long version();
public static ExampleRequest.Builder builder() {
return Builder.builder();
}
#AutoValue.Builder
#JsonIgnoreProperties(ignoreUnknown = true)
public abstract static class Builder {
#JsonCreator
public static ExampleRequest.Builder builder() {
return new AutoValue_ExampleRequest.Builder();
}
#JsonProperty("name")
public abstract ExampleRequest.Builder name(String name);
#JsonProperty("version")
public abstract ExampleRequest.Builder version(long version);
abstract ExampleRequest autoBuild();
public ExampleRequest build() {
ExampleRequest request = autoBuild();
validateParameterVersion(request.version());
return request;
}
private void validateParameterVersion(long version) {
if (version < 0) {
throw new IllegalStateException("version cannot be negative");
}
}
}
}

Bean Validation for Super class

Hi I have a question regarding Bean Validation (JSR-303). We can validate one bean against set of annotations provided in a bean. What if i have a Bean Car which extends Bean Vehicle, and if i pass Bean Car to the validator, how to make Bean Vehicle(i.e. Super Class) also get Validated ?
I am using this Bean Validation in Camel. Below is my Code
public void configure() throws Exception {
from("file:data/source?noop=true").process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("Process 1");
Car car = new Car();
//this property is of Super Class
//car.setVehicleId(1);
car.setName("Swift");
car.setCompany("Maruti");
exchange.getIn().setBody(car);
}
}).to("bean-validator://v").process(new Processor() {
#Override
public void process(Exchange arg0) throws Exception {
System.out.println("Exchange is : "+arg0.getIn().getBody(Car.class));
}
});
}
My Car Bean is
public class Car extends Vehicle{
#NotNull
private String name;
#NotNull
private String company;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
My Vehicle Bean is
public class Vehicle {
#NotNull
private int vehicleId;
public int getVehicleId() {
return vehicleId;
}
public void setVehicleId(int vehicleId) {
this.vehicleId = vehicleId;
}
}
Thank you. Help will be greatly appreciated.
I got this working. The problem is i have used primitive types in Vehicle [ private int vehicleId; ]
So i corrected it with [ private Integer vehicleId; ]. So after this my Vehicle bean is
public class Vehicle {
#NotNull
private Integer vehicleId;
public Integer getVehicleId() {
return vehicleId;
}
public void setVehicleId(Integer vehicleId) {
this.vehicleId = vehicleId;
}
}

get dbref from document in Mongo using Java

I cannot get dbRef object from Mongo. In my entity package I have a User class with a Parent class inheriting.
Here is the User class:
public class User {
#Id
private ObjectId id;
#DBRef
private Account account;
private String name;
public String getId() {
if (id != null) {
return id.toStringMongod();
}
return null;//no id
}
public void setId(ObjectId objectId) {
this.id = objectId;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
}
As you can see above, I am putting an object of Account here.
My Parent class simply extends User:
#Document
public class Parent extends User {
#JsonProperty("is_activated")
private boolean isActivated;
public boolean isActivated() {
return isActivated;
}
public void setActivated(boolean isActivated) {
this.isActivated = isActivated;
}
}
Note: nothing magic with isActivated.
In my ParentDaoImpl class:
#Service
public class ParentDaoImpl extends AbstractDaoImpl implements ParentDao {
#Override
public Parent getParentByLogin(String login) {
Query query = new Query(Criteria.where("login").is(login));
return mongoOperations.findOne(query, Parent.class, "parents");
}
}
The problem is that if I call getParentByLogin method, it returns evertyning but Account field is null. Maybe findOne doesn't give dbRef inside. I think in relational Databases, there would be something like join. I want my method to give me account field as well.
Thanks for your help!
Can you try something like this.
....
#Field("fieldName")
#DBRef(collection = "mongoCollectionName")
private Account account;
....

Categories