Is data.sql disabled in spring boot 2.3.1.RELEASE? - spring

#SpringBootApplication
public class DatabaseDemoApplication implements CommandLineRunner {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
PersonJbdcDao dao;
public static void main(String[] args) {
SpringApplication.run(DatabaseDemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// Thread.sleep(1000); --------------------------------------------------> line 1
logger.info("All users -> {}", dao.findAll());
}
}
#Repository
public class PersonJbdcDao {
#Autowired
JdbcTemplate jdbcTemplate;
public List<Person> findAll() {
return jdbcTemplate.query("select * from person",
new BeanPropertyRowMapper<Person>(Person.class));
}
}
In pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
I am using h2 database. Following is the application.properties
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
Person.class
private int id;
private String name;
private String location;
private Date birthDate;
// getter and setter
I also have a data.sql file in the resources folder which have create and insert statements.
If i keep line 1(Thread.sleep(1000)) commented and run the project I'm facing an error but I am able to access table Person via h2 console
java.lang.IllegalStateException: Failed to execute CommandLineRunner
Caused by: org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar [select * from person]; nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "PERSON" not found; SQL statement:
select * from person [42102-200]
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "PERSON" not found; SQL statement:
select * from person [42102-200]
I have 2 scenarios
If I keep line 1 commented and rename data.sql file to schema.sql file everything works perfect. I'm able to access why is that?
If I keep data.sql file and don't rename it I have to uncomment line 1 to get application working. Why is that ?

Since Spring Boot 2.3, JPA repository initialization is "deferred".
So run method runs before initialized it (if you don't define any JPA repositories).
If set
spring.data.jpa.repositories.bootstrap-mode=default
on application.properties, run as you expected.
see also:
9.10.3. Initialize a Database using basic SQL scripts
DataSourceInitializerInvoker javadoc says:
Bean to handle DataSource initialization by running schema-*.sql on InitializingBean#afterPropertiesSet() and data-*.sql SQL scripts on DataSourceSchemaCreatedEvent.
, and DataSourceSchemaCreatedEvent says:
This happens when schema-*.sql files are executed or when Hibernate initializes the database.
So, on first scenario, data.sql is executed after JPA(Hibernate) initialization, but it is "deferred".
On the other hand, schema.sql is executed before run(), same as earlier version.

Related

#Transactional not starting transactions with Spring Boot 3 / Hibernate 6

I am currently migrating to Spring Boot 3 / Hibernate 6.
Hibernate is correctly parsing all the entities and repos, connecting to the database, etc...
However, it seems #Transactional is not starting transactions correctly.
Small example:
#Component
public class Test {
#Autowired
private EntityManagerFactory entityManager;
#Transactional
public void test() {
Session s = entityManager.unwrap(SessionFactory.class).getCurrentSession();
s.createQuery("FROM sometable").list();
}
}
Error:
Caused by: org.hibernate.HibernateException: Calling method 'createQuery' is not valid without an active transaction (Current status: NOT_ACTIVE)
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:341)
Relevant Config:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="com.somepackage")
#EntityScan(basePackages="com.somepackage")
public class TransactionConfig {
...
}
session context class in application.properties
...
spring.jpa.properties.hibernate.current_session_context_class=thread
...
If I remove the above setting of session_content_class=thread,
I get this error:
Caused by: org.hibernate.HibernateException: No CurrentSessionContext configured
Edit 1:
The below still results in the same error "is not valid without an active transaction"
#PersistenceUnit
private EntityManagerFactory entityManager;
Edit 2:
If I do not unwrap a session and just call a class with extends extends JpaRepository, it works... but it creates a new transaction and ignores the parent #Transaction
Fix was the following:
#PersistenceContext
private EntityManager entityManager;
and to unwrap:
Session s = entityManager.unwrap(Session.class);

Spring Security Oauth2 H2 for testing - Table "OAUTH_ACCESS_TOKEN" not found

