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.
Related
I have a specific requirement where in i need to fetch the database password (spring.datasource.password=) from a secure vault through a web service call.
But the problem is that when i start my spring boot application it tries to make a Hikari connection pool.
How can i acheive this ?
Option one
You can override Spring's autoconfiguration by creating DataSource bean in your configuration.
#Bean
public DataSource dataSource(HikariConfig hikariConfig) {
return new HikariDataSource(hikariConfig);
}
#Bean
#ConfigurationProperties(prefix = "prefix")
public HikariConfig hirariConfig() {
HicariConfig config = new HikariConfig();
// password retrieve logic
config.setPassword(password);
return config;
}
Option two
If you want to utilize Spring's autoconfigured DataSource, you can also use Spring Cloud Vault, which can lookup properties in Vault. Here are some tutorials which may help you to get started: https://spring.io/projects/spring-cloud-vault, https://spring.io/guides/gs/vault-config/.
Other options
You can inject your password as an environment variable. This variable should be named SPRING_DATASOURCE_PASSWORD. Or you can create your own PropertySource which will supply needed variable.
You can find more about this option in Spring blog.
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.
Is #EnableTransactionManagement required in Spring Boot?
I did some research. Some folks say you don't need it, as Spring Boot has it already enabled, others say you do have to use it explicitly. So how is it?
Probably you're also using Spring Data. Calls on Spring Data repositories are by default surrounded by a transaction, even without #EnableTransactionManagement. If Spring Data finds an existing transaction, the existing transaction will be re-used, otherwise a new transaction is created.
#Transactional annotations within your own code, however, are only evaluated when you have #EnableTransactionManagement activated (or configured transaction handling some other way).
You can easily trace transaction behavior by adding the following property to your application.properties:
logging.level.org.springframework.transaction.interceptor=TRACE
(see Showing a Spring transaction in log)
According to > https://spring.io/guides/gs/managing-transactions/
Spring Boot will detect spring-jdbc on the classpath and h2 and will create a DataSource and a JdbcTemplate for you automatically. Because such infrastructure is now available and you have no dedicated configuration, a DataSourceTransactionManager will also be created for you: this is the component that intercepts the #Transactional annotated method.
You can also use spring-boot-starter-actuator to list your beans created in your context and you will find it
bean": "transactionManager"
Little old post but the answers given previously were not straight forward when I was searching for it.
#EnableTransactionManagement is optional in Spring boot, provided that spring-data* or spring-tx are found in classpath. How it works? As below:
Spring boot adds a spring-boot-autoconfigure.jar in the classpath. Go to the META-INF's spring.factories file and you can see org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration entry there. This initializes the transaction auto configuration for you.
Note that the class has following lines: (snippet)
#Configuration
#ConditionalOnClass({PlatformTransactionManager.class})
#AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
#EnableConfigurationProperties({TransactionProperties.class})
public class TransactionAutoConfiguration {
..
}
Have a look at TransactionAutoConfiguration to see that it enables transaction support if the PlatformTransactionManager is available in classpath. EnableTransactionManagementConfiguration is also configured there.
No. #EnableTransactionManagement is on by default, see that: https://github.com/jkubrynski/spring-boot/commit/9d219ef7a004c58a88bbbef82a520a22961c9402
#EnableTransactionManagement is conditionally turned on/off based of the dependency jars we add in the classpath. If we use spring data jpa starter it is turned on.
In the class org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, there is such code(Spring Boot 1.5+):
#Configuration
#EnableTransactionManagement(proxyTargetClass = false)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
#Configuration
#EnableTransactionManagement(proxyTargetClass = true)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
The default is spring.aop.proxy-target-class=true, enabling CGLIB proxy by default.
If you want to use JDK proxy, set spring.aop.proxy-target-class=false instead.
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.
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.