Spring dblink and invoke trigger on another database - spring

Hi there I would ask you about recommended solutions of connecting another database in Spring + Hibernate.
In my case I have small application with its database and what I need to do is to obtain some data from another (large) db.
Currently I'm doing that with postgresql and dblink but now it would be better to move this query into code.
In near future I'll also need to invoke triggers on that database.
So the question is whats the best practice to solve this kind of connection problems?
To sum up, what I need:
invoke another database trigger
invoke dblink and obtain data from another database

You can try this to invoke another database from your spring boot application. So your base application will use MySQL and the secondary database will be Postgres.
If you are using spring-boot then create lib folder under the project folder (Same hierarchy of src). Add required jdbc jar in that folder.
Then, create DataBaseConfig class with #Configuration, inside that class create ComboPooledDataSource and JdbcTemplate bean.
Now in your service class just do Autowiring of that JdbcTemplate bean. Then, write SQL query to get value using this method queryForList
In pom.xml, you can map that external jar file like below
<dependency>
<groupId>com.mysql</groupId>
<artifactId>db2jcc</artifactId>
<version>11.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/mysql.jar</systemPath>
</dependency>
#Configuration
public class DatabaseConfig {
#Bean(name = "employeeDataBase")
#ConfigurationProperties(prefix = "employee.datasource")
public ComboPooledDataSource employeeDataSource() {
return new ComboPooledDataSource();
}
#Bean(name = "employeeTemplate")
public JdbcTemplate employeeTemplate(#Qualifier("employeeDataBase") DataSource ds) {
return new JdbcTemplate(ds);
}
}
#Service
public class EmployeeService {
#Autowired
#Qualifier("employeeTemplate")
JdbcTemplate employeeTemplate;
public void getEmployeeFromExternalDB() {
List<Map<String, Object>> maps = employeeTemplate.queryForList("SELECT * FROM EMPLOYEE");
}
}
//application.properties
employee.datasource.jdbc-url=${EMPLOYEE_URL}
employee.datasource.user=${EMPLOYEE_UNAME}
employee.datasource.password=${EMPLOYEE_PWD}
employee.datasource.driver-class=com.edb.Driver
employee.datasource.max-idle-time=6
employee.datasource.min-pool-size=3
employee.datasource.max-pool-size=15
employee.datasource.jdbcUrl=${EMPLOYEE_URL}
employee.datasource.driver-class-name=com.edb.Driver

Related

Programmatic RedissonClient in Spring boot project

