I would like to implement a RESTful web service using Jersey/Spring which switches between application contexts depending on the host name to which the request was made (similar to virtual hosting).
For example the requests made to site1.domain.com and site2.domain.com should have different sets of service beans working with different databases, users, etc.
What is the best way to do it?
EDIT: the application contexts are supposed to have identical bean classes. The difference is the database used. Also they have to be dynamic, i.e. defined/destroyed during application runtime.
Would be nice to know if this is possible with Spring at all and what is the starting point to search for. Most of the information I found is related to static configuration during webapp init.
You need to use a Custom Spring bean scope depending on the request, to manage multiple EMF/SessionFactory, with multiple Units.
#Scope("dynamic")
#Bean(name ="erpEMF")
public LocalContainerEntityManagerFactoryBean erpManagerFactory() {
LocalContainerEntityManagerFactoryBean emf = buildEmf();
return emf;
}
#Scope("dynamic")
#Bean(name ="erpJPA")
public JpaTransactionManager erpTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setPersistenceUnitName("erpUnit");
return transactionManager;
}
#Scope("dynamic")
#Bean(name ="erpDataSource", destroyMethod=EmfHolder.DataSourceCloseMethod)
public DataSource erpDataSource() {
return dynamicDataSource( DB_NAMES.AGI_ERP );
}
public class DynamicScope implements Scope{
public Object get(String name, ObjectFactory<?> objectFactory) {
//return Service depending on your request
}
}
#Scope("dynamic")
#Service
public class ActAccountService extends ErpGenericService<ActAccount> implements IActAccountService {
#Transactional("erpJPA")
public Account create(Account t){
}
}
Related
In Java with Spring Boot framework there is an #Bean named DataSource that is used to make connections to a database, and is used by another #Bean called JdbcTemplate that serves to perform actions on the database. But, there is a problem, this #Bean DataSource, which serves to make the connection to the database, requires the properties for the connection to be preconfigured (url, username and password). The #Bean DataSource needs to start with an "empty" or "default" value at project startup, and at runtime it changes this value. I want a certain Endpoint to execute the action of changing the value of #Bean, to be more exact. And with the change of the value of the #Bean DataSource the JdbcTemplate, consequently, will be able to perform actions in several database.
Some details:
I have already evaluated this issue of using multiple databases, and in my case, it will be necessary.
All databases to be connected have the same model.
I do not think I need to delete and create another #Bean DataSource at runtime, maybe just override the #Bean values that the Spring Boot itself already creates automatically.
I have already made the #Bean DataSource start with an "empty" value by making a method with the #Bean annotation and that returns a DataSource object that is literally this code: DataSourceBuilder.build().create();.
My English is not very good so if it's not very understandable, sorry.
DataSource #Bean code:
#Bean
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
Main class:
#SpringBootApplication(scanBasePackages = "br.com.b2code")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class RunAdm extends SpringBootServletInitializer implements
CommandLineRunner {
public static final String URL_FRONTEND = "*";
/**
* Método main do módulo de Gestão.
*
* #param args argumentos de inicialização
* #throws Exception uma exception genérica
*/
public static void main(String[] args) throws Exception {
SpringApplication.run(RunAdm.class, args);
}
#Override
protected SpringApplicationBuilder
configure(SpringApplicationBuilder application) {
return application.sources(RunAdm.class);
}
#Override
public void run(String... args) throws Exception {
}
}
A class to exemplify how I use JdbcTemplate:
#Repository
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class ClienteQueryRepositoryImpl implements ClienteQueryRepository {
private final #NonNull
JdbcTemplate jdbc;
#Override
public List<Cliente> findAll() {
return jdbc.query(ClienteQuerySQL.SELECT_ALL_CLIENTE_SQL, new ClienteRowMapper());
}
}
I think as a general approach you might consider a Proxy Design Pattern for the actual DataSource Implementation.
Let's suppose the DataSource is an interface that has getConnection method by user and password (other methods are not really important because this answer is theoretical):
interface DataSource {
Connection getConnection(String user, String password);
}
Now, in order to maintain many databases you might want to provide an implementation of the datasource which will act as a proxy for other datasources that will be created on the fly (upon the endpoint call as you say).
Here is an example:
public class MultiDBDatasource implements DataSource {
private DataSourcesRegistry registry;
public Connection getConnection(String user, String password) {
UserAndPassword userAndPassword = new UserAndPassword(user, password);
registry.get(userAndPassword);
}
}
#Component
class DataSourcesRegistry {
private Map<UserAndPassword, DataSource> map = ...
public DataSource get(UserAndPassword a) {
map.get(a);
}
public void addDataSource(UserAndPassword cred, DataSource ds) {
// add to Map
map.put(...)
}
}
#Controller
class InvocationEndPoint {
// injected by spring
private DataSourcesRegistry registry;
#PostMapping ...
public void addNewDB(params) {
DataSource ds = createDS(params); // not spring based
UserAndPassword cred = createCred(params);
registry.addDataSource(cred, ds);
}
}
A couple of notes:
You should "override" the bean of DataSource offered by spring - this can be done by defining your own bean with the same name in your own configuration that will take precedence over spring's definition.
Spring won't create Dynamic Data Source, they'll be created from the "invocation point" (controller in this case for brevity, in real life probably some service). in any case only Registry will be managed by spring, not the data sources.
Keep in mind that this approach is very high-level, in a real life you'll have to think about:
Connection Pooling
Metering
Transaction Support
Multithreaded Access
and many other things
I am writing services in Spring boot that get their configurations from Spring cloud. These services are multi-tenant and the tenant is based on the host name.
what I have now is
public class MyController {
#Autowired
public MyController(MyServiceFactory factory) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#RequestHeader header, #PathVariable id) {
return factory.getMyService(header).handle(id);
}
}
where MyServiceFactory looks something like...
public class MyServiceFactory {
private final HashMap<String, MyService> serviceRegistry = new HashMap<>();
public MyService getMyService(String key) {
return serviceRegistry.get(key);
}
MyServiceFactory withService(String key, MyService service) {
this.serviceRegistry.put(key, service);
return this;
}
}
then in a configuration file
#Configuration
public ServiceFactoryConfiguration {
#Bean
public MyServiceFactory getMyServiceFactory() {
return new MyServiceFactory()
.withService("client1", new MyService1())
.withService("client2", new MyService2());
}
}
While what I have now works, I don't like that I need to create a factory for every dependency my controller may have. I'd like to have my code look something like this...
public class MyController {
#Autowired
public MyController(MyService service) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#PathVariable id) {
return service.handle(id);
}
}
with a configuration file like
#Configuration
public class MyServiceConfiguration() {
#Bean
#Qualifier("Client1")
public MyService getMyService1() {
return new MyService1();
}
#Bean
#Qualifier("Client2")
public MyService getMyService2() {
return new MyService2();
}
}
I can get the code that I want to write if I use a profile at application start up. But I want to have lots of different DNS records pointing to the same (pool of) instance(s) and have an instance be able to handle requests for different clients. I want to be able to swap out profiles on a per request basis.
Is this possible to do?
Spring profiles would not help here, you would need one application context per client, and that seems not what you want.
Instead you could use scoped beans.
Create your client dependent beans with scope 'client' :
#Bean
#Scope(value="client",proxyMode = ScopedProxyMode.INTERFACES)
#Primary
MyService myService(){
//does not really matter, which instance you create here
//the scope will create the real instance
//may be you can even return null, did not try that.
return new MyServiceDummy();
}
There will be at least 3 beans of type MyService : the scoped one, and one for each client. The annotation #Primary tells spring to always use the scoped bean for injection.
Create a scope :
public class ClientScope implements Scope {
#Autowired
BeanFactory beanFactory;
Object get(String name, ObjectFactory<?> objectFactory){
//we do not use the objectFactory here, instead the beanFactory
//you somehow have to know which client is the current
//from the config, current request, session, or ThreadLocal..
String client=findCurrentClient(..);
//client now is something like 'Client1'
//check if your cache (HashMap) contains an instance with
//BeanName = name for the client, if true, return that
..
//if not, create a new instance of the bean with the given name
//for the current client. Easiest way using a naming convention
String clientBeanName=client+'.'+name;
Object clientBean=BeanFactory.getBean(clientBeanName);
//put in cache ...
return clientBean;
};
}
And your client specific beans are configured like this :
#Bean('Client1.myService')
public MyService getMyService1() {
return new MyService1();
}
#Bean('Client2.myService')
public MyService getMyService2() {
return new MyService2();
}
Did not test it but used it in my projects. Should work.
tutorial spring custom scope
I have an application that copy data from one DB to an other DB. On both DB's the same DB schema is installed. When we copy data a lot of business logic is needed.
We have a typical, traditional implementation with a service, dao and a custom persistence layer (no entity manager).
First I start to define two datasources used by two different transaction managers.
#Configuration
#Profile("test")
public class TestEnvironment {
/**
* Configuration for the database datasource 1
*
* #return
*/
#Bean(name = { "DataSource1"})
public DataSource basicDatasource1() {
...
}
#Bean
public DataSourceTransactionManager txManagerDB1() {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(basicDatasource1());
return dataSourceTransactionManager;
}
/**
* Configuration for the database datasource 2
*
* #return
*/
#Bean(name = { "DataSource2"})
public DataSource basicDatasource2() {
...
}
#Bean
public DataSourceTransactionManager txManagerDB2() {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(basicDatasource2());
return dataSourceTransactionManager;
}
}
Then I implement my Service and inject via #Autowired my test DAO.
I implement two methods using the same DAO (reentrant). This two methods in my Service I annotate with #Transactional and set respectively the txManager I want to use.
#Service
#Primary
public class MyTestServiceImpl extends AbstractHandler implements MyTestService {
#Autowired
private TxManagerTestDao txManagerTestDao;
#Transactional("txManagerDB1")
public String callToDB1() {
txManagerTestDao.setTransactionName("txManagerDB1");
return txManagerTestDao.loadTestBean().getTestString();
}
/*
* Return DB Connection Type
*/
#Transactional("txManagerDB2")
public String callToDB2() {
txManagerTestDao.setTransactionName("txManagerDB2");
return txManagerTestDao.loadTestBean().getTestString();
}
}
The line txManagerTestDao.setTransactionName("txManagerDB1") or txManagerTestDao.setTransactionName("txManagerDB2") is bad design I want to remove. Because, I need that only to know in the DAO layer what transaction manager I want to use to set the active connection to my persistence layer.
My DAO looks like the following:
#Repository
public class TxManagerTestDaoImpl implements TxManagerTestDao {
private String transactionName;
#Autowired
private AutowireCapableBeanFactory autowireBeanFactory;
public TestBean loadTestBean() {
DataSourceTransactionManager myTransaction = (DataSourceTransactionManager) autowireBeanFactory.getBean(transactionName);
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(myTransaction.getDataSource());
//conHolder.getConnection() is know the correct connection I have to use due to the #Transactional in my servide layer.
//HOW CAN I GET WITH A GENERIC WAY THE ACTIVE TRANSACTION MANAGER?
TestBean testBean = new TestBean();
testBean.setTestString("myTransaction: '" + myTransaction + "' Connection: '" + conHolder.getConnection() + "'");
return testBean;
}
#Override
public void setTransactionName(String transactionName) {
this.transactionName = transactionName;
}
}
I want to set the active txManager via #Transactional("...") as I did it in my service layer. I will not use in the future txManagerTestDao.setTransactionName("txManagerDB2") in an redundant way.
The solution works perfect, but is not perfect to implement for our special use case.
Is there a generic way to found out in the method TxManagerTestDaoImpl.loadTestBean() if txManagerDB1 or txManagerDB2 is active?
As a result I need the active ConnectionHolder Object of my active Transaction.
The I inject that Connection into my Persistence, then all classes runs in the same Transaction.
The DAO and the persistence Layer should be neutral. Our persistence is only a DataMapper from/to SQL. I want/can not use JPA or a similar framework.
Thanks
I stuck with a simple refactoring from plain Java to Spring. Application has a "Container" object which instantiates its parts at runtime. Let me explain with the code:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
public void load() {
// repeated several times depending on external data/environment
RuntimeBean beanRuntime = createRuntimeBean();
runtimeBeans.add(beanRuntime);
}
public RuntimeBean createRuntimeBean() {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
}
}
Basically, during load container asks some external system to provide him information about number and configuration of each RuntimeBean and then it create beans according to given spec.
The problem is: usually when we do in Spring
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
Container container = (Container) context.getBean("container");
our object is fully configured and have all dependencies injected. But in my case I have to instantiate some objects which also needs dependency injection after I execute load() method.
How can I achieve that?
I am using a Java-based config. I already tried making a factory for RuntimeBeans:
public class BeanRuntimeFactory {
#Bean
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
Expecting #Bean to work in so called 'lite' mode. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html Unfortunately, I found no difference with simply doing new RuntimeBean();
Here is a post with a similar issue: How to get beans created by FactoryBean spring managed?
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Configurable.html but it looks like a hammer in my case.
I also tried ApplicationContext.getBean("runtimeBean", args) where runtimeBean has a "Prototype" scope, but getBean is an awful solution.
Update 1
To be more concrete I am trying to refactor this class:
https://github.com/apache/lucene-solr/blob/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java
#see #load() method and find "return create(cd, false);"
Update 2
I found quite interesting thing called "lookup method injection" in spring documentation:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-lookup-method-injection
And also an interesting jira ticket https://jira.spring.io/browse/SPR-5192 where Phil Webb says https://jira.spring.io/browse/SPR-5192?focusedCommentId=86051&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-86051 that javax.inject.Provider should be used here (it reminds me Guice).
Update 3
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
Update 4
The issue with all these 'lookup' methods is they don't support passing any arguments.. I also need to pass arguments as I would do with applicationContext.getBean("runtimeBean", arg1, arg2). Looks like it was fixed at some point with https://jira.spring.io/browse/SPR-7431
Update 5
Google Guice have a neat feature for it called AssistedInject. https://github.com/google/guice/wiki/AssistedInject
Looks like I found a solution. As I am using java based configuration it is even simpler than you can imagine. Alternative way in xml would be lookup-method, however only from spring version 4.1.X as it supports passing arguments to the method.
Here is a complete working example:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
private RuntimeBeanFactory runtimeBeanFactory;
public void load() {
// repeated several times depending on external data/environment
runtimeBeans.add(createRuntimeBean("Some external info1"));
runtimeBeans.add(createRuntimeBean("Some external info2"));
}
public RuntimeBean createRuntimeBean(String info) {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
return runtimeBeanFactory.createRuntimeBean(info);
}
public void setRuntimeBeanFactory(RuntimeBeanFactory runtimeBeanFactory) {
this.runtimeBeanFactory = runtimeBeanFactory;
}
}
public interface RuntimeBeanFactory {
RuntimeBean createRuntimeBean(String info);
}
//and finally
#Configuration
public class ApplicationConfiguration {
#Bean
Container container() {
Container container = new Container(beanToInject());
container.setBeanRuntimeFactory(runtimeBeanFactory());
return container;
}
// LOOK HOW IT IS SIMPLE IN THE JAVA CONFIGURATION
#Bean
public BeanRuntimeFactory runtimeBeanFactory() {
return new BeanRuntimeFactory() {
public RuntimeBean createRuntimeBean(String beanName) {
return runtimeBean(beanName);
}
};
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
RuntimeBean runtimeBean(String beanName) {
return new RuntimeBean(beanName);
}
}
class RuntimeBean {
#Autowired
Container container;
}
That's it.
Thanks everyone.
i think that your concept is wrong by using
RuntimeBean beanRuntime = createRuntimeBean();
you are bypassing Spring container and resorting to using regular java constructor therefore any annotations on factory method are ignored and this bean is never managed by Spring
here is the solution to create multiple prototype beans in one method, not pretty looking but should work, I autowired container in RuntimeBean as proof of autowiring shown in log also you can see in log that every bean is new instance of prototype when you run this .
'
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Container container = (Container) context.getBean("container");
container.load();
}
}
#Component
class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
#Autowired
ApplicationContext context;
#Autowired
private ObjectFactory<RuntimeBean> myBeanFactory;
public void load() {
// repeated several times depending on external data/environment
for (int i = 0; i < 10; i++) {
// **************************************
// COMENTED OUT THE WRONG STUFFF
// RuntimeBean beanRuntime = context.getBean(RuntimeBean.class);
// createRuntimeBean();
//
// **************************************
RuntimeBean beanRuntime = myBeanFactory.getObject();
runtimeBeans.add(beanRuntime);
System.out.println(beanRuntime + " " + beanRuntime.container);
}
}
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
// #Component
class RuntimeBean {
#Autowired
Container container;
} '
A simple approach:
#Component
public class RuntimeBeanBuilder {
#Autowired
private ApplicationContext applicationContext;
public MyObject load(String beanName, MyObject myObject) {
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) applicationContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
if (beanRegistry.containsSingleton(beanName)) {
return beanRegistry.getSingleton(beanName);
} else {
beanRegistry.registerSingleton(beanName, myObject);
return beanRegistry.getSingleton(beanName);
}
}
}
#Service
public MyService{
//inject your builder and create or load beans
#Autowired
private RuntimeBeanBuilder builder;
//do something
}
Instead of using SingletonBeanRegistry you can use this:
BeanFactory beanFactory = configContext.getBeanFactory();
Anyway SingletonBeanBuilder extends HierarchicalBeanFactory and HierarchicalBeanFactory extends BeanFactory
You don't need the Container because all of the runtime objects should be created, held and managed by ApplicationContext. Think about a web application, they are much the same. Each request contains external data/environment info as you mentioned above. What you need is a prototype/request scoped bean like ExternalData or EnvironmentInfo which can read and hold runtime data through a static way, let's say a static factory method.
<bean id="externalData" class="ExternalData"
factory-method="read" scope="prototype"></bean>
<bean id="environmentInfo" class="EnvironmentInfo"
factory-method="read" scope="prototype/singleton"></bean>
<bean class="RuntimeBean" scope="prototype">
<property name="externalData" ref="externalData">
<property name="environmentInfo" ref="environmentInfo">
</bean>
If you do need a container to save the runtime objects, code should be
class Container {
List list;
ApplicationContext context;//injected by spring if Container is not a prototype bean
public void load() {// no loop inside, each time call load() will load a runtime object
RuntimeBean bean = context.getBean(RuntimeBean.class); // see official doc
list.add(bean);// do whatever
}
}
Official doc Singleton beans with prototype-bean dependencies.
It is possible to register beans dynamically by using BeanFactoryPostProcesor. Here you can do that while the application is booting (spring's application context has been initialized). You can not register beans latest, but on the other hand, you can make use of dependency injection for your beans, as they become "true" Spring beans.
public class DynamicBeansRegistar implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (! (beanFactory instanceof BeanDefinitionRegistry)) {
throw new RuntimeException("BeanFactory is not instance of BeanDefinitionRegistry");
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// here you can fire your logic to get definition for your beans at runtime and
// then register all beans you need (possibly inside a loop)
BeanDefinition dynamicBean = BeanDefinitionBuilder.
.rootBeanDefinition(TheClassOfYourDynamicBean.class) // here you define the class
.setScope(BeanDefinition.SCOPE_SINGLETON)
.addDependsOn("someOtherBean") // make sure all other needed beans are initialized
// you can set factory method, constructor args using other methods of this builder
.getBeanDefinition();
registry.registerBeanDefinition("your.bean.name", dynamicBean);
}
#Component
class SomeOtherClass {
// NOTE: it is possible to autowire the bean
#Autowired
private TheClassOfYourDynamicBean myDynamicBean;
}
As presented above, you can still utilize Spring's Dependency Injection, because the post processor works on the actual Application Context.
I want to load some data into the mysql database during loading of the application. I am using Hibernate for managing database for the application. I can do it in groovy by using Bootstrap but I want to achieve it in Java. I would like to mention that it is Spring MVC based web application.
While searching on the internet, I found that but using hibernate property named as import_file, I can achieve it but I am looking for an alternate route.
You could also take advantage of Spring's DataSourceInitializer . The following is an example of Java Config for it.
#Bean
public DataSourceInitializer dataSourceInitializer() {
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
resourceDatabasePopulator.addScript(new ClassPathResource("/data.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(dataSource());
dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
return dataSourceInitializer;
}
Spring already provides a way of initializing databases with content, using a DatabasePopulator.
Here's one quick example that I found, for a Spring Batch sample application. The class to look at in that code is ResourceDatabasePopulator.
Another example is in Spring Social project samples.
I would go for registering an instance of ApplicationListener in the Spring context configuration, that listens for the ContextRefreshedEvent, which is signalled when the application context has finished initializing or being refreshed. After this moment you could setup your database population.
Below you will find the ApplicationListener implementation (which depends on the DAO responsible for performing the database operations) and the Spring configuration (both Java and XML)that you need to achieve this. You need to choose the configuration specific to your app:
Java-based configuration
#Configuration
public class JavaConfig {
#Bean
public ApplicationListener<ContextRefreshedEvent> contextInitFinishListener() {
return new ContextInitFinishListener(personRepository());
}
#Bean
public PersonRepository personRepository() {
return new PersonRepository();
}
}
XML
<bean class="com.package.ContextInitFinishListener">
<constructor-arg>
<bean class="com.package.PersonRepository"/>
</constructor-arg>
</bean>
This is the code for the ContextInitFinishListener class:
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class ContextInitFinishListener implements ApplicationListener<ContextRefreshedEvent> {
private PersonRepository personRepository;
public ContextInitFinishListener(PersonRepository personRepository) {
this.personRepository = personRepository;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//populate database with required values, using PersonRepository
}
}
NOTE: PersonRepository is just a generic DAO for the purpose of the example, it's meant to represent the DAO that YOU use in your app
I used as below in my spring boot console application test.
ResourceDatabasePopulator rdp = new ResourceDatabasePopulator();
rdp.addScript(new ClassPathResource("sql/create-tables.sql"));
rdp.execute(dataSource);
there are different ways to get datasource depending on type of application or data layered framework.
if u r using spring boot atoconfigured h2 datasource u can use.
#Autowired
Datasource datasource;
to get the data source throgh external configuration class is below
#Value("${spring.datasource.driver-class-name}")
private String driverClass;
#Value("${spring.datasource.url}")
private String dbUrl;
#Value("${spring.datasource.username}")
private String dbUserName;
#Value("${spring.datasource.password}")
private String dbPassword;
#Bean
public DataSource dataSource(){
SingleConnectionDataSource dataSource = new
SingleConnectionDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(dbUrl);
dataSource.setUsername(dbUserName);
dataSource.setPassword(dbPassword);
dataSource.setSuppressClose(true);
dataSource.setAutoCommit(true);
return dataSource;
}
this worked for me, keep al queries that u need to execute in create-tables.sql