Make all fields in Java Immutable Nullable - java

#Immutable
#Modifiable
public interface Record {
String id();
String fName();
String lName();
String mName();
}
All the fields in Record can be Nullable. One way for me to make these fields Nullable is to add #Nullable annotation on top of each field. Like this:
#Nullable
String mName()
Is there a way for me to specify at the interface level(through some annotation) that all fields can be null?

Related

Can we use Composite Primary Key Mapping in spring data elastic search

I have an entity 'Product' and I want the primary key in ES to be used as a combination of 'id' and 'name' attributes. How can we do that using spring data elastic search.
public class Product {
#Id
private String id;
#Id
private String name;
#Field(type = FieldType.Keyword)
private Category category;
#Field(type = FieldType.Long)
private double price;
#Field(type = FieldType.Object)
private List<ValidAge> age;
public enum Category {
CLOTHES,
ELECTRONICS,
GAMES;
}
}
One way to achieve this would be the following:
first rename your id property, I changed it to documentId here. This is necessary, because in Spring Data
Elasticsearch an id-property can be either annotated with #Id or it can be namend id. As there can only be one
id-property we need to get this out of the way. It can have the name id in Elasticsearch, set by the #Field
annotation, but the Java property must be changed.
second, add a method annotated with #Id and #AccessType(AccessType.Type.PROPERTY) which returns the value you
want to use in Elasticsearch.
third, you need to provide noop-setter for this property. This is necessary because Spring Data Elasticsearchsoe
not check the id property to be read only when populating an entity after save or when reading from the index.
This is a bug in Spring Data Elasticsearch, I'll create an issue for that
So that comes up with an entity like this:
#Document(indexName = "composite-entity")
public class CompositeEntity {
#Field(name="id", type = FieldType.Keyword)
private String documentId;
#Field(type = FieldType.Keyword)
private String name;
#Field(type = FieldType.Text)
private String text;
#Id
#AccessType(AccessType.Type.PROPERTY)
public String getElasticsearchId() {
return documentId + '-' + name;
}
public void setElasticsearchId(String ignored) {
}
// other getter and setter
}
The repository definition would be straight forward:
public interface CompositeRepository extends ElasticsearchRepository<CompositeEntity,
String> {
}
Remember that for every method that needs an Elasticsearch Id, you'll need to create like it's done in the entity
class.
I am not sure about spring data elasticsearch but spring jpa provides the facility of defining composite primary key by using #IdClass where we can define a separate class(let us say class A) in which we can define all the fields which we want to be a part of composite key Then we can use #IdClass(A.class) in entity class and use #Id annotation on all the fields which should be the part of the composite key
you can refer to this article, although I am not sure whether the same concept will be applicable for spring data es - https://www.baeldung.com/jpa-composite-primary-keys

Disable JSR-303 annotation processing in springdoc

