Reading property values in constructor using prefix - spring

How to use the prefix in the below code?
Properties:
height.customer.feet=10
height.customer.eu.timezone=UTC
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "height.customer")
public class Customer {
private final int age;
private final String timezone;
public Customer(int age, String timezone){
this.age = age;
this.timezone = timezone;
}
}
Here i want to set the default value for both age and timezone. Default values are read from application.properties file. Can someone help me please?
I could use like below.
#Value("${height.customer.age}")
private final int age;
#Value("${height.customer.eu.timezone}")
private final String timezone;
But if i use like this, i may not able to use constructor injection

There is no relationship between the #ConfigurationProperties and #Value annotation. Check here. What you should be using is the #PropertySource annotation. If you use #ConfigurationProperties then you should have hierarchical properties
#Configuration
#ConfigurationProperties(prefix = "height.customer")
public class Customer {
private final int age; // This maps to height.customer.age
private final String timezone; // This does NOT map to height.customer.eu.timezone but maps to height.customer.timezone
public Customer(int age, String timezone){
this.age = age;
this.timezone = timezone;
}
}
Use the #PropertySource with this example
#Configuration
#PropertySource("classpath: demo.properties") // your properties file
public class Customer {
#Value("${height.customer.age}")
private final int age;
#Value("${height.customer.eu.timezone}")
private final String timezone;
public Customer(int age, String timezone){
this.age = age;
this.timezone = timezone;
}
public Customer(){}
}
And there won't be a conflict with the existing constructor because, values injecting via #PropertySource will be default values. If you provide values in the constructor, these will get overridden.

Related

How to validate a field based on other field value in bean (pojo) class in Spring Boot using annotations