I am trying to implement Hibernate second level caching in a Spring boot project using Redisson.
I have followed this blog as a reference
https://pavankjadda.medium.com/implement-hibernate-2nd-level-cache-with-redis-spring-boot-and-spring-data-jpa-7cdbf5632883
Also i am trying to initialize the RedissionClient programmatically and not through declaratively /through a config file
Created a spring bean to be initialized which should create the RedissonClient instance.
#Configuration
#Lazy(value = false)
public class RedissonConfig {
#Bean
public RedissonClient redissionClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
However this bean is never intialized and i get the following error while application startup.
Caused by: org.hibernate.cache.CacheException: Unable to locate Redisson configuration
at org.redisson.hibernate.RedissonRegionFactory.createRedissonClient(RedissonRegionFactory.java:107) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
at org.redisson.hibernate.RedissonRegionFactory.prepareForUse(RedissonRegionFactory.java:83) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
It seems Spring boot Hibernate still trying to load the Redisson config through a config file.
is it possible to load the Redission config in spring boot programmatically ?
Best Regards,
Saurav
I just did exactly this, here is how:
you need a custom RegionFactory that is similar to the JndiRedissonRegionFactory but gets its RedissonClient injected somehow.
an instance of this Class, fully configured, is put into the hibernate-properties map. Hibernates internal code is flexible: if the value of hibernate.cache.region.factory_class is a string it is treated as a FQDN. If it is an instance of Class<?>, it will be instantiated. If it is an Object, it will be used.
Spring offers a rather simple way to customize hibernate properties with a bean:
#AutoConfiguration(after = RedissonAutoConfiguration.class, before = JpaAutoConfiguration.class)
#ConditionalOnProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache")
public class HibernateCacheAutoConfiguration {
#Bean
public HibernatePropertiesCustomizer setRegionFactory(RedissonClient redisson) {
return hibernateProperties -> hibernateProperties.put(AvailableSettings.CACHE_REGION_FACTORY, new SpringBootRedissonRegionFactory(redisson));
}
}
My RegionFactory is really simple:
#AllArgsConstructor
public class SpringBootRedissonRegionFactory extends RedissonRegionFactory {
private RedissonClient redissonClient;
#Override
protected RedissonClient createRedissonClient(Map properties) {
return redissonClient;
}
#Override
protected void releaseFromUse() {
}
}
I used the redisson-starter to get a RedissonClient, hence the reference to RedissonAutoConfiguration, but you could just create an instance by hand.
It is possible, but then you need to provide a custom implementation of RegionFactory to Hibernate, which can extends RedissonRegionFactory but uses your own client instance.

Spring boot application.properties naming

I am learning about springboot and trying to connect to a DB2 database. I got that working just fine.
Below are my working DB2 properties:
spring.datasource.url=jdbc:db2://server:port/database:currentSchema=schema-name;
spring.datasource.username=user1
spring.datasource.password=password1
But I renamed them to start with "db2" instead "spring" like:
db2.datasource.url=jdbc:db2://server:port/database:currentSchema=schema-name;
db2.datasource.username=user1
db2.datasource.password=password1
My app still runs, hHowever, when I do that, my controllers no longer return results as they did before the rename.
The reason I ask this is that if I add 2nd data source in the future, I could distinguish easily properties by their data sources if I name them like this.
UPDATE:
Thanks to #Kosta Tenasis answer below and this article (https://www.javadevjournal.com/spring-boot/multiple-data-sources-with-spring-boot/), I was able to resolve and figure this out.
Then going back to my specific question, once you have the configuration for data source in place, you can then modify application.properties to have:
db2.datasource.url=...
instead of having:
spring.datasource.url=...
NOTE1: if you are using Springboot 2.0, they changed to use Hikari and Hikari does not have url property but instead uses jdbc-url, so just change above to:
db2.datasource.jdbc-url=...
NOTE2: In your datasource that you had to create when adding multiple datasources to your project, you will have annotation #ConfigurationProperties. This annotation needs to point to your updated application.properties for datasource (the db2.datasource.url).
By default Spring looks for spring.datasource.** for the properties of the DataSource to connect to.
So you might be getting wrong results because you are not connecting to the database. If you want to configure a DataSource with different,from default, properties you can do like so
#Configuration
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="db2.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create()
.build();
}
And let's say a day comes along and you want a second DataSource you can modify the previous class to something like:
#Configuration
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix="db2.datasource")
public DataSource d2Datasource() {
return DataSourceBuilder.create()
.build();
}
#Bean
#ConfigurationProperties(prefix="db3.datasource")
public DataSource db3Datasource() { //pun intented
return DataSourceBuilder.create()
.build();
}
}
and after that in each Class that you want a DataSource you can specify which of the beans you like:
public class DB3DependedClass{
private final DataSource dataSource;
public DB3DependedClass(#Qualifier("db3Datasource") DataSource dataSource){
this.dataSource = dataSource;
}
}
So by default spring will look for
spring.datasource.url (or spring.datasource.jdbc-url)
spring.datasource.username
spring.datasource.password
If you specify another DataSource of your own, those values are not needed.
So in the above example where we specified let's say db3.datasource spring will look for
db3.datasource.url
db3.datasource.username
db3.datasource.password
Important thing here is that the spring IS NOT inferred meaning the complete path is indeed: db3.datasource.url
and NOT
spring.db3.datasource.url
Finally to wrap this up you do have the flexibility to make it start with spring if you want so by declaring a prefix like spring.any.path.ilike.datasouce and of course under that the related values. Spring will pick up either path as long as you specify it.
NOTE: This answer is written solely in the text box provided here and was not tested in an IDE for compilation errors. The logic still holds though

How to prevent HSQLDB from persisting data between applications runs when using Spring Boot?