How can I disable JSR-303 annotation processing in springdoc for specific fields?
I have the following request class MyRequestTO where the field name is actually optional. The #NotBlank annotation is only applied to the unwrapped JsonNullable. This means the user is allowed to omit the field when sending MyRequestTO but if set it must not be blank. However the open api doc marks the name field as required. Changing the #Schema annotation to #Schema(type = "string", required = false) does not help.
I want to avoid a solution where I have to write my own annotation and make use of org.springdoc.core.customizers.OpenApiCustomiser. The desired solution should also work for other types like JsonNullable<Boolean> annotated with #NotNull.
public class MyRequestTO {
#Schema(type = "string")
#NotBlank
private JsonNullable<String> name = JsonNullable.undefined();
public JsonNullable<String> getName() {
return name;
}
public void setName(JsonNullable<String> name) {
this.name = name;
}
}
Relevant dependencies
implementation "org.openapitools:jackson-databind-nullable:0.2.1"
implementation "org.springdoc:springdoc-openapi-ui:1.5.5"
This is not working as #NotBlank allow null values
The #NotNull class isValid() method is called after the #NotBlank class isValid(), hence forbidding null values.
So you can try with #Pattern with validation of not black string as follows :
#Pattern(regexp = "/^$|\\s+/")
String name
This will allows not null values but not allow empty string
#crizzis solution from the comments to my question works as expected. Fields are no longer marked as required but if supplied have to conform to the annotation constraints.
The JSR-303 (e.g. #NotBlank or #NotNull) annotation belongs in front of the type parameter:
private JsonNullable<#NotBlank String> name = JsonNullable.undefined();
private JsonNullable<#NotNull Boolean> enabled = JsonNullable.undefined();
Then the resulting openAPI docs will mark the fields as "required" : false.

SpringBoot DTO + JPA with List<T> property

Let say I have the following entity, (I skip the constructor for simplicity):
public class Person {
int id;
String name;
String lastName;
Date birthday;
List<Vehicles> vehicles;
}
And I want to create a DTO in order to get only what I need from the DB:
public class PersonDTO {
int id;
String name;
List<Vehicles> vehicles;
}
My Crud repository looks like this:
Person findById(int personId);
But I want to change it to:
PersonDTO findById(int personId);
It works perfectly if I remove the vehicles property, (is a List), but I do need this list. Any clue?
Citing the Spring Data JPA reference:
Another way of defining projections is by using value type DTOs (Data Transfer Objects) that hold properties for the fields that are supposed to be retrieved. These DTO types can be used in exactly the same way projection interfaces are used, except that no proxying happens and no nested projections can be applied.
Your List<Vehicles> vehicles is a nested projection, so your apporach is not applicable. You need to make use of an interface-based projection in this case:
public interface PersonProjection {
int getId();
String getName();
List<VehicleProjection> getVehicles();
interface VehicleProjection {
// Getters for desired fields of Vehicle as above for Person(Projection)
}
}
Then you can change your repository method to
PersonProjection findById(int personId);
Unfortunately, the interface-based approach is slightly less performant because, as the reference documentation states, proxying happens.

how to force hibernate call setter methods to fill class fields?

I've one persistent class that has one transient field representing the API version of this class (subset of the fields that I user for api requests). This field is #Transient as I simple use the other fields to create it.
The problem is that hibernate uses the default empty constructor to instantiate the class and reflection to access the fields... so i can't instantiate my transient class on constrorctor nor on the call of setter methods
I tried to anotate the getter method instead of the field to force hibernate to use the setter, but it didn't work
I tried to use #Access(AccessType.PROPERTY) on the fields but it didn't work
how to force hibernate call setter methods to fill class fields?
#Entity
public class User {
#Transient
private ApiUser tempUser = new ApiUser ();
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Access(AccessType.PROPERTY)
#Column(nullable = false)
private String name;
#Access(AccessType.PROPERTY)
#Column(nullable = false, unique = true)
private String username;
#Access(AccessType.PROPERTY)
#Column(nullable = false)
private String userId;
//lots of others fields//
public void setUsername(String username) {
this.username = username;
this.tempUser.setUsername(username);
}
public void setUserId(String userId) {
this.userId = userId;
this.tempUser.setId(Long.parseLong(userId));
}
By default the access type is defined by the place where you put your identifier annotation (#Id). If you put it on the field - it will be AccessType.FIELD, if you put it on the getter - it will be AccessType.PROPERTY.
Sometimes you might want to annotate not fields but properties (e.g. because you want to have some arbitrary logic in the getter or because you prefer it that way.) In such situation you must define a getter and annotate it as AccessType.PROPERTY.
As far as I remember, if you specify either AccessType.FIELD or AccessType.PROPERTY on any of the entity fields / methods you must specify the default behaviour for the whole class. And that's why you need to have AccessType.FIELD on the class level (despite that AccessType.FIELD is the default value.)
Now, if you wouldn't have #Transient on the phnnumber field, the JPA would provide you with a 3 columns table:
id,
phnnumber,
getphnnumber.
That's because it would use AccessType.FIELD for all of the entity fields (id and phnnumber) and, at the same time, it'd use AccessType.PROPERTY for your getter (getPhnnumber()).
You'll end with phone number mapped twice in the database.
Therefore, the #Transient annotation is required - it means that the entity won't store the value of the field in the underlying storage but the value returned by your getter.

Why JPA Transient annotation have method in Target?

Can anyone explain using an example as to why the #Transient annotation in JPA has #Target method as well?
I am referring to documentation http://docs.oracle.com/javaee/5/api/javax/persistence/Transient.html
#Target(value={METHOD,FIELD})
Thanks in advance!
In JPA entity you can annotate fields or methods (getters). The #Id annotation dictates this, meaning if you put #Id on a field then all your annotations should go on fields but if you put it on, for example, #Id Long getId() then other annotations should follow. That's why #Transient can be on a method as well.
For example, if you have this
#Id
private Long id;
#Transient
private String someTransientField;
private Long getId() {
return this.id;
}
private String getSomeTransientField() {
return this.someTransientField;
}
then someTransientField would be treated as transient. But if #Id would stay on the field, and you move #Transient to private String getSomeTransientField() then someTransientField would be treated as persistent, since #Id is on the field and therefore all other annotations are expected to be on fields as well.
So the case where #Transient would work on the method is this
private Long id;
private String someTransientField;
#Id
private Long getId() {
return this.id;
}
#Transient
private String getSomeTransientField() {
return this.someTransientField;
}
#Target annotation lets you define where this annotation can be used, e.g., the class, fields, methods, etc. indicates which program element(s) can be annotated using instances of the annotated annotation type.
#Target(value={METHOD,FIELD}) means that the annotation can only be used on top of types (methods and fields typically).you can leave the target out all together so the annotation can be used for both classes, methods and fields.
In JPA #Target – Marks another annotation #Transient to restrict what kind of java elements the annotation may be applied to.
It means the annotation can be used on Field or method.
If the field is annotated, the field will be accessed using reflection.
If method(getter) is annotated, then the getter method will be used to access it.

Categories