SpringBoot Datasource AutoConfiguration Not Working - spring-boot

I have a simple SpringBoot application and I'd like to use AutoConfiguration to configure the Tomcat jdbc pooled data sources.
I am using these Spring dependencies:
// Spring Boot
compile 'org.springframework.boot:spring-boot-starter-web:1.3.5.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-jdbc:1.3.5.RELEASE'
Here are my datasource properties in my application.yml file:
spring:
datasource:
url: jdbc:mysql://my.host/mydb
username: user
password: pwd
driver-class-name: com.mysql.jdbc.Driver
initialSize: 5
I am sure the properties are being loaded because the app is picking up other values.
I define the bean in my config file as:
#Bean(name="myDataSource")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource getDataSource() {
DataSource dataSource = DataSourceBuilder.create().build()
return dataSource
}
And I inject the datasource into my DAO like this:
#Slf4j
#Repository
class MyDAO {
#Autowired
DataSource dataSource
public void getFoo() {
log.info("DB URL: ${dataSource.getUrl()}")
}
}
If I set a breakpoint in the getDataSource() method, the DataSourceBuilder will create an instance of DataSource. However, all the properties of that object like URL, user and password are all null. Also, when I call getFoo(), the dataSource variable is null. I have tried commenting out the bean definition in my AppConfig. The dataSource is still null. Any suggestions?
I looked through the Spring Boot documentation and my Spring book but I didn't see any examples like this. I see examples where I create the DataSource myself. But I was hoping Spring's auto-configuration would tie this stuff together automatically.
Thanks in advance for any help you can provide.

By creating your own bean, you're actually switching off Boot's auto-configuration of a DataSource. You can just delete your getDataSource method and let Boot auto-configure one instead.

Based on Andy's comments I found out that I had two problems. First of all, I needed to include the JPA dependency to the project. I added this line to my build.gradle file:
compile 'org.springframework.boot:spring-boot-starter-data-jpa:1.3.5.RELEASE'
Second, I was creating instances of MyDAO using new(). I fixed this by creating a service class that used #Autowired to inject an instance of MyDAO. Once the DAO became a Spring managed bean, it was able to inject the instance of DataSource from the Tomcat connection pool.

Related

spring boot hikari config not loaded

I'm using spring boot 2.6.7 and hikari pool.
I'm trying to use autoconfigure ans setting :
spring:
datasource:
url: "jdbc:postgresql://localhost:5432/bdd"
username: username
password: pass
type: app.CustomHikariDatasource
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 3
minimum-idle: 3
register-mbeans: true
But when application is launch pool is always 10 (default hikari value)
I try to disable autoconfiguration (DataSourceAutoConfiguration.class) and create my datasource manually like this:
#Bean
#Primary
public DataSource dataSource(HikariConfig hikariConfig) {
return new CustomHikariDatasource(hikariConfig);
}
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariConfig hikariConfig() {
return new HikariConfig();
}
but whe application start i got :
org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [HikariDataSource (HikariPool-1)] with key 'dataSource'; nested exception is javax.management.InstanceAlreadyExistsException: MXBean already registered with name com.zaxxer.hikari:type=PoolConfig (HikariPool-1)
If i put a breakpoint in HikariConfig constructor, i effectivly pass 2 times in it!
If i disable register-mbeans: false it work, but i need to have mbeans activated!
2 ways to find a solution:
first, can someone explain to me why my autoconfigure not taking hikariconfig properties and perhaps fix the problem?
secondly, if first point is not working, why spring boot instantiate 2 times hikariconfig when i disable autoconfiguration? does i need to disable something else?
thanks for helping me.
#EDIT1
To disable default mbean exporter i follow this issue :
https://github.com/brettwooldridge/HikariCP/issues/342
And with doing this:
#Bean
public MBeanExporter exporter() {
MBeanExporter exporter = new MBeanExporter();
exporter.setAutodetect(true);
exporter.setExcludedBeans("dataSource");
return exporter;
}
I don't have exception anymore and my mbean is correctly exposed!
#EDIT2
For the first point i think it's because i'm using CustomHikariDatasource and in the code of DatasourceConfiguration i can see :
#Configuration(proxyBeanMethods = false)
#ConditionalOnClass(HikariDataSource.class)
#ConditionalOnMissingBean(DataSource.class)
#ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
so because my spring.datasource.type isn't com.zaxxer.hikari.HikariDataSource, spring don't instanciate it in the right way, so i decided to disable autoconfiguration and instantiate my CustomHikariDatasource manually and with the solution found in the Edit1, all seems to be working.
As mentioned in this issue this is due to a change in Hikari which now leads to Spring detecting it as an MBean and tries to export it as such. However as you enabled jmx export on Hikari as well there is already a bean.
The initial thought might be to disable the JMX export in Hikari by specifying this in your config.
spring.datasource.hikari.register-mbeans=false
However according to the earlier mentioned issue this will only register the datasource in JMX. While Hikari itself exports two in JMX.
I suggest to add the #EnableMBeanExport annotation to an configuration class and override the behavior when it detects an existing bean. The default is to throw an exception, but you could configure it to ignore the exception and keep the original bean.
#EnableMBeanExport(registration=IGNORE_EXISTING)
This will enable JMX exporting (as it was already enabled) and keep the Hikari registered ones.
Another option would be to register both and ignore the exception and generate a new name. You can do this by specifying this in your application.properties.
spring.jmx.unique-names=true
Which should generate a new name for the Spring registered one. Drawback is that you now have the same bean twice in JMX. Advantage you don't have to override any Spring configuration.

