I want to fill some tables of my DB from a text file on startup, I want my initialization method to be called only when my application do start.
I am using Spring (+MVC) and Hibernate with MySQL.
how can I do?
I want to fill some tables of my DB from a text file on startup, I want my initialization method to be called only when my application do start.
I am using Spring (+MVC) and Hibernate with MySQL.
how can I do?
You can create an application listener, it's designed specifically for such needs. In this case it will be executed every time context is started (or refreshed).
@Component
public class DatabaseFillerOnStartup implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
...
}
}
Hibernate comes with an way to add some files with sql statements that will be executed on startup.
The parameter is hibernate.hbm2ddl.import_files
.
@See Hibernate Reference: Chapter 3.4. Optional configuration properties
Comma-separated names of the optional files containing SQL DML statements executed during the SessionFactory creation. This is useful for testing or demoing: by adding INSERT statements for example you can populate your database with a minimal set of data when it is deployed.
File order matters, the statements of a give file are executed before the statements of the following files. These statements are only executed if the schema is created ie if hibernate.hbm2ddl.auto is set to create or create-drop.
e.g. /humans.sql,/dogs.sql
I fond some hints that this may only work if hibernate is started in 'create' mode. But I am not sure.
Use the postconstuct annotation somewhere inside a bean :
@PostConstruct
public void init() {
//startup logic here
}
Probably makes (desgin) sense to use a configuration bean, but it can be any bean at all.
Two options,
First: Let hibernate create your ddl everytime the services is startup(Only for local or dev environment)
<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="false"
p:generateDdl="true"/>
Or create in spring a Bean with init-lazy false and add in there the logic to create the database if you dont have it or inject your script, or do whatever you want.
<bean id="bootstrapStartup" class="com.tscompany.rest.bootstrap.BootstrapStartup" lazy-init="false" init-method="init"/>
You can create some bean in root-context configuration - like
<bean id="someBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="someProperty">
... description of the property
</property>
<property name="targetMethod" value="methodName" />
</bean>
Thus - on application startup method "methodName" will be invoked. We have DB upgrades implemented in such a way.
If you have your hibernate.hbm2ddl.auto
property set to create
or create-drop
then the easiest way to do that is by setting hibernate.hbm2ddl.import_files
property with SQL file name that contains SQL statements to load your initial data into the database schema, e.g. initial application users.
The following is an example method that I have in my DatabaseConfig
class. It sets hibernate.hbm2ddl.import_files
property with SQL file name import_initial_data.sql that contains SQL Insert statements to load my initial data to the database schema.
@Bean
public LocalSessionFactoryBean hibernate5SessionFactoryBean(){
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource((DataSource) appContext.getBean("DataSource"));
localSessionFactoryBean.setAnnotatedClasses( AppUser.class );
Properties properties = new Properties();
properties.put( "hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
properties.put("hibernate.hbm2ddl.auto","create-drop");
properties.put("hibernate.hbm2ddl.import_files", "import_initial_data.sql");
properties.put("hibernate.show_sql","true");
localSessionFactoryBean.setHibernateProperties(properties);
return localSessionFactoryBean;
}
This is my AppUser.java
in model
package:
package com.beniregev.model;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor(access = AccessLevel.PUBLIC)
public class AppUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(nullable = false, unique = true)
private String userName;
@Column(nullable = false)
private String password;
public AppUser(String userName, String password) {
this.userName = userName;
this.password = password;
}
}
Maven dependency for lombok:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
<scope>provided</scope>
</dependency>
The import_initial_data.sql file that I have in resources
directory:
INSERT INTO appuser(username, password) VALUES ('jesus', 'christ');
INSERT INTO appuser(username, password) VALUES ('mary', 'virgin');
INSERT INTO appuser(username, password) VALUES ('josef', 'who?');
INSERT INTO appuser(username, password) VALUES ('jeremaia', 'profet');
COMMIT;
And the result of *SELECT * FROM appuser;*: