Disable / remove spring boot datasource by profile - spring

Using spring boot yaml config, I have a datasource that looks like this:
datasource:
url: jdbc:postgresql://somehost/somedb
username: username
password: password
hikari:
connection-timeout: 250
maximum-pool-size: 1
minimum-idle: 0
I can succesfully point to different DBs based on profile, but I'd like to setup a profile that does not use this datasource at all. When I use that profile, however, I get this:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified and no embedded datasource could be auto-configured.
Reason: Failed to determine a suitable driver class
How do I use this datasource in some profiles, but not in others?

You can skip the bean for specific profiles using `#Profile("!dev") annotation
profile names can also be prefixed with a NOT operator e.g. “!dev” to exclude them from a profile
from docs here
If a given profile is prefixed with the NOT operator (!), the annotated component will be registered if the profile is not active — for example, given #Profile({"p1", "!p2"}), registration will occur if profile 'p1' is active or if profile 'p2' is not active.
Profiles can also be configured in XML – the tag has “profiles” attribute which takes comma separated values of the applicable profiles:here
<beans profile="dev">
<bean id="devDatasourceConfig"
 class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>

Change to:
spring:
datasource:
url: jdbc:postgresql://somehost/somedb
username: username
password: password
hikari:
connection-timeout: 250
maximum-pool-size: 1
minimum-idle: 0
Springboot works with Autoconfiguration by default, but you can customize excluding some AutoConfiguration classes
Edit your configuration to skip AutoConfiguration:
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
Make your own datasource by profile
#Bean
#Profile("dev")
DataSource dataSourceDevProfile(org.springframework.core.env.Environment environment) throws Exception {
return DataSourceBuilder.create().url("").driverClassName("").password("").username("").build();
}
#Bean
#Profile("!dev")
DataSource dataSourceNoDev(org.springframework.core.env.Environment environment) throws Exception {
return DataSourceBuilder.create().url(environment.getProperty("spring.datasource.url")).driverClassName("").password(environment.getProperty("spring.datasource.password")).username(environment.getProperty("spring.datasource.username")).build();
}
Or Totally Programatically
#Bean
DataSource dataSource2(org.springframework.core.env.Environment environment) throws Exception {
if (environment.acceptsProfiles("dev")){
//return datasource dev
}else{
//return datasource prod
}

Related

springboot quartz init schema only on first startup

This is my config:
#Bean
#QuartzDataSource
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource quartzDataSource() {
return DataSourceBuilder.create().build();
}
and this is my app.yml:
datasource:
url: my-url
jdbcUrl: ${spring.datasource.url}
username: 'root'
password: 'root'
...
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: always
wait-for-jobs-to-complete-on-shutdown: true
properties:
org:
quartz:
dataSource:
quartz-data-source:
provider: hikaricp
driver: com.mysql.cj.jdbc.Driver
URL: ${spring.datasource.url}
user: ${spring.datasource.username}
password: ${spring.datasource.password}
maximumPoolSize: 5
connectionTestQuery: SELECT 1
validationTimeout: 5000
idleTimeout: 1
scheduler:
instanceId: AUTO
instanceName: my-project-scheduler
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
useProperties: false
misfireThreshold: 60000
clusterCheckinInterval: 30000
isClustered: true
dataSource: quartz-data-source
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 1
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
My question:
If I set initialize-schema: always then the qrtz tables are created on each application startup.
On the other side, if I set initialize-schema: never then I get an error on the first startup that the qrt tables are missing.
Is there a way to configure it to initialize the qrtz tables only if they do not exist?
You are gonna need a migration tool to handle the database creation.
Spring Boot provides two options: Flyway and LiquidBase.
Choose one, create migration scripts and you are up and running.
I personally like the Flyway approach.
You just add implementation 'org.flywaydb:flyway-core' to your build.gradle file (or the maven alternative).
Then add this to your application.yml
spring:
flyway:
enabled: true
baseline-on-migrate: true
Then create db/migration folder in resources folder and put in your migration scripts eg. V1_0_0__db_init.sql (flyway has its own naming convention).
To get the create SQL scripts I recommend that you export them from running database.
Also do not forget to change the spring.jpa.hibernate.ddl-auto to validate.

R2DBC and Spring Data integration issues

I have spring boot project (version 2.4.6) with spring data dependency (spring-boot-starter-data-jpa) and postgreSQL driver.
In project we are using Hibernate and data repositories, which configured via:
#EnableSpringDataCommons(basePackages = ["..."]) // path to folder with repositories
#EnableJpaAuditing(auditorAwareRef = "auditorAware")
#EnableTransactionManagement
#Configuration
class PersistenceConfig
And also I want to add reactive R2DBC. My plan is to use it in one specific place, where we have integration with other system, such communication happened via reactive data streaming. According on it, I need to update some state in database reactivly. That's why I'm added next dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
<version>2.4.6</version>
</dependency>
dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
And also, next properties configuration:
spring:
name: configuration
url: jdbc:postgresql://${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DBNAME}
username: ${POSTGRES_USERNAME}
password: ${POSTGRES_PASSWORD}
driverClassName: org.postgresql.Driver
hikari:
maximum-pool-size: 2
jpa:
database: POSTGRESQL
database-platform: org.hibernate.dialect.PostgreSQLDialect
generate-ddl: false
open-in-view: false
properties:
javax:
persistence:
validation:
mode: auto
hibernate:
temp:
use_jdbc_metadata_defaults: false
jdbc:
time_zone: UTC
lob.non_contextual_creation: true
hibernate:
ddl-auto: none
r2dbc:
url: r2dbc:postgresql://${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DBNAME}
username: ${POSTGRES_USERNAME}
password: ${POSTGRES_PASSWORD}
liquibase.change-log: classpath:/db/changelog-master.xml
And finally, I have such data-layer logic service:
#Service
class MyDataReactiveService(
val operator: TransactionalOperator,
val template: R2dbcEntityTemplate
) {
fun updateObjectStatus(state: String, objectId: UUID): Mono<Int> =
template
.update(ObjectEntity::class.java)
.matching(query(Criteria.where("id").`is`(objectId)))
.apply(update("state", state))
.`as` { operator.transactional(it) }
}
Where ObjectEntity - regular spring data entity.
But, unfortunately, I have next error while application startup (inside tests):
Field objectRepository in com.slandshow.TestManager required a bean named 'entityManagerFactory' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
TestManager - Test wrapper with injected beans:
#Service
class TestManager {
#Autowired
lateinit var objectRepository: ObjectRepository
...
}
And ObjectRepository:
interface ObjectRepository : JpaRepository<ObjectEntity> {
...
}
As far as I understand, such issue related to r2dbc and spring data misconfig.
But how can I fix it?
Since you did not post the code of ObjectRepository it’s hard to say what is wrong. However, I do not recommend to use JPA and R2DBC in same project for the same database..it’s a hassle and furthermore this may not give you any advantage. Instead I would recommend to use WebClient to make HTTP calls and use Kotlin Coroutine to fire query in dedicated thread (since you are using Kotlin already). In my opinion this will be better approach. However all this depends on your application i.e. how many queries you are firing after calls and so forth.

Spring Test Wants To Connect to Database

I am trying to write some tests for my application and encountered following problem:
I defined a application-test.yml with folling content:
server:
port: 8085
spring:
security:
oauth2:
resourceserver:
jwt:
# change localhost:8081 with container name
issuer-uri: http://localhost:8081/auth/realms/drivingschool
jwk-set-uri: http://localhost:8081/auth/realms/drivingschool/protocol/openid-connect/certs
keycloak:
realm: drivingschool
auth-server-url: http://localhost:8081/auth
ssl-required: external
resource: client-interface
use-resource-role-mappings: true
credentials:
secret: xxx
bearer-only: true
My test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("test")
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private StudentService service;
#MockBean
private StudentRepository repository;
#Test
public void contextLoads(){}
//more tests
}
the test all pass green BUT in the log i can see, that my app tries to connect to a database configured in my (basic) application.yml.
ava.sql.SQLNonTransientConnectionException: Could not connect to address=(host=localhost)(port=9005)(type=master) : Socket fail to connect to host:localhost, port:9005. Verbindungsaufbau abgelehnt (Connection refused)
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73) ~[mariadb-java-client-2.6.1.jar:na]
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:192) ~[mariadb-java-client-2.6.1.jar:na]
jpa:
database-platform: org.hibernate.dialect.MariaDBDialect
hibernate:
use-new-id-generator-mappings: false
ddl-auto: create
datasource:
url: jdbc:mariadb://localhost:9005/waterloo
username: waterloo
password: xxx
driver-class-name: org.mariadb.jdbc.Driver
when creating a application-prod.yml and moving all content from application.yml to application-prod.yml it tells me I have to configure a datasource URL
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
I have the following questions:
Do the application.yml files get layered (*-test.yml settings on top of application.yml)?
Why does Spring try to build a connection to my database when I am not setting a datasource on my application-test.yml AND mocking the repository on the test?
Is it normal that Spring trys to establish a connection at this part?
3.1) If not: How to i prevent it from doing so?
Thanks and kind regards!
Failed to determine a suitable driver class
You need to add mariadb driver dependency to your gradle or maven file.
https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client/2.6.2
Make sure that dependency scope is suitable for test
If you already have and its still not working try to clean and rebuild your project.
Your Questions:
Do the application.yml files get layered (*-test.yml settings on top
of application.yml)?
If you add #ActiveProfiles("test") to you TestClass Spring will try to find an application-test.yml and overrrides application.yml properties with the given properties
Why does Spring try to build a connection to my database when I am not
setting a datasource on my application-test.yml AND mocking the
repository on the test?
Thats the magic of spring boot - it has default configurations for everything. You just need to set the Datasource properties and it will create the bean by itself.
Is it normal that Spring trys to establish a connection at this part? 3.1) If not: How to i prevent it from doing so?
You are starting the whole spring context with #SpringBootTest Annotation.
So it will startup all Repositories and try to establish connection to your database. If you don't want spring to startup the database layer you can just use #WebMvcTest
eg:
#RunWith(SpringRunner.class)
#WebMvcTest
#ActiveProfiles("test")
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#Test
public void contextLoads(){}
//more tests
}
Check this out: https://spring.io/guides/gs/testing-web/
If you need to startup the whole SpringContext you can also disable Spring Data AutoConfiguration with:
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
Check this out: https://www.baeldung.com/spring-data-disable-auto-config
missed following annotation:
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})

Read values from yaml file

I have the following issue. I create a data source based on a value I read in the yaml file based on a given profile.
Here is my code
#Value("${my.db.serviceId}")
private String serviceId;
#Primary
#Bean(name = "prodDataSource")
#Profile("prod")
public DataSource prodDataSource() {
return getDataSource(serviceId);
}
#Bean(name = "devDataSource")
#Profile("dev")
public DataSource devDataSource() {
return getDataSource(serviceId);
}
Here is my yaml file
---
spring:
profile: dev
my:
db:
serviceId: 'my-dev-service'
---
spring:
profile: prod
my:
db:
serviceId: 'my-prod-service'
---
My current issue is that when I start my application with the "dev" profile,
the value of the serviceId is 'my-prod-service'.
What am I doing wrong here?
#Primary annotation enables a bean that gets preference when more than one bean is qualified to autowire a single valued dependency
So the bean with #Primary annotation will get more preference
so I finally realized that in the yaml file I put "profile" instead of "profiles". that's why it wasn't picking up my profile.
I endeded up changing to:
---
spring:
profiles: dev
my:
db:
serviceId: 'my-dev-service'
---
spring:
profiles: prod
my:
db:
serviceId: 'my-prod-service'
---

Spring Cloud Config Server issue - Configuring multiple sources native and jdbc

I want to connect to multiple repositories i.e native (file system) and jdbc in spring cloud config. I created a spring cloud config server with below details
application.properties
server.port=8888
spring.profiles.include=native,jdbc
spring.cloud.config.server.native.search-locations=classpath:/config,classpath:/app1, classpath:/app2,classpath:/ep
encrypt.key=abcdef
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/configuration?useSSL=false
spring.cloud.config.server.jdbc.sql=SELECT properties.key, properties.value from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?
spring.datasource.username=root
spring.datasource.password=root
spring.cloud.config.server.native.order=1
spring.cloud.config.server.jdbc.order=2
Irrespective of priority order it always fetches information from jdbc and not from native.
I tried adding the last 2 properties for order to bootstrap.properties still same behavior.
Am is missing anything ? Is my configuration correct ? Please suggest
in spring boostrap.yml loaded before application.yml so you declare server port,config search location and active profile configuration is good approach for this stack,so keep it simple boostrap.yml also spring cloud default profile is native
and in application-"profile".yml is have environment and other configuration properties
and your boostrap.yml or properites like that
server:
port: 8888
spring:
application:
name: appName
profiles:
active: native,jdbc
cloud:
config:
server:
native:
order: 1
searchLocations: classpath:/config,classpath:/app1, classpath:/app2,classpath:/ep
and create applicaiton-jdbc.properties or yml file in same layer in boostrap.yml or properties and declare jdbc properties
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: 'jdbc:mysql://localhost:3306/configuration?useSSL=false'
cloud:
config:
server:
jdbc:
order: 2
sql: 'SELECT properties.key, properties.value from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?'
username: root
password: root
and your config server configuration like this
#SpringBootApplication
#EnableConfigServer
#Import({JdbcEnvironmentRepository.class})
public class ConfigServer {
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
public static void main(String[] arguments) {
SpringApplication.run(ConfigServer.class, arguments);
}
}

Resources