Как создать базу данных с пролетом?
Вопрос: Возможно ли создать новую БД в миграции script, а затем подключиться к ней? Как?
Мой сценарий:
Я пытаюсь использовать пролет в моем проекте Java (приложение RESTful с использованием Jersey2.4 + tomcat 7 + PostgreSQL 9.3.1 + EclipseLink) для управления изменениями между разными разработчиками, которые используют git. Я написал init script и запустил его с помощью:
PGPASSWORD='123456' psql -U postgres -f migration/V1__initDB.sql
и он работал нормально. Проблема в том, что я не могу создать новую БД с моими скриптами. когда я включаю следующую строку в свой script:
CREATE DATABASE my_database OWNER postgres ENCODING 'UTF8';
Я получаю эту ошибку:
org.postgresql.util.PSQLException: ERROR: CREATE DATABASE cannot run inside a transaction block
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2157)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1886)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:555)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:403)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:395)
at com.googlecode.flyway.core.dbsupport.JdbcTemplate.executeStatement(JdbcTemplate.java:230)
at com.googlecode.flyway.core.dbsupport.SqlScript.execute(SqlScript.java:89)
at com.googlecode.flyway.core.resolver.sql.SqlMigrationExecutor.execute(SqlMigrationExecutor.java:72)
at com.googlecode.flyway.core.command.DbMigrate$2.doInTransaction(DbMigrate.java:252)
at com.googlecode.flyway.core.command.DbMigrate$2.doInTransaction(DbMigrate.java:250)
at com.googlecode.flyway.core.util.jdbc.TransactionTemplate.execute(TransactionTemplate.java:56)
at com.googlecode.flyway.core.command.DbMigrate.applyMigration(DbMigrate.java:250)
at com.googlecode.flyway.core.command.DbMigrate.access$700(DbMigrate.java:47)
at com.googlecode.flyway.core.command.DbMigrate$1.doInTransaction(DbMigrate.java:189)
at com.googlecode.flyway.core.command.DbMigrate$1.doInTransaction(DbMigrate.java:138)
at com.googlecode.flyway.core.util.jdbc.TransactionTemplate.execute(TransactionTemplate.java:56)
at com.googlecode.flyway.core.command.DbMigrate.migrate(DbMigrate.java:137)
at com.googlecode.flyway.core.Flyway$1.execute(Flyway.java:872)
at com.googlecode.flyway.core.Flyway$1.execute(Flyway.java:819)
at com.googlecode.flyway.core.Flyway.execute(Flyway.java:1200)
at com.googlecode.flyway.core.Flyway.migrate(Flyway.java:819)
at ir.chom.MyApp.<init>(MyApp.java:28)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.glassfish.hk2.utilities.reflection.ReflectionHelper.makeMe(ReflectionHelper.java:1117)
at org.jvnet.hk2.internal.Utilities.justCreate(Utilities.java:867)
at org.jvnet.hk2.internal.ServiceLocatorImpl.create(ServiceLocatorImpl.java:814)
at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:906)
at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:898)
at org.glassfish.jersey.server.ApplicationHandler.createApplication(ApplicationHandler.java:300)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:279)
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:302)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:167)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:349)
at javax.servlet.GenericServlet.init(GenericServlet.java:160)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1280)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1091)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5176)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5460)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3954)
at org.apache.catalina.loader.WebappLoader.backgroundProcess(WebappLoader.java:426)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1345)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1530)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1540)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1540)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1519)
at java.lang.Thread.run(Thread.java:724)
Кажется, что это проблема с JDBC, которая использует опцию autocommit
. Этот параметр можно отключить с помощью следующего:
Connection connection = dataSource.getConnection();
Connection.setAutoCommit(false); // Disables auto-commit.
но я не знаю, как передать этот параметр на пролетное соединение. Также, если я решаю это, я думаю, что у меня будет проблема с передачей пароля команде \c
.
Ответы
Ответ 1
Flyway всегда работает в базе данных, используемой в строке подключения jdbc.
После подключения все сценарии выполняются внутри транзакции. Поскольку CREATE DATABASE не поддерживается в транзакциях, вы не сможете выполнить то, что хотите.
Однако вы можете создать схему вместо этого. Flyway даже сделает это за вас, если вы укажете его на несуществующий.
Ответ 2
Я не знаю, возможно ли это сделать в пролете.
Flyway предназначен для подключения к уже существующей базе данных (пустой или нет). Также было бы хорошей практикой, чтобы сохранить создание базы данных отдельно от миграции базы данных.
Ответ 3
Вот обходной путь, который сработал для меня (при условии использования плагина Maven):
Настройте плагин с двумя исполнениями. Первое выполнение создает базу данных. Второе выполнение переносит существующую базу данных.
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>${flyway.version}</version>
<executions>
<execution>
<id>create-db</id>
<goals>
<goal>migrate</goal>
</goals>
<configuration>
<driver>org.postgresql.Driver</driver>
<url>jdbc:postgresql://database-server/</url>
<user>postgres</user>
<password>password</password>
<placeholders>
<DATABASE.NAME>MyDatabase</DATABASE.NAME>
</placeholders>
<locations>
<location>com/foo/bar/database/create</location>
</locations>
</configuration>
</execution>
<execution>
<id>migrate-db</id>
<goals>
<goal>migrate</goal>
</goals>
<configuration>
<driver>org.postgresql.Driver</driver>
<url>jdbc:postgresql://database-server/MyDatabase</url>
<user>postgres</user>
<password>password</password>
<locations>
<location>com/foo/bar/database/migrate</location>
</locations>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
</dependencies>
</plugin>
Затем добавьте V1__Created_database.sql
в V1__Created_database.sql
com/foo/bar/database/create
. Этот файл содержит:
CREATE DATABASE ${DATABASE.NAME}
Ответ 4
Если у вас есть команда создания базы данных в V1 ваших скриптов, flyway может сгенерировать ее, но не базу данных:
flyway -baselineOnMigrate=true -url=jdbc:mysql://localhost/ -schemas=test_db -user=root -password=root_pass -locations=filesystem:/path/to/scrips/ migrate
и аналогично этому в файле script:
DROP SCHEMA IF EXISTS `test_db` ;
CREATE SCHEMA `test_db` COLLATE utf8_general_ci ;