How to prevent HSQLDB from persisting data between applications runs when using Spring Boot? - 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

Related

H2 DB Initial Set Up scripts are getting called multiple times in JUNIT

Am trying to write JUNIT test cases with H2 DB for my micorservices. I have some .sql files for initial set up of the DB, like creating the schema, tables etc. One issue am seeing here is that the .sql files are getting called multiple times, once per JUNIT test file. Is there any way to handle this scenario? My DB Set up file looks like this -
#Configuration
#ComponentScan(basePackages={"com.sample.repository"})
public class SampleDBConfig {
JdbcTemplate jdbcTemplate = null;
DataSource dataSource = null;
#Bean
public JdbcTemplate jdbcTemplate()
{
if(jdbcTemplate == null){
LOGGER.info("JdbcTemplate is null, so calling to create ");
jdbcTemplate = new JdbcTemplate(getDataSource());
}else{
LOGGER.info("JdbcTemplate is already set");
}
return jdbcTemplate;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
DataSource getDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
if(dataSource == null){
dataSource = builder
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:folder/initial-setup.sql")
.build();
}
return dataSource;
}
}
One of my JUNIT test class looks like this -
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SampleDBConfig.class)
public class XyxImplTest{
#Autowired
ClassInTestImpl classInTestImpl ;
#Test
public void testMethod(){
.......
}
}
And one of the classes am testing are like this -
#Component
#Transactional(rollbackFor = Exception.class)
public class ClassInTestImpl implements ClassInTest {
#Autowired
private JdbcTemplate jdbcTemplate;
.....
I know this is a bit old thread, but adding some notes if anyone else having similar issue. I've faced same issue where I was getting unique constraint violation errors when H2 trying to insert data for each test file.
At the end it was the latest H2 version which causes this. If you change the H2 version from 1.4.200 to 1.4.199, it works like a charm. No other changes required to get this working as expected.
I had the same problem. The quick fix I did is, I din configure the datasource bean. Added the relevant spring properties in the properties file which creates the datasource.
Also I used #Sql("classpath:data1.sql") on the first test, so the script ran only once for that testsuite. It worked for me.

Custom DataSource Spring boot

spring works well when we use the default datasource and ways we can use in-build spring jpa.
So currently what we do is the following
specify the config for DB in the application.properties
myapp.datasource.url=jdbc:mysql:thin:#localhost:1521:myschema
myapp.datasource.username=user
myapp.datasource.password=password
myapp.datasource.driver-class=com.mysql.cj.jdbc.Driver
Custom datasource
#ConfigurationProperties(prefix = "myapp.datasource")
#Bean
public DataSource mySqlDataSource()
{
return DataSourceBuilder.create().build();
}
We have the same application running for multiple clients. Problem is each client has their own DB schema.
So, the problem now is that we need to be able to serve each client but in order to do this, we need to create multiple datasources
for instance:
#ConfigurationProperties(prefix = "myapp.partner1.datasource")
#Bean
public DataSource mySqlDataSourcePartner1()
{
return DataSourceBuilder.create().build();
}
#ConfigurationProperties(prefix = "myapp.partner2.datasource")
#Bean
public DataSource mySqlDataSourcePartner2()
{
return DataSourceBuilder.create().build();
}
#ConfigurationProperties(prefix = "myapp.partner3.datasource")
#Bean
public DataSource mySqlDataSourcePartner3()
{
return DataSourceBuilder.create().build();
}
and so on...
Is there a generic and more efficient way of doing this? where if in future when a new partner is added we can just specify the config in application properties and get that working?
You can use Spring Boot Multi-tenancy model using a separate database for each client. You can save the database configuration in config-properties or database then depending upon the ClientId you can you the Datasource. You need to add Interceptor to intercept the Request and identify the tenant. Please refer to the below example
https://tech.asimio.net/2017/01/17/Multitenant-applications-using-Spring-Boot-JPA-Hibernate-and-Postgres.html
please check
https://github.com/sumanentc/multitenant

Spring dblink and invoke trigger on another database

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

How to create custom datasource for each session

I would like to create a stateless bean in Spring boot which enable user to connect to a specific database, so I started with this code, but i'm still new with spring boot.
#Bean
#Primary
public DataSource helloDataSource() {
return DataSourceBuilder.create()
.username("myUsername")
.password("myPassword")
.driverClassName("myDBDriver")
.build();
}
So what is the best way to follow to make this code work and connect to any database ( Also remote Database )
There are several ways that this could be done. One of my preferred way is to provide configuration of data sources via properties file. Here is a sample property file for postgresql:
pg.datasource.url=jdbc:postgresql://db-server-bar:5432/app-db
pg.datasource.username=root
pg.datasource.password=toor
pg.datasource.driver-class-name=org.postgresql.Driver
Now you can create configuration class for each datasource:
public class BarDbConfig {
#Bean(name = "pgDataSource")
#ConfigurationProperties(prefix = "pg.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "barEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean
barEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("pgDataSource") DataSource dataSource
) {
return
builder
.dataSource(dataSource)
.packages("com.app.domain")
.persistenceUnit("pg")
.build();
}
}
For a detailed tutorial, you can refer to this tutorial.
There can also be situations where you would like to switch databases at runtime. In which case, you can use something called AbstractRoutingDataSource. A detailed tutorial on how to use this feature can be found at Spring's official blog site.

Spring batch boot Multiple datasources Multiple schemas

I have a spring batch job using spring boot which has 2 datasources. Each datasource again has 2 schemas each. I need to specify default schema for both the datasources. I know of property spring.jpa.properties.hibernate.default_schema which i am using to specify default schema for one datasource. Is there a way to specify default schema for another schema?
Currently, to specify default schema for the other datasource , i am using alter session query to switch schema as required. I am trying to get rid of this alter session query from my java code. Any suggestions on it is greatly appreciated.
edit 1: Both are ORACLE databases
If you use multiple datasources, then you probably has a #Configuration class for each datasource. In this case you can set additional properties to the entityManager. This configuration is needed:
props.put("spring.datasource.schema", "test");
Full example
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "testEntityManagerFactory", transactionManagerRef = "testTransactionManager",
basePackages = {"com.test.repository"})
public class TestDbConfig {
#Bean(name = "testDataSource")
#ConfigurationProperties(prefix = "test.datasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "testEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier("testDataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.test.model").persistenceUnit("test").properties(jpaProperties()).build();
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
props.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
props.put("spring.datasource.schema", "test");
return props;
}
#Bean(name = "testTransactionManager")
public PlatformTransactionManager transactionManager(#Qualifier("testEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}

Resources