Creating a custom FactoryBean in Sprint Boot 2.3/Spring 5

I've got a spring-boot web application that's mostly working; my DataSource is properly configured by an external application.properties file.
Now I want to add properties to that file to help me instantiate and configure two instances of a class in my app. I have a APNsFactory that I currently instantiate manually and configure using JNDI, but I want to get away from JNDI calls:
#Bean
public
APNsFactory
apnsFactory()
throws
javax.naming.NamingException
{
sLogger.info("Configuring APNsFactory");
InitialContext ctx = new InitialContext();
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath((String) ctx.lookup("java:comp/env/apns/prod/keystorePath"));
f.setProductionKeystorePassword((String) ctx.lookup("java:comp/env/apns/prod/keystorePassword"));
f.setDevelopmentKeystorePath((String) ctx.lookup("java:comp/env/apns/dev/keystorePath"));
f.setDevelopmentKeystorePassword((String) ctx.lookup("java:comp/env/apns/dev/keystorePassword"));
return f;
}
When running before in a standalone webapp container, Spring properly called that method and the JNDI context from the container’s <env-entry> tags was available.
I'm trying to update my APNsFactory to be a proper Spring FactoryBean<>, and I’ve given it a couple of #Autowire String variables that I want to be set by Spring Boot from the application.properties file.
For bonus points, I want this to be usable both in Spring Boot and in a standalone container like Tomcat or Resin.
For the life of me, I can't figure out how to get Spring to do this. There are dozens of examples for DataSources and other Beans already implemented by Spring, but none for a completely custom one, using application.properties, in a Spring Boot web environment.
I've seen some examples that use an XML config file, but I'm not sure how to do that with Spring Boot.
I don't think you need a factory bean here.
You already have spring boot that can read application.properties out-of-the-box:
So try the following:
Create key/values in the application.properties file:
myapp.keystore.path=...
myapp.keystore.passwd=...
// the same for other properties
Create ConfigurationProperties class
#ConfigurationProperties(prefix="myapp.keystore")
public class MyAppKeyStoreConfigProperties {
private String path; // the names must match to those defined in the properties file
private String passwd;
... getters, setters
}
In the class marked with #Configuration (the one where you create #Bean public APNsFactory apnsFactory()) do the following:
#Configuration
// Note the following annotation:
#EnableConfigurationProperties(MyAppKeyStoreConfigProperties.class)
public class MyConfiguration {
// Note the injected configuration parameter
#Bean public APNsFactory apnsFactory(MyAppKeyStoreConfigProperties config) {
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath(config.getKeyPath());
and so on
}
}
I've intentionally didn't show the separation between production/dev stuff.
In spring boot you have profiles so that the same artifact (WAR, JAR whatever) can be configured to run with different profile and depending on that the corresponding properties will be read.
Example:
If you're running with prod profile, then in addition to application.properties that will be loaded anyway, you can put these keystore related definitions to application-prod.properties (the suffix matches the profile name) - spring boot will load those automatically. The same goes for dev profile of course.
Now I haven't totally understand the "bonus points" task :) This mechanism is spring boot proprietary way of dealing with configuration. In "standalone" server it should still have a WAR with spring boot inside so it will use this mechanism anyway. Maybe you can clarify more, so that I / our colleagues could provide a better answer

