JPA初始化数据

背景

我们常常需要在应用的初始化阶段自动向数据库中插入一些基础数据,比如默认分类或省市信息等。

SpringBoot应用可以通过在application.properties文件中配置spring.jpa.generate-ddlspring.jpa.hibernate.ddl-auto这两个属性,或者在 src/main/resources目录下创建schema.sqldata.sql文件来实现初始化数据的目的,本文介绍的另外一种用法,直接在容器启动时通过JPA操作实现初始化数据。

方案

监听ApplicationReadyEvent事件

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class DemoData
{
@Autowired
private final EntityRepository repo;

@EventListener
public void appReady(ApplicationReadyEvent event)
{
repo.save(new Entity(...));
}
}

实现CommandLineRunner接口

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class DemoData implements CommandLineRunner
{
@Autowired
private final EntityRepository repo;

@Override
public void run(String...args) throws Exception
{
repo.save(new Entity(...));
}
}

实现ApplicationRunner接口

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class DemoData implements ApplicationRunner
{
@Autowired
private final EntityRepository repo;

@Override
public void run(ApplicationArguments args) throws Exception
{
repo.save(new Entity(...));
}
}

示例

定义一个数据仓库,用来保存实体数据:

1
2
3
4
5
6
7
8
9
10
package me.ningyu.burnerlist.repository;

import me.ningyu.burnerlist.entity.Kitchenware;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface KitchenwareRepository extends JpaRepository<Kitchenware, String>
{
}

定义一个配置类,注册一个CommandLineRunner到容器中:

需要特别注意:因为每次应用启动时都会执行这个配置类,即每次重启都会初始化数据,为了防止出现重复数据,就需要在插入之前先判断是否存在,不存在时才新增。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package me.ningyu.burnerlist.common.config;

import me.ningyu.burnerlist.entity.Kitchenware;
import me.ningyu.burnerlist.repository.KitchenwareRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Example;

@Configuration
public class DataInitializeConfig
{

@Bean
public CommandLineRunner loadData(KitchenwareRepository kitchenwareRepository)
{
return args ->
{
Kitchenware frontBurner = new Kitchenware("Front Burner", "Top Priority", "重要项目", null);
if (!kitchenwareRepository.exists(Example.of(frontBurner)))
{
kitchenwareRepository.save(frontBurner);
}

Kitchenware backBurner = new Kitchenware("Back Burner", "Second Top Priority", "次重要项目", null);
if (!kitchenwareRepository.exists(Example.of(backBurner)))
{
kitchenwareRepository.save(backBurner);
}

Kitchenware counterSpace = new Kitchenware("Counter Space", "Notes", "重要项目的笔记", null);
if (!kitchenwareRepository.exists(Example.of(counterSpace)))
{
kitchenwareRepository.save(counterSpace);
}

Kitchenware sink = new Kitchenware("Sink", "Misc Task", "杂项", null);
if (!kitchenwareRepository.exists(Example.of(sink)))
{
kitchenwareRepository.save(sink);
}
};
}
}

参考

Execute schema.sql and data.sql on startup of Spring Boot - Walking Techie