В этой статье описывается метод создания mysql
Процесс
, управляемый приложением Spring Boot, зависит от определения свойства application.properties
, ${mysqld.home}
. Если свойство определено, соответствующий компонент @Configuration
с помощью invoke mysqld
с опцией --initialize-insecure
для создания базы данных, а затем создания и управления mysqld
|/Процесс в течение срока службы приложения Spring Boot, включая плавное завершение работы при завершении работы приложения.
Предоставляется полный javadoc .
Теория работы
Конфигурация Mysqld
представляет собой @ConditionalOnProperty
аннотацию для ${mysqld.home}
; если свойство определено, Процесс
@Bean
создается под управлением сервера MySQL. Если ${mysqld.datadir}
не существует, mysqld
вызывается с параметром --initialize-insecure
для первого создания базы данных. Определен метод @PreDestroy
для уничтожения mysqld
Процесс
при завершении работы приложения.
@Configuration @ConditionalOnProperty(name = "mysqld.home", havingValue = "") @NoArgsConstructor @ToString @Log4j2 public class MysqldConfiguration { @Value("${mysqld.home}") private File home; @Value("${mysqld.defaults.file:${mysqld.home}/my.cnf}") private File defaults; @Value("${mysqld.datadir:${mysqld.home}/data}") private File datadir; @Value("${mysqld.port}") private Integer port; @Value("${mysqld.socket:${mysqld.home}/socket}") private File socket; @Value("${logging.path}/mysqld.log") private File console; private volatile Process mysqld = null; ... @Bean public Process mysqld() throws IOException { if (mysqld == null) { synchronized (this) { if (mysqld == null) { Files.createDirectories(home.toPath()); Files.createDirectories(datadir.toPath().getParent()); Files.createDirectories(console.toPath().getParent()); String defaultsArg = "--no-defaults"; if (defaults.exists()) { defaultsArg = + defaults.getAbsolutePath(); } String datadirArg = + datadir.getAbsolutePath(); String socketArg = + socket.getAbsolutePath(); String portArg = + port; if (! datadir.exists()) { try { new ProcessBuilder("mysqld", defaultsArg, datadirArg, "--initialize-insecure") .directory(home) .inheritIO() .redirectOutput(Redirect.to(console)) .redirectErrorStream(true) .start() .waitFor(); } catch (InterruptedException exception) { } } if (datadir.exists()) { socket.delete(); mysqld = new ProcessBuilder("mysqld", defaultsArg, datadirArg, socketArg, portArg) .directory(home) .inheritIO() .redirectOutput(Redirect.appendTo(console)) .redirectErrorStream(true) .start(); while (! socket.exists()) { try { mysqld.waitFor(15, SECONDS); } catch (InterruptedException exception) { } if (mysqld.isAlive()) { continue; } else { throw new IllegalStateException("mysqld not started"); } } } else { throw new IllegalStateException("mysqld datadir does not exist"); } } } } return mysqld; } @PreDestroy public void destroy() { if (mysqld != null) { try { for (int i = 0; i < 8; i+= 1) { if (mysqld.isAlive()) { mysqld.destroy(); mysqld.waitFor(15, SECONDS); } else { break; } } } catch (InterruptedException exception) { } try { if (mysqld.isAlive()) { mysqld.destroyForcibly().waitFor(60, SECONDS); } } catch (InterruptedException exception) { } } } }
Сервер mysqld
настроен с параметром --socket=${mysqld.socket}
с целью уведомления приложения Spring Boot о запуске сервера: В то время как MySQL Connector/J не поддерживает доменные сокеты UNIX, приведенный выше код ожидает, пока сервер mysqld
создаст сокет, чтобы убедиться, что сервер запущен, прежде чем продолжить. Компонент Mysqld
будет просто отслеживать, что Процесс
все еще активен. Этот @Component
зависит от mysqld
@Bean
который, в свою очередь, зависит от свойства ${mysqld.home}
.
@Component @ConditionalOnBean(name = { "mysqld" }) @NoArgsConstructor @ToString @Log4j2 public class MysqldComponent { @Autowired private Process mysqld; @Scheduled(fixedRate = 15 * 1000) public void run() { if (mysqld != null) { if (mysqld.isAlive()) { try { mysqld.waitFor(15, SECONDS); } catch (InterruptedException exception) { } } else { throw new IllegalStateException("mysqld is not running"); } } } }
В соответствии с указаниями Справочного руководства по пружинным ботинкам , Компонент EntityManagerFactory
предоставляется для указания mysqld
Process
требуется JPA.
@Component @ConditionalOnProperty(name = "mysqld.home", havingValue = "") @ToString @Log4j2 public class EntityManagerFactoryComponent extends EntityManagerFactoryDependsOnPostProcessor { @Autowired private Process mysqld; public EntityManagerFactoryComponent() { super("mysqld"); } }
Приложение может интегрировать эту функциональность, аннотируя некоторый компонент (предположительно, тот, который зависит от Репозитория
или JpaRepository
) с помощью:
@Component @ComponentScan(basePackageClasses = { ball.spring.mysqld.MysqldComponent.class }) public class SomeComponent { ... }
Сценарий оболочки
Следующий сценарий оболочки может быть помещен в каталог ${mysql.home}
, чтобы удобно запускать сервер MySQL из оболочки.
#!/bin/bash PRG="$0" while [ -h "$PRG" ]; do ls=$(ls -ld "$PRG") link=$(expr "$ls" : '.*-> \(.*\)$') if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=$(dirname "$PRG")"/$link" fi done cd $(dirname "$PRG") MYCNF=$(pwd)/my.cnf DATADIR=$(pwd)/data SOCKET=$(pwd)/socket if [ ! -f "${MYCNF}" ]; then cat > "${MYCNF}" <
Резюме
Описанный здесь метод может быть использован с другими приложениями баз данных (например, PostgreSQL).
Оригинал: “https://dev.to/allenball/spring-embedded-mysql-server-2j14”