Configuring data source URL in Spring without XML - java

I have a simple Web MVC application using Spring Boot that communicates with a database; the DB is H2 and has been in memory until now. I want to change that, and thus use a jdbc:h2:file:... URL.
Up until now, I have not needed to add any XML to configure my application, and I'd prefer it to stay that way if possible. But I can't figure out how to specify a different JDBC URL. I obtained and inspected the data source by passing it to an #Bean method:
org.apache.tomcat.jdbc.pool.DataSource#745e6f01{ConnectionPool[
defaultAutoCommit=null;
defaultReadOnly=null;
defaultTransactionIsolation=-1;
defaultCatalog=null;
driverClassName=org.h2.Driver;
maxActive=100;
maxIdle=100;
minIdle=10;
initialSize=10;
maxWait=30000;
testOnBorrow=false;
testOnReturn=false;
timeBetweenEvictionRunsMillis=5000;
numTestsPerEvictionRun=0;
minEvictableIdleTimeMillis=60000;
testWhileIdle=false;
testOnConnect=false;
password=********;
url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;
username=sa;
validationQuery=null;
validationQueryTimeout=-1;
validatorClassName=null;
validationInterval=30000;
accessToUnderlyingConnectionAllowed=true;
removeAbandoned=false;
removeAbandonedTimeout=60;
logAbandoned=false;
connectionProperties=null;
initSQL=null;
jdbcInterceptors=null;
jmxEnabled=true;
fairQueue=true;
useEquals=true;
abandonWhenPercentageFull=0;
maxAge=0;
useLock=false;
dataSource=null;
dataSourceJNDI=null;
suspectTimeout=0;
alternateUsernameAllowed=false;
commitOnReturn=false;
rollbackOnReturn=false;
useDisposableConnectionFacade=true;
logValidationErrors=false;
propagateInterruptState=false;
ignoreExceptionOnPreLoad=false;
}
(newlines mine)
The setup of that bean seems rather intricate, so I want to interfere with it as little as possible - just replace the default JDBC URL.
How can I configure individual properties for Spring to create the datasource? Preferably in Java, but if there is a concise XML way I'm happy as well. I just want to avoid adding 100 lines of boilerplate for something equivalent to url=...

A DataSource is auto configured by Spring Boot for you. To influence how and what there are several properties you can set. Those are prefixed with spring.datasource, for a list take a look at the Spring Boot Reference Guide for a full list.
In your case simply add the following to the application.properties file
spring.datasource.url=jdbc:h2:file:...
This will tell Spring Boot to use this URL instead of the default.
As H2 is considered an in-memory database and not a regular database, when using JPA this will lead to your database to be dropped when the application is stopped. To fix this simply add the following
spring.jpa.hibernate.ddl-auto=update
To specify a dialect simply add the following
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
or even simpler
spring.jpa.database=H2

Related

how to add / read multiple data centers properties from spring boot / java

I'm developing an application where I will have 1 to N number of data centers details passed from properties file. for ex:..
myapp.ds1.domain.name=https://www.ds1.com
myapp.ds1.domain.username=us1
myapp.ds1.domain.password=pw1
myapp.ds2.domain.name=https://www.ds2.com
myapp.ds2.domain.username=us2
myapp.ds2.domain.password=pw2
.
.
.
.
myapp.dsn.domain.name=https://www.dsn.com
myapp.dsn.domain.username=usn
myapp.dsn.domain.password=pwn
I don't know how many fixed data centers information i will be getting, but what ever is there, I need to read it in my spring boot code.
I know if I want to read a particular value I can use
#Value("${myapp.ds.username:default}")
But this dynamic configuration, how to do it ? Any leads would be helpful
The #Value annotation is used to read the environment or application property value in Java code.
#Value("${spring.application.name}")
If the property is not found while running the application, Spring Boot throws the Illegal Argument exception as Could not resolve placeholder 'spring.application.name' in value "${spring.application.name}".
To resolve the placeholder issue, we can set the default value for the property using the syntax given below
#Value("${property_key_name:default_value}")
for example :-
#Value("${myproperty_key_name:mydefault_value}")
Please note You will use #Value to read the properties config file.

Configuration of thymeleaf text template in spring boot application.properties failed