spring-boot-starter-quartz specify different db connector?

I have a spring boot application that is working correctly
with quartz job that is working correctly
all quartz related tables are created on FOO schema that is specified in the application.properties
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
how do I specify a different DB schema only for the quartz objects?
To use another datasource for quartz-schedulers you have to create a bean data-source annotated with #QuartzDataSource
#Bean
#QuartzDataSource
public DataSource dataSource() {
...
}
To have Quartz use a DataSource other than the application’s main DataSource, declare a DataSource bean, annotating its #Bean method with #QuartzDataSource. Doing so ensures that the Quartz-specific DataSource is used by both the SchedulerFactoryBean and for schema initialization.
Related docs: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-quartz.html

Spring Boot and Spring Session: How to control the DataSource

I'm experimenting with Spring Boot and Spring session together, specifically using JDBC.
Just adding the line in application.properties:
spring.session.store-type=jdbc
made it just work, which is nice because I happen to also have some data source properties in that file, ie
myapp.datasource.url=jdbc:mysql://localhost/etc...
myapp.datasource.driver-class-name=com.mysql.jdbc.Driver
But I'm actually using those for my own data source with my own configuration, like so:
#Configuration
#PropertySource("classpath:credentials.properties")
public class DataSourceConfig {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "myapp.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
and as far as I can tell, Spring Session is creating its own data source instead of using mine. Is there a way I can get it to use mine instead?
(my real data source has some additional configs with Hikari not shown here)
Spring Session itself does not create DataSource but rather uses the one present in your application context, if it's the either:
the only DataSource bean
DataSource marked as #Primary
Also if you wish to use a specific DataSource for Spring Session (for example, if you have multiple DataSources in your application) you can do that by:
annotating DataSource marked as designated for Spring Session by #SpringSessionDataSource (Spring Session 2.0 onwards)
providing JdbcTemplate bean that uses the desired DataSource and naming it springSessionJdbcOperations (Spring Session 1.x)
The Spring Session JDBC configuration capabilities and logic should be quite easy to understand from the JdbcHttpSessionConfiguration.

Using #ConfigurationProperties in Spring Boot Application doesn't work

I am using Spring Boot V 1.4.1 for a new application.
My app requires two JDBC data sources and I was following the example at http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-two-datasources how to set it up.
My Spring beans configuration class is annotated with #EnableConfigurationProperties and my first bean is defined as
#Primary
#Bean
#ConfigurationProperties(prefix = "first.database")
DataSource qivsDB() {
return DataSourceBuilder.create().build();
}
, the second one accordingly. My application.properties file has properties defined like
first.database.url=jdbc:[redacted]
first.database.username=[redacted]
first.database.password=[redacted]
For reasons I not transparent to me during debugging this is failing to initialize: Cannot determine embedded database driver class for database type NONE - debug showed me that the builder does not have any properties set when calling build().
What did I miss here?
Before you do all the debugging part, you should have a look to the auto-configuration report. If you define your own DataSource there's no reason for Spring Boot to start looking at what it can do for your app. So, for some reasons, that definition of yours is not applied in your app and the default in Spring Boot still applies, doesn't find any JDBC url in the default namespace and attempt to start an embedded database. You should see in the auto-config report that the DataSourceAutoConfiguration still matches.
I am not sure the public keyword has anything to do with it, though you won't get custom meta-data for that key since we only scan for public methods.

Resources