Cannot bind Map to Object in Spring Configuration Properties from YAML file - spring-boot

I have the following configuration in my Spring boot's application.yml file:
project:
country-properties:
france:
capital: paris
population: 60
And I have the the properties class : CountryProperties :
#Getter
#AllArgsConstructor
#ConstructorBinding
#ConfigurationProperties(prefix="project.country-properties")
public class CountryProperties {
private Map<String, CountryData> countryProperties;
#Getter
#Setter
public static class CountryData {
private String capital;
private Integer population;
}
However my CountryProperties is always null, and it's because of a failed mapping with the CountryData object.
Any ideas what is wrong with what I wrote?

You have the annotation #ConstructorBinding. This annotation tells Spring to look for a constructor in your class that has parameters corresponding to your configuration properties, and then will bind the properties.
What you are missing is:
public CountryProperties(Map<String, CountryData> countryProperties) {
this.countryProperties = countryProperties;
}
Update:
After inspecting your code again, it looks like you aren't mapping the configuration correctly to the instance field. Please update your #ConfigurationProperties(prefix="project.country-properties") to #ConfigurationProperties(prefix="project").
Also replace the #ConstructorBinding with #Configuration.

Related

#DefaultValue not working with #ConstructorBinding for Spring boot 2.4.2

Here is an example of my problem. when no value is supplied to default-name in yml file. #DefaultValue should step in and fill with "Name". However, is not how it behaves. An empty string is assigned to defaultName
application.yml:
account:
default-name:
class:
#ConstructorBinding
#ConfigurationProperties(prefix = "account")
public class Account {
private final String defaultName;
public Account(#DefaultValue("Name") String defaultName) {
this.defaultName = defaultName;
}
..
..
}

How to make Set using spring-data-aerospike

Environment:
spring-boot v2.0.4 RELEASE
spring-data-aerospike v2.0.1.RELEASE
java - 8
Here are my application code and properties.
// application.properties
aerospike.hosts=xxx:3000
aerospike.namespace=test
// aerospike configuration class
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(AerospikeConfiguration.AerospikeConfigurationProperties.class)
#EnableAerospikeRepositories(basePackageClassses = TestAeroRepository.class)
public class AerospikeConfiguration extends AbstractAerospikeDataConfiguration {
private final AerospikeConfigurationProperties aerospikeConfigurationProperties;
#Override
protected Collection<Host> getHosts() {
return Host.parseServiceHosts(aerospikeConfigurationProperties.getHosts());
}
#Override
protected String nameSpace() {
return aerospikeConfigurationProperties.getNamespace();
}
#Data
#Validate
#ConfigurationProperties("aerospike")
public static class AerospikeConfigurationProperties {
#NotEmpty
String hsots;
#NotEmpty
String namespace;
}
}
# Entity class
#Value
#Document
#Builder(toBuilder = true)
#AllArgsConstructor
public class testEntity() {
#Id
int id;
#Field
String name;
#Field
String timestamp;
}
#Repository
public interface TestAeroRepository extends AerospikeRepository<TestEntity, Integer> {
}
public interface TestAeroService {
void save();
}
#Service
#RequiredArgsConstructor
public class TestAeroServiceImpl implements TestAeroService {
private final TestAeroRepository testAeroRepository;
#Override
public void save(TestEntity entity) {
testAeroRepository.save(entity);
}
}
I checked Aerospike client connection has no problem.
But error is occurred when save() method is executed.
org.springframework.cglib.core.ReflectUtils.defineClass(Ljava/lang/String;[BLjava/lang/ClassLoader;Ljava/security/ProtectionDomain;Ljava/lang/Class;)Ljava/lang/Class;
Have to make sets before execute the application? I didn't make sets.
Any problem with my code?
You’re using an old version of spring-data-aerospike (2.0.1.RELEASE was released on April 2019) is there any chance you can upgrade to the latest version? 2.4.2.RELEASE
You can see how to setup a simple spring data aerospike application here: https://medium.com/aerospike-developer-blog/simple-web-application-using-java-spring-boot-aerospike-database-and-docker-ad13795e0089
Please share the entire project’s code and the entire exception.
I would look into:
The configuration class (The Aerospike Beans creation).
The content of the testEntity class - are you using #Id annotation on the primary key field?
Extending the repository class with specifying the testEntity object (… extends AerospikeRepository<testEntity, Object> {) you can see an example in the link I added.
The set is automatically created and takes the name of your object class, which is testEntity in your case. For example, based on your code, if you do not specify a collection in the #Document annotation a set named "testEntity" will automatically be created. I added the #Document(collection = "testEntitys") annotation and all I did was create two set. Once you insert your first record, run the "SHOW SETS" aql command and it will be there. So that's one way to do it.

How to map property in application.yml to JsonNode? (Spring)

In a #ConfigurationProperties bean, I could map the customers property of the application.yml file below to List<Customer>, but I would like to instead map it to a Jackson JsonNode. Is this possible? If so, how?
shop:
name: "Sam's Bikes"
customers:
- name: Lucy
age: 26
- name: James
age: 24
This is what I'd like to achieve:
#ConfigurationProperties("shop")
public class ShopProperties() {
private String name;
private JsonNode customers;
}
Since customers in application.yml are in Array of objects format, i would recomend either to collect them into List<Customer> or List<Map<String,Object>> using below code, #Data is lombok annotation, if you are not using lombok add getters and setters
#ConfigurationProperties("shop")
#Data
#Configuration
public class TestConfig {
private String name;
private List<Map<String,Object>> customers; //[{name=Lucy, age=26}, {name=James, age=24}]
}

Spring does not complain if a property is not set when using ConfigurationProperties

I have a bean that is configured via ConfigurationProperties:
#Component
#ConfigurationProperties(prefix = "mybean")
public class MyBean {
#NotEmpty
private String name;
// Getters, setters, ...
}
I configure the field values via application.yml but in "two levels". In the default application.yml I just set the value to the value of another property:
myBean.name: ${theValueOf.myBean.name}
In the profile specific YML file I have:
theValueOf.myBean.name: 'The desired value'
My expectation would be that if I forget to specify the property theValueOf.myBean.name then the application should fail at startup with the message that the placeholder 'theValueOf.myBean.name' could not be resolved. Instead, the field name is assigned the value (literally) ${theValueOf.myBean.name}.
If I annotate the name field with #Value("${myBean.name}") (and do not use ConfigurationProperties), and forget to define the property theValueOf.myBean.name, then the application fails at startup -- as expected.
My question is: How can I make Spring fail at startup with the message 'Could not resolve placeholder ...' when using ConfigurationProperties?
Simply mark your properties with JSR303 annotations, inside your #ConfigurationProperties.
#Component
#ConfigurationProperties(prefix = "mybean")
public class MyBean {
#NotEmpty
private String name;
}

Why I receive 404 error use Spring MVC framework?

When I send request http://localhost:8080/pets My server response 404!
The code on github: https://github.com/Teemitze/petstore
I build war file. Version spring 2.2.6.RELEASE
#Controller
#RequestMapping("/pets")
public class PetsController {
#Autowired
PetRepository petRepository;
#PostMapping("/addPet")
public void addPet(Pet pet) {
petRepository.save(pet);
}
#GetMapping
#ModelAttribute
public String pets(Model model) {
List<Pet> petList = new ArrayList<>();
petList.add(getPet());
petList.add(getPet());
petList.add(getPet());
model.addAttribute("pets", petList);
return "allPets";
}
public Pet getPet() {
Pet pet = new Pet();
pet.setId(1L);
pet.setName("Мурзик");
pet.setPrice(100);
pet.setBirthday(Date.valueOf("2019-12-12"));
pet.setSex("М");
return pet;
}
}
I checked out your code and found a few issues.
1) Package structure
Move controller, dto, repo packages to the main package (com.petstore)
Since the main application is inside the (com.petstore) package and the controller is outside the package, so it fails to scan the class.
2) Use annotation #Entity for the Pet entity class with #Id for the id property
3) Remove #ModelAttribute from pets() method since you are not binding any method parameter.
After this, I see the /pets
SpringBoot project requires define some configuration conventions that need to be follow in order to start a minimum application.
Some points you have to consider when you want to start a spring boot application.
For example:
Your SpringBootApplication(PetstoreApplication) class should be in the directory level above your other packages so that it can scan all classes.
If you want to use SpringData JPA you have to manage your model class
#Data
#Entity
public class Pet {
#Id
private long id;
private String name;
private String sex;
private Date birthday;
private byte[] photo;
private int price;
}
because it is handled by respository
public interface PetRepository extends CrudRepository<Pet, Long>
Need minimum configuration for Thymeleaf https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
You are making a GET request for a resource "/pets" so no need #ModelAttribute in get mapping method
#GetMapping()
public String allPets(Model model) {
Make sure your html files is under resources/templates directory.
Check out the reference docs
spring mvc
spring data jpa

Resources