I am run a Spring Boot/Web application and I am using an embedded HSQLDB during prototyping. As part of this I do not want to persist data between runs. I would like the only data in the system to be that from those scripts.
How can I modify my configuration to achieve that?
#Configuration
public class RepoConfig {
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:table.sql")
.addScript("classpath:data.sql")
.build()
;
return db;
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
}
I tried adding a script to drop the tables before creating them, like
DROP TABLE mytablename ;
but I kept getting the error:
user lacks privilege or object not found: MYTABLENAME
PS:
Spring Boot 2.5.0
HSQLDB 2.6.0
You can do that with the following configuration:
spring.jpa.hibernate.ddl-auto = create-drop
You can find more detail on Initialize a Database Using Hibernate
Or also you can set this property spring.datasource.data
spring.datasource.data=drop.sql,data.sql
then those files should be in
src/main/resources/drop.sql
src/main/resources/data.sql

How to read SQL queries from a properties file

I have a legacy spring mvc application which is using spring jdbc as ORM. Now I wanted to use spring boot and instead of mvc I will be converting it in to RestAPI. I have problem with database query part. Using propertyplaceholder xml confi the external sql query properties file is configured in the old application. Using Spring boot and latest annotation methods how can I configure this. My understanding is
Place the query properties file in src/main/resources directory
In DAO create property name same as the key ( name ) of the sql.
create getter and setter for the key property
Is this is the right approach ? if yes if I use this how I will get the query in my DAO class ?. If not what is the best method.
It is not a good approach but you can do it in several ways and I am always approaching to use JPA when you work with database and spring boot
application.yml
emp:
eid: "select eid from employee"
name: "select ename from employee"
create a class for Employee under the model package
#ConfigurationProperties("emp")
#Getter
#Setter
public class Employee
{
private List<Integer> eid;
private List<String> ename;
}
create a bean instance for Employee under the config package
#Configuration
public class Config
{
#Bean
public Employee getEmployee()
{
return new Employee();
}
}
then you can call Employee instance whenever you need in your project
#RestController
public class EmpController
{
#Autowired
Employee emp;
}
After posting this question I didn't get much response. So I did some work from my side and the fixed it same way as I mentioned in the question. I didn't find any other method.
First a java class ApplicationPropertyConfig for PropertySourcesPlaceholderConfigurer . Property source file is my sql properties file.
#Configuration
#PropertySource("classpath:appSql.properties")
public class ApplicationPropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
appSql.properties
selectallemployees=select * from employee;
created another class with name ApplicationSQLWrapper.java which is annotated with #Component. This contains the property name same as the sql properties key with #value annotation.
#Component
public class ApplicationSQLWrapper{
#Value("${selectallemployees}")
private String selectallemployees;
//getter and setter
This SQL's can be accessed from DAO class by creating an Object of the Component class.

Spring + multiple H2 instances in memory

Two different H2 instances to be created in-memory. To make sure this happens, I initialized both instances with same shema and different data. So that when I query using DAO different set of data picked from different DataSource. However this is not happening. What am I doing wrong? How to name the instance of H2 correct?
#Bean(name = "DS1")
#Primary
public EmbeddedDatabase dataSource1() {
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
setName("DB1").
addScript("schema.sql").
addScript("data-1.sql").
build();
}
#Bean(name = "DS2")
public EmbeddedDatabase dataSource2() {
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
setName("DB2").
addScript("schema.sql").
addScript("data-2.sql").
build();
}
You have created two DataSources and have marked one as #Primary -- this is the one which will be used when your EntityManagerFactories and Repositories are autoconfigured. That's why both DAO's are accessing the same database.
In order to get around this, you need to declare two separate EntityManagerFactories, as described in the Spring Boot documentation:
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-use-two-entity-managers
After that, you'll need to declare two separate repositories and tell each repository which of the EntityManagerFactory to use. To do this, in your #EnableJpaRepositories annotation you'll have to specify the correct EntityMangerFactory. This article describes very nicely how to do that:
http://scattercode.co.uk/2013/11/18/spring-data-multiple-databases/
It would be nice if Spring Boot supported autoconfiguration with two DataSources, but I don't think it's going to happen soon:
https://github.com/spring-projects/spring-boot/issues/808
UPDATE
The author of the above article has published an updated approach:
https://scattercode.co.uk/2016/01/05/multiple-databases-with-spring-boot-and-spring-data-jpa/
The issue was not with multiple instances of H2; but with DataSource injection.
I solved it by passing the Qualifier in method argument.
#Autowired
#Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(#Qualifier("myDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

Resources