Here I'm using InMemoryTokenStore. I can get this TokenStore to work when running the app by doing the following:
#Configuration
public class AppConfig {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
// ...
}
Now I can make HTTP requests to /oauth/token and get an access token. I can also access authorized resources. And, my tests are passing and creating tables even on the fly (e.g. oauth_access_token table).
However, I don't want to use InMemoryTokenStore for dev and later on production environments. So, I'm trying to use configure H2 in a test properties file for my tests only, other environments I intend to use MySQL.
So I've modified my config class:
#Configuration
public class AppConfig {
#Value("${spring.datasource.url}")
private String datasourceUrl;
#Value("${spring.datasource.username}")
private String dbUsername;
#Value("${spring.datasource.password}")
private String dbPassword;
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(datasourceUrl);
dataSource.setUsername(dbUsername);
dataSource.setPassword(dbPassword);
return dataSource;
}
#Bean
public TokenStore tokenStore(DataSource dataSource) {
return new JdbcTokenStore(dataSource);
}
// ...
}
And I have two application.properties files for each environment:
src/main/resources/application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:33067/budget?useSSL=false
spring.datasource.username=budget
spring.datasource.password=s3cret
src/test/resources/application.properties
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
My intention here is that the test environment will pick up the H2 db config, and others will use the MySQL db config. However, I'm seeing the following error in my tests now:
2019-12-30 21:09:41.627 ERROR 20628 --- [ main] o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: BadSqlGrammarException, PreparedStatementCallback; bad SQL grammar [select token_id, token from oauth_access_token where authentication_id = ?]; nested exception is org.h2.jdbc.JdbcSQLException: Table "OAUTH_ACCESS_TOKEN" not found; SQL statement:
select token_id, token from oauth_access_token where authentication_id = ? [42102-197]
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [select token_id, token from oauth_access_token where authentication_id = ?]; nested exception is org.h2.jdbc.JdbcSQLException: Table "OAUTH_ACCESS_TOKEN" not found; SQL statement:
select token_id, token from oauth_access_token where authentication_id = ? [42102-197]
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:234) ~[spring-jdbc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1402) ~[spring-jdbc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
...
I can confirm that the tests are picking up the H2 config but throws an error as the table is missing. If I skip tests, then I can run the app and make HTTP requests successfully - if the table is missing in the MySQL db it creates it. InMemoryTokenStore config creates tables too so this error doesn't occur - but I don't want this for dev/prod. But the H2 config (src/test/resources/application.properties) during tests throws an error as the table is missing, so it's not creating any tables it would seem. What am I doing wrong?

Am trying to do simple JDBC call in a spring boot application is not working

I am looking forward to creating a service using spring boot application where I like to use JDBC prepared statement call which executes the stored procedure get me the required result.
I like to have connection pooling but unfortunately, I don’t know implement
Summary
(services using spring boot --->Simple JDBC with connection pooling---->Mysql)
For this, I have tried to create a data source and execute jdbc statement but not working
#Controller
public class ExampleController {
#Autowired
private ExampleRepository repo;
#RequestMapping("/")
public #ResponseBody String getDataBaseData() throws SQLException{
return repo.getDataBaseData();
}
}
#Configuration
public class DataSources {
#Bean(name = "primary")
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
#Component
public class ExampleRepository {
#Autowired
private DataSource ds;
public String getDataBaseData() throws SQLException {
Connection con = ds.getConnection();
System.out.println(con);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from emp");
while (rs.next())
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getString(3));
con.close();
return rs.toString();
}
}
getting errors like below
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
Expected Result : Database data should display in web browser
This is my github repo https://github.com/PradeepKumarHE/SpringBootWithSimpleJDBC/tree/master
where i have DBscript file to create
What I can see from your pom.xml, you are using spring-boot-starter-data-jpa. It fetches unneccessary dependencies, which triggers SpringBoot jpa autoconfiguration.
If you want to use pure jdbc with spring boot, replace spring-boot-starter-data-jpa with spring-boot-starter-jdbc (https://mvnrepository.com/artifact/org.springframework.boot/)
In this case you need to
have mysql jdbc driver declared in maven dependencies
define spring.datasource.url, spring.datasource.username, spring.datasource.password in your properties or yaml (you don't need to define spring.datasource.driver if you have only one jdbc driver in your maven deps)
remove your DataSources configuration, since springboot will autoconfigure it for you

Flyway & MyBatis: Java Configuration for Spring-Boot

I'm trying to code a web-app using Spring-Boot, HSQLDB, Flyway and MyBatis. I started without MyBatis and Flyway happily created the database every time I had removed the HSQLDB.
After I added MyBatis all was fine until I had to make changes in the initial SQL-file and removed the database. Now I'm failing to start the web-app. It seems as if Flyway and MyBatis somehow depend on each other.
My database configuration:
#Configuration
public class DatabaseConfiguration {
#Bean
public DataSource dataSource() { ... }
#Bean
public DataSourceTransactionManager transactionManager() { ... }
#Bean
public static MapperScannerConfigurer mapperScannerConfigurer() { ... }
#Bean
public DataSourceInitializer dataSourceInitializer() throws Exception { ... }
#Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception { ... }
#Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception { ... }
}
I fully understand that MapperScannerConfigurer, SqlSessionFactoryBean and SqlSessionTemplate are coming from MyBatis, all Flyway-stuff is done by Spring-Boot. From what I can tell Flyway needs a DataSource and MyBatis needs a database where the Flyway scripts ran already to allow the initialisation of the mappers.
The error I get is
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: MY_TABLE
### The error may exist in file [/home/work/Eclipse/com.sjngm.hs/target/classes/sqlmap/MyTableMapper.xml]
### The error may involve com.sjngm.hs.dao.mapper.MyTableMapper.getByName
### The error occurred while executing a query
### SQL: SELECT * FROM MY_TABLE WHERE Name = ?
### Cause: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: MY_TABLE
Note that there is no indication in the entire log-file that Flyway already did something. Also I can't find any CREATE TABLE in HSQLDB's files, which makes the above error saying that it can't find table MY_TABLE.
I already tried moving dataSource() to a new configuration class, but that didn't help.
What do I need to do to solve that indirect dependency?
What helped was making dataSource() a #FlywayDataSource.

Spring Boot - Loading Initial Data

I'm wondering what the best way to load initial database data before the application starts? What I'm looking for is something that will fill my H2 database with data.
For example, I have a domain model "User" I can access users by going to /users but initially there won't be any users in the database so I have to create them. Is there anyway to fill the database with data automatically?
At the moment I have a Bean that gets instantiated by the container and creates users for me.
Example:
#Component
public class DataLoader {
private UserRepository userRepository;
#Autowired
public DataLoader(UserRepository userRepository) {
this.userRepository = userRepository;
LoadUsers();
}
private void LoadUsers() {
userRepository.save(new User("lala", "lala", "lala"));
}
}
But I very much doubt that is the best way of doing it. Or is it?
You can create a data.sql file in your src/main/resources folder and it will be automatically executed on startup. In this file you can add some insert statements, eg.:
INSERT INTO users (username, firstname, lastname) VALUES
('lala', 'lala', 'lala'),
('lolo', 'lolo', 'lolo');
Similarly, you can create a schema.sql file (or schema-h2.sql) as well to create your schema:
CREATE TABLE task (
id INTEGER PRIMARY KEY,
description VARCHAR(64) NOT NULL,
completed BIT NOT NULL);
Though normally you shouldn't have to do this since Spring boot already configures Hibernate to create your schema based on your entities for an in memory database. If you really want to use schema.sql you'll have to disable this feature by adding this to your application.properties:
spring.jpa.hibernate.ddl-auto=none
More information can be found at the documentation about Database initialization.
If you're using Spring Boot 2, database initialization only works for embedded databases (H2, HSQLDB, ...). If you want to use it for other databases as well, you need to change the initialization mode property:
spring.sql.init.mode=always # Spring Boot >=v2.5.0
spring.datasource.initialization-mode=always # Spring Boot <v2.5.0
If you're using multiple database vendors, you can name your file data-h2.sql or data-mysql.sql depending on which database platform you want to use.
To make that work, you'll have to configure the datasource platform property:
spring.sql.init.platform=h2 # Spring Boot >=v2.5.0
spring.datasource.platform=h2 # Spring Boot <v2.5.0
If I just want to insert simple test data I often implement a ApplicationRunner. Implementations of this interface are run at application startup and can use e.g. a autowired repository to insert some test data.
I think such an implementation would be slightly more explicit than yours because the interface implies that your implementation contains something you would like to do directly after your application is ready.
Your implementation would look sth. like this:
#Component
public class DataLoader implements ApplicationRunner {
private UserRepository userRepository;
#Autowired
public DataLoader(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void run(ApplicationArguments args) {
userRepository.save(new User("lala", "lala", "lala"));
}
}
You can add a spring.datasource.data property to application.properties listing the sql files you want to run. Like this:
spring.datasource.data=classpath:accounts.sql, classpath:books.sql, classpath:reviews.sql
//or (depending on SB version)
spring.sql.init.data-locations=classpath:accounts.sql, classpath:books.sql, file:reviews.sql
The sql insert statements in each of these files will then be run, allowing you to keep things tidy.
If you put the files in the classpath, for example in src/main/resources they will be applied. Or replace classpath: with file: and use an absolute path to the file
If you want to run DDL type SQL then use:
spring.datasource.schema=classpath:create_account_table.sql
// depending on spring version
spring.sql.init.schema-locations=classpath:create_account_table.sql
Edit: these solutions are great to get you up and running quickly, however for a more production ready solution it would be worth looking at a framework such as flyway, or liquibase. These frameworks integrate well with spring, and provide a quick, consistent, version-controlled means of initialising schema, and standing-data.
There are multiple ways how to achieve this. I prefer to use one of following options:
Option 1: Initializing with CommandLineRunner bean:
#Bean
public CommandLineRunner loadData(CustomerRepository repository) {
return (args) -> {
// save a couple of customers
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
// fetch all customers
log.info("Customers found with findAll():");
log.info("-------------------------------");
for (Customer customer : repository.findAll()) {
log.info(customer.toString());
}
log.info("");
// fetch an individual customer by ID
Customer customer = repository.findOne(1L);
log.info("Customer found with findOne(1L):");
log.info("--------------------------------");
log.info(customer.toString());
log.info("");
// fetch customers by last name
log.info("Customer found with findByLastNameStartsWithIgnoreCase('Bauer'):");
log.info("--------------------------------------------");
for (Customer bauer : repository
.findByLastNameStartsWithIgnoreCase("Bauer")) {
log.info(bauer.toString());
}
log.info("");
}
}
Option 2: Initializing with schema and data SQL scripts
Prerequisites:
application.properties
spring.jpa.hibernate.ddl-auto=none
Explanation:
Without ddl-auto SQL scripts will be ignored by
Hibernate and trigger default behavior - scanning project for
#Entity and/or #Table annotated classes.
Then, in your MyApplication class paste this:
#Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:~/myDB;MV_STORE=false");
dataSource.setUsername("sa");
dataSource.setPassword("");
// schema init
Resource initSchema = new ClassPathResource("scripts/schema-h2.sql");
Resource initData = new ClassPathResource("scripts/data-h2.sql");
DatabasePopulator databasePopulator = new ResourceDatabasePopulator(initSchema, initData);
DatabasePopulatorUtils.execute(databasePopulator, dataSource);
return dataSource;
}
Where scripts folder is located under resources folder (IntelliJ Idea)
Hope it helps someone
Update 04-2021: Both options are great to combine with Spring Profiles as this will help you to avoid creating additional config files making your life as the developer easy.
You can use something like this:
#SpringBootApplication
public class Application {
#Autowired
private UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
InitializingBean sendDatabase() {
return () -> {
userRepository.save(new User("John"));
userRepository.save(new User("Rambo"));
};
}
}
In Spring Boot 2 data.sql was not working with me as in spring boot 1.5
import.sql
In addition, a file named import.sql in the root of the classpath is executed on startup if Hibernate creates the schema from scratch (that is, if the ddl-auto property is set to create or create-drop).
Note very important if you insert Keys cannot be duplicated do not use ddl-auto property is set to update because with each restart will insert same data again
For more information you vist spring websit
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
Spring Boot allows you to use a simple script to initialize your database, using Spring Batch.
Still, if you want to use something a bit more elaborated to manage DB versions and so on, Spring Boot integrates well with Flyway.
See also:
Spring Boot Database initialization
You can simply create a import.sql file in src/main/resources and Hibernate will execute it when the schema is created.
If you came here and nothing seems to work for you, then it might be the case that you are affected from some changes that were introduced with Spring Boot 2.5 and onwards.
Here is the total set of properties which I use for postgresql.
spring:
sql.init.mode: always <-----------------
datasource:
url: jdbc:postgresql://localhost:5432/products
username:
password:
jpa:
defer-datasource-initialization: true <------------------
hibernate:
ddl-auto: create-drop <----------------
database-platform: org.hibernate.dialect.PostgreSQLDialect
I have also marked with <--- the relevant properties for the current topic in order to achieve the following.
ORM vendor will create database schema for you from Java Entities model.
After database schema is created, initial data will be loaded to database from the file data.sql
Ps: Don't forget to add the file with initial data, data.sql under src/main/resources
Also as reference: Spring Boot 2.5 release notes
Here is the way I got that:
#Component
public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Autowired
private MovieRepositoryImpl movieRepository;
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
seedData();
}
private void seedData() {
movieRepository.save(new Movie("Example"));
// ... add more code
}
}
Thanks to the author of this article:
http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/
I solved similar problem this way:
#Component
public class DataLoader {
#Autowired
private UserRepository userRepository;
//method invoked during the startup
#PostConstruct
public void loadData() {
userRepository.save(new User("user"));
}
//method invoked during the shutdown
#PreDestroy
public void removeData() {
userRepository.deleteAll();
}
}
You're almost there!
#Component
public class DataLoader implements CommandLineRunner {
private UserRepository userRepository;
public DataLoader(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public void run(String... args) throws Exception {
LoadUsers()
}
private void LoadUsers() {
userRepository.save(new User("lala", "lala", "lala"));
}
}
you can register and event listener to achieve that like below:
#EventListener
public void seed(ContextRefreshedEvent event) {
userRepository.save(new User("lala", "lala", "lala"));
}
When the ContextRefreshEvent is fired, we get access to all autowired beans in the application — including models and repositories.
If someone are struggling in make this to work even following the accepted answer, for me only work adding in my src/test/resources/application.yml the H2 datasource details:
spring:
datasource:
platform: h2
url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
driver-class-name: org.h2.Driver
username: sa
password:
If you want to insert only few rows and u have JPA Setup. You can use below
#SpringBootApplication
#Slf4j
public class HospitalManagementApplication {
public static void main(String[] args) {
SpringApplication.run(HospitalManagementApplication.class, args);
}
#Bean
ApplicationRunner init(PatientRepository repository) {
return (ApplicationArguments args) -> dataSetup(repository);
}
public void dataSetup(PatientRepository repository){
//inserts
}
You can use the below code. In the following code a database insertion occurs during the startup of the spring boot application.
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private IService<Car> service;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) throws Exception {
for(int i=1; i<=1000; i++) {
Car car = new Car();
car.setName("Car Name "+i);
book.setPrice(50 + i);
service.saveOrUpdate(car);
}
}
}
This will also work.
#Bean
CommandLineRunner init (StudentRepo studentRepo){
return args -> {
// Adding two students objects
List<String> names = Arrays.asList("udara", "sampath");
names.forEach(name -> studentRepo.save(new Student(name)));
};
}
The most compact (for dynamic data) put #mathias-dpunkt solution into MainApp (with Lombok #AllArgsConstructor):
#SpringBootApplication
#AllArgsConstructor
public class RestaurantVotingApplication implements ApplicationRunner {
private final VoteRepository voteRepository;
private final UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(RestaurantVotingApplication.class, args);
}
#Override
public void run(ApplicationArguments args) {
voteRepository.save(new Vote(userRepository.getOne(1), LocalDate.now(), LocalTime.now()));
}
}
One possibility is using incorrect JDBC URL. make sure it is jdbc:h2:mem:testdb
I created a library that facilitates initial/demo data loading in a Spring Boot application. You can find it at https://github.com/piotrpolak/spring-boot-data-fixtures
Once the data fixtures starter is on the classpath, it will automatically try to load DICTIONARY data upon application startup (this behavior can be controlled by properties) - all you need to do is to register a bean implementing DataFixture.
I find loading initial data by code superior to loading it using SQL scripts:
the logic of your fixtures lives close to your application logic/domain model and it is subject to refactoring as your domain evolves
you benefit from incremental demo data updates - imagine a QA environment with some user data (that needs not to be lost after application deploy) but at the same time you want to add data for the new features you developed
Example data fixture:
/**
* You can have as many fixture classes as you want.
* #Order annotation is respected for the fixtures belonging to the same set.
* You can make your demo database to be incrementally updated with fresh data
* each time the application is redeployed - all you need to do is to write
* a good condition in `canBeLoaded()` method.
*/
#Component
public class InitialDataFixture implements DataFixture {
private final LanguageRepository languageRepository;
// ...
#Override
public DataFixtureSet getSet() {
return DataFixtureSet.DICTIONARY;
}
/**
* We want to make sure the fixture is applied once and once only.
* A more sophisticated condition can be used to create incremental demo data
* over time without the need to reset the QA database (for example).
*/
#Override
public boolean canBeLoaded() {
return languageRepository.size() == 0;
}
/**
* The actual application of the fixture.
* Assuming that data fixtures are registered as beans, this method can call
* other services and/or repositories.
*/
#Override
public void load() {
languageRepository.saveAll(Arrays.asList(
new Language("en-US"), new Language("pl-PL")));
}
}
The concept is inspired by the Symfony Doctrine Data Fixtures bundle.
For those using MysqlDriver, I tried using Init attribute of #bean annotation and it works.
After created the Schema and Data sql file in the path of resources\Scripts
Add the line in application.properties
spring.jpa.hibernate.ddl-auto=none
Edit the Application content:
package com.spring_mvaen.demo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
System.out.println("Hello world from Command Line Runner");
}
#Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db_spring_rest?useUnicode=true&useLegacyDatetimeCode=fa lse&serverTimezone=UTC&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
// schema init
Resource initSchema = new ClassPathResource("scripts/schema.sql");
Resource initData = new ClassPathResource("scripts/data.sql");
DatabasePopulator databasePopulator = new ResourceDatabasePopulator(initSchema, initData);
DatabasePopulatorUtils.execute(databasePopulator, dataSource);
return dataSource;
}
}
If you want to do insert quick some queries, you can do with h2 data.sql queries as well
application.properties include:
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
#This directs the data.sql file and help it to run
spring.sql.init.data-locations=classpath:data.sql
spring.jpa.defer-datasource-initialization=true
data.sql file include:
INSERT INTO todo (id, username, description, target_date, is_done) VALUES (10001, 'lighteducation', 'Learn dance', CURRENT_DATE ,false);
INSERT INTO todo (id, username, description, target_date, is_done) VALUES (10002, 'lighteducation', 'Learn Angular14', CURRENT_DATE, false);
INSERT INTO todo (id, username, description, target_date, is_done) VALUES (10003, 'lighteducation', 'Learn Microservices', CURRENT_DATE,false);
P.S.: data.sql file should be inside src/main/resources
Your #Entity include
#Getter
#Setter
#AllArgsConstructor
#ToString
#Entity
public class Todo {
#Id
#GeneratedValue
private Long id;
private String username;
private String description;
private Date targetDate;
private boolean isDone;
protected Todo() {
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Todo todo = (Todo) o;
return id == todo.id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
That is it basically. it will be in memory, it means when you restart application data will arese and will be again same as queries show.
But it is easy for quick check
also you can access the path with http://localhost:8080/h2-console/ or you can edit the path from .properties file

Resources