Fetching global values from database on application startup in Spring boot - spring

I'm writing a Spring Boot / JPA application. I have some values which needs to be visible to the entire application and these values are located in a database. Where should I fetch these values? Should I fetch them in the class containing #SpringBootApplication?
And how do I make it visible to the application? I read that with Spring, we can use a #Bean class to hold global variables. Then do I have to map my #Entity class with a Bean class and autowire the Bean class where ever I want? I'm new to Spring / JPA, so I apologize if the question is basic.
Thanks.

Make a bean that is instantiated with your applicationContext, and use the init-method to run some code after it's instantiated.
A very off the top of my head solution:
In applicationContext.xml:
<bean class="com.example.DbConfigLoader" init-method="init">
A class to load a config entity at startup:
public class DbConfigLoader {
#Autowired
private DbConfigRepository repository;
private DbConfig dbConfig;
public void init(){
dbConfig = repository.findOne(1L);
}
public DbConfig getDbConfig() {
return dbConfig;
}
}
A class representing your config:
#Entity
public class DbConfig {
#Id
private Long id;
private String someSetting;
public String getSomeSetting() {
return someSetting;
}
}
A Spring Data repository for easy database access:
public interface DbConfigRepository extends JpaRepository<DbConfig, Long> {
}

Related

Why setters are mandatory for fields in a class which reads properties from application.yml file in springboot?

Following is my code:
Why setter is mandatory. Without it, the class does not
read the property from the
application.yml file
correctly.
Thank you.
#Getter
#Setter
#NoArgsConstructor
#Configuration
#ConfigurationProperties(prefix = "test")
#EnableConfigurationProperties
public class KafkaTopicConfig {
private String bootstrapAddress;
#Value(value = "${test.bootstrapAddress}")
private String bootstrapAddressFromVariable;
should only use #Value in encapsulated components/services (we can call them configuration services).
This way, we will have all our configurations in one place, and that component will only have the responsibility of loading and providing them to other components.
https://stackabuse.com/the-value-annotation-in-spring
From baeldung.com... The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.
So it looks like you're using Lombok so I would make my class look more like this:
#ConfigurationProperties(prefix = "test")
#Data
public class KafkaTopicConfig {
private String bootstrapAddress;
}
Then in the main spring boot application class or a #Configuration class I would do:
#Configuration
#EnableConfigurationProperties({KafkaTopicConfig.class})
public MyApplicationConfig{
}
Then to use my configuration properties I would autowire it into the #Component where I wished to use it.
e.g.
#Component
public MyComponent{
private final KafkaTopicConfig config;
public MyComponent(KafkaTopicConfig config) {
this.config = config;
}
public void doStuff() {
if ("some address".equals(config.getBootstrapAddress())) {
blah();
}
}
}
Using the #Value inside the configuration properties feels confusing to me, and defeats the point of using configuration properties in the first place.

How to declare multiple object with same class but using different properties in spring boot

I want declare multiple object using same class but different properties in Spring boot annotation
application.properties
test1.name=Ken
test2.name=Anthony
the code example
#Component
public class People {
private String name;
public String getName() {
return this.name;
}
}
#SpringBootApplication
public class Application {
#AutoWired
public People man1;
#AutoWired
public People man2;
System.out.println(man1.getName());
System.out.println(man2.getName());
}
I try to add #ConfigurationProperties(prefix="test1") before declare man1
but it returned
The annotation #ConfigurationProperties is disallowed for this location
#ConfigurationProperties is only allow to be placed on the #Bean method in the #Configuration class or at the class level. For the former case , it will map the properties from the application.properties to the bean instance , which means you have to :
#SpringBootApplication
public class Application {
#Bean
#ConfigurationProperties(prefix="test1")
public People man1() {
return new People();
}
#Bean
#ConfigurationProperties(prefix="test2")
public People man2() {
return new People();
}
}
And since both man1 and man2 are the same type , you have to further use #Qualifier to tell Spring which instance you actually want to inject by specifying its bean name. The bean name can be configured by #Bean("someBeanName"). If #Bean is used without configuring the bean name, the method name will be used as the bean name. (i.e. man1 and man2)
#Autowired
#Qualifier("man1")
public People man1;
#Autowired
#Qualifier("man2")
public People man2;

How to autowire SimpleJpaRepository in a spring boot application?

I'm working on a project that uses SpringBoot 2.0.5 version, Spring Data JPA to persists and retrieve records using JPA. I autowired SimpleJpaRepository in the service layer. But while starting my application, it failed with
"NoSuchBeanDefinitionException"- No qualifying bean of type
'org.springframework.data.jpa.repository.support.SimpleJpaRepository<?, ?>'
available: expected at least 1 bean which qualifies as autowire candidate.
My controller, service and DAO are like below
Controller class:
#Controller
public class MyController{
#Autowired
private MyService<Person,PersonPK> service;
Service layer as
public interface MyService<V,K>{
methods defined
}
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private SimpleJpaRepository<V,K> repository; // This dependency is failing
}
Application as :
#SpringBootApplication (exclude = {SecurityAutoConfiguration.class})
#EnableJpaRepositories
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Is my approach incorrect? Is that not the correct way of autowiring the SimpleJpaRepository.
There is no need for me to extend SimpleJpaRepository as Spring provided JPARepository is good for me, for now.
Thanks
You still need to create a repository interface that extends JpaRepisitory, or the spring repository type of your choice.
To quote the spring data documentation:
1.2.1 Defining repository interfaces
As a first step you define a domain class-specific repository interface. The interface must extend
Repository and be typed to the domain class and an ID type. If you
want to expose CRUD methods for that domain type, extend
CrudRepository instead of Repository.
Once you do create a new repository type, you will autowire by that type rather than the SimpleJpaRepository.
One way to get an implementation of the SimpleJpaRepository is by using a Configuration class to create an instance as a bean that you will use inside your service.
#Configuration
public class PersistanceConfiguration {
#PersistenceContext
private EntityManager entityManager;
#Bean
public SimpleJpaRepository<YourEntity, Long> getYourEntitySimpleRepository() {
return new SimpleJpaRepository<>(YourEntity.class, entityManager);
}
}
And inject it to your service as you would do with a JpaRepository, for example:
#Service
public class YourEntityServiceImpl<YourEntity, Long> implements YourEntityService {
private JpaRepository<YourEntity, K> repository;
private SimpleJpaRepository<YourEntity, K> simpleRepository;
#Autowired
public YourEntityServiceImpl(YourEntityRepository repository, SimpleJpaRepository<YourEntity, Long> simpleRepository) {
this.repository = repository;
this.simpleRepository = simpleRepository;
}
}
You should create a repository interface that extending JpaRepisitory.
#Repository
public interface MyRepository extends JpaRepisitory<T, ID> {
//
}
And the you should Autowired in your service class.
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private MyRepository myRepository;
}