I have created a request class having some fields with getters & setters. Now I want to validate each & every field. So with this validation I need to check if the value for field1 is A then fields2 should be mandatory and if value for field1 is B then field3 should be mandatory and field2 will be optional. Consider the below pojo class.
public class CreateADTSpaceRequestDTO implements Serializable{
private static final long serialVersionUID = 5654993652896223164L;
#NotEmpty(message = "taskUId cannot be null/empty")
#JsonProperty(value = "taskUId")
private String taskUId;
#NotEmpty(message = "clientName cannot be null/empty")
#JsonProperty(value = "clientName")
private String clientName;
#NotEmpty(message = "SpaceType cannot be null/empty")
#JsonProperty(value = "spaceType")
private String spaceType;
public String getTaskUId() {
return taskUId;
}
public void setTaskUId(String taskUId) {
this.taskUId = taskUId;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getSpaceType() {
return spaceType;
}
public void setSpaceType(String spaceType) {
this.spaceType = spaceType;
}
}
In the above class we have a field called clientName, so based on client name value I want to validate spaceType field.
For ex. if clientName = A then spaceType is mandatory and if clientName = B then spaceType is optional.
Please help me with your comments how we can have this kind of validation using annotations or using regex or any other way.

JPA calling default constructor even during POST request

I didn't had a default constructor in my entity class in the beginning. Eventually found out that JPA requires a default constructor in entity class so I made one.
After adding the default constructor, even during post requests, JPA keeps calling default constructor which leads to incorrect initialisation of properties. For example, if you see the property called availableSeats, it is initialised to 100, but during post request only default constructor is called which leads to initialisation of availableSeats to 0.
This is extremely weird and I don't understand what am I doing wrong here.
#Entity
public class Flight {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotBlank(message = "Airline Name cannot be blank!")
private String airlineName;
#NotBlank(message = "From-Location cannot be blank!")
private String fromLocation;
#NotBlank(message = "To-Location cannot be blank!")
private String toLocation;
#NotBlank(message = "Airport Gate Number cannot be blank")
private String gateNumber;
// #NotBlank(message = "Boarding time cannot be blank")
private ZonedDateTime dateTimeZone;
private static final int INITIAL_SEAT_CAPACITY = 100;
private int availableSeats;
// constructor should not be able to set id
public Flight(Long id, String airlineName, String fromLocation, String toLocation, String gateNumber, ZonedDateTime dateTimeZone, int availableSeats) {
this.id = id;
this.airlineName = airlineName;
this.fromLocation = fromLocation;
this.toLocation = toLocation;
this.gateNumber = gateNumber;
this.dateTimeZone = dateTimeZone;
// setting up initial number of available seats
this.availableSeats = INITIAL_SEAT_CAPACITY;
}
public Flight(){
}
// getters and setters
}
Also adding FlightController.java code here
#RestController
#RequestMapping("/api/flights")
public class FlightController {
#Autowired
FlightService flightService;
#GetMapping(value = "/")
public ResponseEntity<List<Flight>> getAllFlights(){
return flightService.getAllFlights();
}
#PostMapping(value = "/")
public ResponseEntity<String> createFlight(#Valid #RequestBody Flight flight){
return flightService.createFlight(flight);
}
#GetMapping(value = "/{id}")
public ResponseEntity<Flight> getFlightById(#PathVariable Long id){
return flightService.getFlightById(id);
}
#DeleteMapping(value = "/{id}")
public ResponseEntity<String> deleteFlight(#PathVariable Long id){
return flightService.deleteFlight(id);
}
}
Spring's controller uses default(zero argument) constructor for object creation and then uses it's setter methods for setting the values in the object. You cannot expect for spring to use parameterized constructor.
So if you need to set some default values then do it in zero argument constructor.
As #grigouille pointed out in the comments, JPA only uses default constructor. Hence, availableSeats should have been initialised in the default constructor too.

Spring boot using Autowire Cofiguration property class in Entity class

I have to use some set data member class in Spring entity class
Current Entity class
Entity(name="users")
public class Users{
#Id
#GeneratedValue
#Column(name=ID")
private long Id;
#Column(name=NAME")
private String name;
#Column(name=AGE")
private String age;
#Column(name=PIN")
private String pin;
public Users(String name, String age, String pin)
{
this.name = name;
this.age = age;
this.pin = pin;
}
}
Now I need to add a new Member which is unique to that place
areaId, we run sperate application per each area so this will be passed from command line arguments or config properties during application starts.
My properties class looks like below
#Component
#ConfigurationProperties("user.info")
public class UserProperties{
public String areaId;
public String getAreaID(){return this.areaId;}
public void setAreaID(String areaId){ this.areaId = areaId;}
}
users:
info:
areaId:124
I have to store this and initializes also during Users object constructing, here I am trying to make simple
Entity(name="users")
public class Users{
#Id
#GeneratedValue
#Column(name=ID")
private long Id;
#Column(name=NAME")
private String name;
#Column(name=AGE")
private String age;
#Column(name=PIN")
private String pin;
#Column(name=AREAID")
private String areaId;
public Users(String name, String age, String pin)
{
this.name = name;
this.age = age;
this.pin = pin;
this.areaId = ""//?? how to get area id directly ?
}
}
I can not change the constructor of Users because it demands changes in the other application which are using this lib
Want to Autowire a users properties class inside Entity class(but this is not suggestable as read in some articles )
What would be the best way to initialize that default kind of variable?
It seems that your property is something static.
Then get it from a static way at start.
There's several ways to get command line value at start directly or in a static block:
static {
(your code here)
}
You'll can put #Column on a getter on this property after that.

SpringBoot check if injected Properties are set NotNull

I know I can easily inject a property file in SpringBoot 2.2 with the following construct
#ConstructorBinding
#ConfigurationProperties(prefix = "example")
#Data
#AllArgsConstructor
public final class MyProps {
#NonNull
private final String neededProperty;
#NonNull
private final List<SampleProps> lstNeededProperty;
public String getFirstSample(){
return lstNeededProperty.get(0); //throws NPE
}
}
#ConstructorBinding
#AllArgsConstructor
#Data
public class SampleProps {
String key;
String label;
}
and yml file like:
expample:
neededProperty: test1
lstNeededProperty:
-key: abc
label: input
The #NonNull works quite well for the String but fails for the List - since the NPE is thrown even when the list will be set.
Is there a simple way to check if the List is initialized? I've tried #Postconstruct but this isn't called at all.
Try to check for the size and intialize the list:
#ConstructorBinding
#ConfigurationProperties(prefix = "example")
public final class MyProps {
#NonNull
private final String neededProperty;
#Size(min=1)
private final List<String> lstNeededProperty = new ArrayList<>();
}

marshall attributes inside XML elements with JAXB

I work with Spring JPA and have the following entity:
#Entity
#Table(name = Constants.ENTITY_TABLE_PREFIX + "ENTRY")
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "monObj_info")
public class EntryXML implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
#XmlAttribute
private long id;
#Column(name = "ip_address", nullable = true)
#XmlElement
private String ip_address;
#Column(name = "network_element_name", nullable = false)
#XmlElement
private String network_element_name;
public EntryXML() {}
public EntryXML(long id, String ip_address, String network_element_name) {
super();
this.id = id;
this.ip_address = ip_address;
this.network_element_name = network_element_name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getIp_address() {
return ip_address;
}
public void setIp_address(String ip_address) {
this.ip_address = ip_address;
}
public String getNetwork_element_name() {
return network_element_name;
}
public void setNetwork_element_name(String network_element_name) {
this.network_element_name = network_element_name;
}
}
and the endpoint:
#RestController
public class EntryXMLEndpoint {
#Autowired
private IEntryXMLService service;
#RequestMapping(value = "/restxml", produces = { "application/xml" })
public EntryXML findEntries() {
EntryXML record = service.findById(1);
return record;
}
}
Now the requested response is:
<monObj_info id="1">
<atribute name="ip_address" value="xx.xxx.xxx.x"/>
<atribute name="network_element_name" value="xxxxxx"/>
</monObj_info>
Of course what I get is :
<monObj_info id="1">
<ip_address>xx.xxx.xxx.x</ip_address>
<network_element_name>xxxxxx</network_element_name>
</monObj_info>
I read similar posts , but the problem is I cannot create a List with the required elements inside my Entity Class, since it will not map with any column in the respective table, any suggestions?
You can achieve your goal in a straight-forward but somewhat hackish way.
Since you don't want the ip_address and network_element_name properties
to be marshalled and unmarshalled directly, you need to remove their #XmlElement annotation
and add #XmlTransient.
Instead, you want some <atribute name="..." value="..." /> elements marshalled and unmarshalled.
Therefore you need to add the following things to your EntryXML class:
an attributes property holding a list of attributes.
It is annotated with #XmlElement so that it will be part of XML marshalling and unmarshalling.
It is annotated with #Transient so that it will not be part of database persistence.
a simple helper class Attribute for holding name and value.
name and value are annotated with #XmlAttribute so that they will be part of XML marshalling and unmarshalling.
a Marshal Event Callback (beforeMarshal)
for doing the conversion from ip_address and network_element_name
to the attributes list.
an Unmarshal Event Callback (afterUnmarshal)
for doing the opposite conversion.
#XmlElement(name = "atribute")
#Transient // from package javax.persistence
private List<Attribute> attributes;
// there is no need for getAttributes and setAttributes methods
private static class Attribute {
#SuppressWarnings("unused") // called by the unmarshaller
Attribute() {
}
Attribute(String name, String value) {
this.name = name;
this.value = value;
}
#XmlAttribute
private String name;
#XmlAttribute
private String value;
}
#SuppressWarnings("unused") // this method is called only by the marshaller
private boolean beforeMarshal(Marshaller marshaller) {
attributes = new ArrayList<>();
attributes.add(new Attribute("ip_address", ip_address));
attributes.add(new Attribute("network_element_name", network_element_name));
return true;
}
#SuppressWarnings("unused") // this method is called only by the unmarshaller
private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
if (attributes != null) {
for (Attribute attribute : attributes) {
switch (attribute.name) {
case "ip_address":
ip_address = attribute.value;
break;
case "network_element_name":
network_element_name = attribute.value;
break;
}
}
}
}
Then the XML output will look like this:
<monObj_info id="1">
<atribute name="ip_address" value="xx.xxx.xxx.x"/>
<atribute name="network_element_name" value="xxxxxx"/>
</monObj_info>

Resources