I'm trying to configure a simple thymeleaf text template in my spring boot configuration and got to the point where the thymeleaf variable placeholder and the spring boot configuration property placeholder interfer (afaik both use the SpEL) when the template contains a colon (e.g. "[(${#dates.format(date, 'dd-MM-yyyy HH:mm')})]" as spring boot tries to resolve the variable "#dates.format(date, 'dd-MM-yyyy HH" but uses default value "mm')" instead.
I tried to change the spring boot prefix of the PropertySourcesPlaceholderConfigurer, but then some of my included libraries no longer work as they use the ${} variables
Is there a way to extend thymeleaf to treat %{} like ${} ?
I want to avoid replacing the template i read from the config, as i configure those templates on various properties and classes
ad1: For configuration i use a custom mechanism that converts a xml file into yaml which will then be used for configuration, so basically you can say its a application.yml configuration file. I cannot use custom template files in this scenario as the customer must be able to configure the template in a custom xml editor. The templates are all just a few words (like the subject of an email e.g.)
Thank you 62mkv, this seems to usually be the solution, thats why i marked it as the answer.
Anyways it wasn't the solution in my case, as it still didnt work, which is because of the way i load the config using a custom PropertySourceLoader
JAXBContext context = JAXBContext.newInstance(PICONFIG.class);
PICONFIG config = (PICONFIG) context.createUnmarshaller().unmarshal(resource.getInputStream());
YAMLFactory factory = new YAMLFactory();
//factory.disable(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID);
ObjectMapper mapper = new ObjectMapper(factory);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String yaml = mapper.writeValueAsString(config);
PropertySourcesPlaceholderConfigurer c;
return new YamlPropertySourceLoader().load(name,new ByteArrayResource(yaml.getBytes(StandardCharsets.UTF_8)));
I now decided to just use [(#{})] for my text templates and then replace the # in the setter of the configuration bean, which seems to work so far and wasn't to big of as issue as i refactored my code to use a common configuration class for the templates
You can protect your expression by wrapping certain placeholders, thus hiding them from Spring Boot PropertyResolver mechanism:
application.properties:
test-template=[(#{'$'}{#dates.format(date, 'dd-MM-yyyy HH:mm')})]
see full working code here: https://github.com/62mkv/spring-properties-thymeleaf.
This solution is "loaned" from here: https://github.com/spring-projects/spring-framework/issues/9628

Setting the Transaction Isolation Level through Spring Boot properties

I have a spring boot application which I have configured most of the properties through the properties file. However, I was looking if there is a way to set the TRANSACTION_ISOLATION_LEVEL through the Spring boot properties. Could someone help me on this.
I'm initializing the data source bean in the following way:
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return buildDataSource("spring.datasource");
}
private DataSource buildDataSource(String propPrefix) {
Stirng driverClassName = env.getProperty(propPrefix + ".driver-class-name");
return DataSourceBuilder.create()
.driverClassName(driverClassName)
.build();
}
Could someone please help me on how to specify the TRANSACTION_ISOLATION_LEVEL either through properties or during the data source initialization.
So, the javax.sql.DataSource does not provide a way to set default Transaction Isolation Level. Still, you can do it, but you must strict to particular implementation. Let me give you c couple of examples:
In case you use Apache BasicDataSource implementation of DataSource, then you can use this. This is for DBCP.
If you are using Apache BasicDataSource, but for DBCP2, you can do something like this.
But in most cases, if we are talking about Spring, we use Hikari Connection Pool. So, in case of HikariCP, you can use this method.
The same is applicable to Spring Boot. Let me explain - using Spring Boot properties file, you can set default transaction isolation level, but for specific DataSource, I mean this property:
spring.datasource.hikari.transaction-isolation
as you probably noticed, let you set default transaction isolation level for HikariCP (if you are using one) And this property:
spring.datasource.dbcp2.default-transaction-isolation
allow you to configure default isolation level for DBCP2.
Hope it helped, have a nice day! :)

How do I set java:comp/DefaultDataSource in WildFly?

According to the JEE spec, there should be a default data source provided to applications using java:comp/DefaultDataSource. WildFly out of the box will provide that data source as per the spec.
What I can't seem to find is a way of changing the value to point somewhere else without changing the java:comp/DefaultDataSource mapping on the application itself.
Under "Container" -> "JPA Subsystem" there's a Default DataSource which I have tried setting, but the connection still goes to the
java:jboss/datasources/ExampleDS
#Resource(name = "somedatasource", lookup = "java:comp/DefaultDataSource")
private DataSource ds;
One thing to note, I am not using JPA for the application, I just wanted to get the default data source and use raw JDBC calls as I am working with a lot of LOB data and dynamic table names and it is more only possible on raw JDBC.
The way I test it is
System.out.println(ds.getConnection().getMetaData().getURL());
Which yields jdbc:h2:mem:test
Open your standalone.xml in your favourite editor and locate the line in the urn:jboss:domain:ee:2.0 subsystem that says:
<default-bindings
context-service="java:jboss/ee/concurrency/context/default"
datasource="java:jboss/datasources/ExampleDS"
jms-connection-factory="java:jboss/DefaultJMSConnectionFactory"
managed-executor-service="java:jboss/ee/concurrency/executor/default"
managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default"
managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
and set the datasource value to the physical datasource name that you would like to be mapped to java:comp/DefaultDataSource.

Programmatically accessing camel property from camel context

The project I am working at the moment uses camel as the routing framework.
When configuring camel context in spring we pass a property file that contains a bunch of global properties needed when configuring camel routes or for controlling run time behavior:
<camel:camelContext xmlns="http://camel.apache.org/schema/spring" id="my-id">
<camel:propertyPlaceholder location="my-system.properties" id="global-properties"/>
...
</camel:camelContext>
and say my-system.properties has an entry like below:
my-system.properties
# Global properties that control my-system configuration and run time
...
foo={{bar}}
...
When configuring the routes I can access foo property using the {{foo}} notation. It is also available to other beans using #PropertyInject annotation. However there is one use case in my design when a plain POJO not created by spring (an enum instead but this is not relevant) needs to access my foo property. Because this POJO it is passed the CamelContext as a method argument I find it natural to think I should be able to get the value of foo from there. However I spent a bit of time and could not figure out by myself how.
I know I can load the properties file again or even get the system property System.getProperty("bar") and everything will work but it looks like cheating to me.
There is an api on CamelContext to resolve property placeholders - its the resolvePropertyPlaceholders method:
http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/CamelContext.html#resolvePropertyPlaceholders(java.lang.String)
If your POJO is not being managed by the SpringContext I don't see any way you can automatically inject the property. Although your approach may not seem the most fancy or elegant, it has the advantage of not giving you any overhead you could have by using another injection tool.

Categories