Spring constructor injection using java config

I have a Class that accepts the following constructor
public Student(int id, String name, Map<String, List<String>> mapInject) {
super();
this.id = id;
this.name = name;
this.mapInject = mapInject;
}
And from spring Java Config, I am injecting the constructor args like below..
#Configuration
public class JavaConfig {
#Bean
public Employee getEmployeeBean() {
Map<String,List<String>> mapInject = new HashMap<String,List<String>>();
//Add map element
return new Employee(3123,"John",mapInject);
}
}
Am i doing constructor injection here? Is this the right way to do so?
I wouldn't use Spring to handle this bean creation, unless you want ALL employees to have the same id and name which I doubt.
The power behind Spring is its Dependency Injection (DI) where you define beans for providers such as database managers, services, etc. and inject those into your components. Defining a #Bean like you have there serves no purpose as now you can only inject employees with an id of 3123 and name John.
It's important to understand that just because you are using Spring it doesn't mean EVERYTHING needs to be handled as a bean - you will always need standard POJOs for housing and passing around state (such as your Employee class) which doesn't need to have anything to do with Spring.
Down the line you might have an EmployeeService for example which houses business logic to fetch employees from a database or something, this could then be configured as a bean so it can be injected across the application.
EDIT
#Configuration
public class JavaConfig {
#Bean
#Autowired //assuming a sessionfactory been is configured elsewhere
public EmployeeService employeeService(final SessionFactory sessionfactory) {
return new EmployeeService(sessionFactory);
}
}
You could then inject this anywhere (maybe in a controller for example):
#RestController
public class EmployeeController {
private final EmployeeService employeeService;
#Autowired
public EmployeeController(final EmployeeService employeeService) {
this.employeeService = employeeService;
}
}
Where the EmployeeController doesn't need to know or care that the userService has a DB connection and doesn't need to worry about configuring it as Spring will handle all of that.

Spring: autowired service in a component is null

I have created a service:
package tn.ett.medial.service;
#Service
public class ExchangeService {
private Currency EURCurrency;
public Currency getEURCurrency() {
....
return EURCurrency;
}
and a component
package tn.ett.medial.utils.dto;
#Component
public class ProductDTO implements Serializable {
#Autowired
ExchangeService exchangeService;
public ProductDTO() {
}
public ProductDTO(Product product){
System.out.println("service****" + exchangeService);
Currency EURCurrency = exchangeService.getEURCurrency();
}
}
I added the component-scan tag in my application context
<context:component-scan base-package="tn.ett.medial" />
Why the exchangeService is null? (although it works when I inject it into a #Controller).
Since this is a DTO, I guess you do something like ProductDTO productDTO = new ProductDTO();.
So annotated #Autowired ExchangeService is null because Spring doesn't know about the copy of ProductDTO that you created with new and didn't know to autowire it.
Some more info
You run this code:
System.out.println("service****" + exchangeService);
Currency EURCurrency = exchangeService.getEURCurrency();
in a constructor, that is not autowired. No wonder it can not autowire bean, since it is not a bean itself.
The idea of IoC is that Spring Container is creating beans itself.
If you want Spring to use a specific constructor, you have to Autowire it like this:
package tn.ett.medial.utils.dto;
#Component
public class ProductDTO implements Serializable {
private final ExchangeService exchangeService;
public ProductDTO() {
}
#Autowired
public ProductDTO(Product product, ExchangeService exchangeService){
this.exchangeService = exchangeService;
System.out.println("service****" + exchangeService);
Currency EURCurrency = exchangeService.getEURCurrency();
}
}

Resources