mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
ENH: Add DB Migration runner (as alternative to running via FlywayDB etc) #636
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Helper to find classes taking into account the context class loader.
|
||||
*/
|
||||
@@ -70,6 +74,10 @@ public class ClassLoadConfig {
|
||||
}
|
||||
}
|
||||
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
return context.getResources(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given class is present.
|
||||
*/
|
||||
@@ -122,6 +130,13 @@ public class ClassLoadConfig {
|
||||
return (loader != null) ? loader: callerLoader;
|
||||
}
|
||||
|
||||
Enumeration<URL> getResources(String name) throws IOException {
|
||||
if (preferredLoader != null) {
|
||||
return preferredLoader.getResources(name);
|
||||
}
|
||||
return contextLoader().getResources(name);
|
||||
}
|
||||
|
||||
Class<?> forName(String name) throws ClassNotFoundException {
|
||||
|
||||
if (preferredLoader != null) {
|
||||
|
||||
@@ -9,6 +9,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
@@ -522,4 +526,25 @@ public class DatabasePlatform {
|
||||
return disallowBatchOnCascade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the table exists.
|
||||
*/
|
||||
public boolean tableExists(Connection connection, String catalog, String schema, String table) throws SQLException {
|
||||
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
ResultSet tables = metaData.getTables(catalog, schema, table, null);
|
||||
try {
|
||||
return tables.next();
|
||||
} finally {
|
||||
close(tables);
|
||||
}
|
||||
}
|
||||
|
||||
protected void close(ResultSet resultSet) {
|
||||
try {
|
||||
resultSet.close();
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing resultSet", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebean.dbmigration;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.model.CurrentModel;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.util.JdbcClose;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.File;
|
||||
@@ -13,6 +14,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.io.Reader;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* Controls the generation and execution of "Create All" and "Drop All" DDL scripts.
|
||||
@@ -81,6 +83,21 @@ public class DdlGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
// get a connection without threadLocal
|
||||
Connection connection = server.createTransaction().getConnection();
|
||||
try {
|
||||
return runner.runAll(content, connection);
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
protected void runDropSql() throws IOException {
|
||||
if (!createOnly) {
|
||||
if (dropAllContent == null) {
|
||||
@@ -110,9 +127,8 @@ public class DdlGenerator {
|
||||
if (sqlScript != null) {
|
||||
InputStream is = getClassLoader().getResourceAsStream(sqlScript);
|
||||
if (is != null) {
|
||||
DdlRunner runner = new DdlRunner(false, sqlScript);
|
||||
String content = readContent(new InputStreamReader(is));
|
||||
runner.runAll(content, server);
|
||||
runScript(false, content, sqlScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,13 +237,4 @@ public class DdlGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
return runner.runAll(content, server);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.avaje.ebean.dbmigration;
|
||||
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -37,29 +35,33 @@ public class DdlRunner {
|
||||
/**
|
||||
* Parse the content into sql statements and execute them in a transaction.
|
||||
*/
|
||||
public int runAll(String content, SpiEbeanServer server) {
|
||||
public int runAll(String content, Connection connection) {
|
||||
|
||||
List<String> statements = ddlParser.parse(new StringReader(content));
|
||||
return runStatements(statements, server);
|
||||
return runStatements(statements, connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the statements in a single transaction.
|
||||
*/
|
||||
public int runStatements(List<String> statements, SpiEbeanServer server) {
|
||||
public int runStatements(List<String> statements, Connection connection) {
|
||||
|
||||
Transaction t = server.createTransaction();
|
||||
try {
|
||||
int statementCount = runStatements(expectErrors, statements, t.getConnection());
|
||||
t.commit();
|
||||
|
||||
int statementCount = runStatements(expectErrors, statements, connection);
|
||||
connection.commit();
|
||||
return statementCount;
|
||||
|
||||
} catch (Exception e) {
|
||||
rollback(connection);
|
||||
throw new PersistenceException("Error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
t.end();
|
||||
private void rollback(Connection connection) {
|
||||
try {
|
||||
connection.rollback();
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error trying to rollback connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.avaje.ebean.dbmigration;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.runner.LocalMigrationResource;
|
||||
import com.avaje.ebean.dbmigration.runner.LocalMigrationResources;
|
||||
import com.avaje.ebean.dbmigration.runner.MigrationTable;
|
||||
import com.avaje.ebeaninternal.util.JdbcClose;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MigrationRunner {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MigrationRunner.class);
|
||||
|
||||
final EbeanServer server;
|
||||
final ServerConfig config;
|
||||
|
||||
private final DbMigrationConfig migrationConfig;
|
||||
|
||||
public MigrationRunner(EbeanServer server) {
|
||||
this.server = server;
|
||||
this.config = server.getPluginApi().getServerConfig();
|
||||
migrationConfig = this.config.getMigrationConfig();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
LocalMigrationResources resources = new LocalMigrationResources(config);
|
||||
if (!resources.readResources()) {
|
||||
logger.debug("no migrations to check");
|
||||
return;
|
||||
}
|
||||
|
||||
Transaction transaction = server.createTransaction();
|
||||
Connection connection = transaction.getConnection();
|
||||
try {
|
||||
MigrationTable table = new MigrationTable(server, migrationConfig, connection);
|
||||
table.createIfNeeded();
|
||||
|
||||
LocalMigrationResource priorVersion = null;
|
||||
List<LocalMigrationResource> localVersions = resources.getVersions();
|
||||
for (int i = 0; i < localVersions.size(); i++) {
|
||||
LocalMigrationResource localVersion = localVersions.get(i);
|
||||
if (i > 0) {
|
||||
priorVersion = localVersions.get(i-1);
|
||||
}
|
||||
if (!table.shouldRun(localVersion, priorVersion)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
table.commit();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,13 @@ public class MigrationVersion implements Comparable<MigrationVersion> {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full version.
|
||||
*/
|
||||
public String getFull() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
/**
|
||||
* Calculates the checksum for the given string content.
|
||||
*/
|
||||
public class Checksum {
|
||||
|
||||
/**
|
||||
* Returns the checksum of this string.
|
||||
*/
|
||||
public static int calculate(String str) {
|
||||
|
||||
final CRC32 crc32 = new CRC32();
|
||||
|
||||
BufferedReader bufferedReader = new BufferedReader(new StringReader(str));
|
||||
try {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
crc32.update(line.getBytes("UTF-8"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to calculate checksum", e);
|
||||
}
|
||||
|
||||
return (int) crc32.getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.dbmigration.model.MigrationVersion;
|
||||
import org.avaje.classpath.scanner.Resource;
|
||||
|
||||
/**
|
||||
* A DB migration resource.
|
||||
*/
|
||||
public class LocalMigrationResource implements Comparable<LocalMigrationResource> {
|
||||
|
||||
private final MigrationVersion version;
|
||||
|
||||
private final String location;
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
public LocalMigrationResource(MigrationVersion version, String location, Resource resource) {
|
||||
this.version = version;
|
||||
this.location = location;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return version.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default ordering by version.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(LocalMigrationResource o) {
|
||||
return version.compareTo(o.version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying migration version.
|
||||
*/
|
||||
public MigrationVersion getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource location.
|
||||
*/
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content for the migration apply ddl script.
|
||||
*/
|
||||
public String getContent() {
|
||||
return resource.loadAsString("UTF-8");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.dbmigration.model.MigrationVersion;
|
||||
import org.avaje.classpath.scanner.Location;
|
||||
import org.avaje.classpath.scanner.MatchResource;
|
||||
import org.avaje.classpath.scanner.Resource;
|
||||
import org.avaje.classpath.scanner.Scanner;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class LocalMigrationResources {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LocalMigrationResources.class);
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final DbMigrationConfig migrationConfig;
|
||||
|
||||
private final List<LocalMigrationResource> versions = new ArrayList<LocalMigrationResource>();
|
||||
|
||||
public LocalMigrationResources(ServerConfig serverConfig) {
|
||||
this.serverConfig = serverConfig;
|
||||
this.migrationConfig = serverConfig.getMigrationConfig();
|
||||
}
|
||||
|
||||
public boolean readResources() {
|
||||
|
||||
String migrationPath = migrationConfig.getMigrationPath();
|
||||
|
||||
ClassLoader classLoader = serverConfig.getClassLoadConfig().getClassLoader();
|
||||
|
||||
Scanner scanner = new Scanner(classLoader);
|
||||
List<Resource> resourceList = scanner.scanForResources(migrationPath, new Match(migrationConfig));
|
||||
|
||||
logger.debug("resources: {}", resourceList);
|
||||
|
||||
for (Resource resource : resourceList) {
|
||||
String filename = resource.getFilename();
|
||||
if (filename.endsWith(migrationConfig.getApplySuffix())) {
|
||||
int pos = filename.lastIndexOf(migrationConfig.getApplySuffix());
|
||||
String mainName = filename.substring(0, pos);
|
||||
|
||||
MigrationVersion migrationVersion = MigrationVersion.parse(mainName);
|
||||
LocalMigrationResource res = new LocalMigrationResource(migrationVersion, resource.getLocation(), resource);
|
||||
versions.add(res);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(versions);
|
||||
return !versions.isEmpty();
|
||||
}
|
||||
|
||||
public List<LocalMigrationResource> getVersions() {
|
||||
return versions;
|
||||
}
|
||||
|
||||
|
||||
static class Match implements MatchResource {
|
||||
|
||||
final DbMigrationConfig migrationConfig;
|
||||
|
||||
Match(DbMigrationConfig migrationConfig) {
|
||||
this.migrationConfig = migrationConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(String name) {
|
||||
|
||||
return name.endsWith(migrationConfig.getApplySuffix())
|
||||
|| name.endsWith(migrationConfig.getModelSuffix())
|
||||
|| name.endsWith(migrationConfig.getDropSuffix())
|
||||
|| name.endsWith(migrationConfig.getRollbackSuffix());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MigrationMetaRow implements Comparable<MigrationMetaRow> {
|
||||
|
||||
int id;
|
||||
String status;
|
||||
String runVersion;
|
||||
String depVersion;
|
||||
String comment;
|
||||
int checksum;
|
||||
Timestamp runOn;
|
||||
String runBy;
|
||||
|
||||
@Override
|
||||
public int compareTo(MigrationMetaRow o) {
|
||||
return (id < o.id) ? -1 : ((id == o.id) ? 0 : 1);
|
||||
}
|
||||
|
||||
|
||||
//String sql = "insert into ebean_migration (id,status,run_version,dep_version,comment,checksum,run_on,run_by,run_ip) "+
|
||||
// "values (?,?,?,?,?,?,?,?,?)";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.dbmigration.DdlRunner;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* Created by rob on 5/02/16.
|
||||
*/
|
||||
public class MigrationScriptRunner {
|
||||
|
||||
final Connection connection;
|
||||
|
||||
public MigrationScriptRunner(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the DDL statements in the script.
|
||||
*/
|
||||
public int runScript(boolean expectErrors, String content, String scriptName) {
|
||||
|
||||
DdlRunner runner = new DdlRunner(expectErrors, scriptName);
|
||||
return runner.runAll(content, connection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
import com.avaje.ebean.EbeanServer;
|
||||
import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.config.DbMigrationConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.plugin.SpiServer;
|
||||
import com.avaje.ebeaninternal.server.transaction.ExternalJdbcTransaction;
|
||||
import com.avaje.ebeaninternal.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by rob on 5/02/16.
|
||||
*/
|
||||
public class MigrationTable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MigrationTable.class);
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
private final EbeanServer server;
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
|
||||
private final DbMigrationConfig migrationConfig;
|
||||
|
||||
private final String catalog;
|
||||
private final String schema;
|
||||
private final String table;
|
||||
private final ServerConfig serverConfig;
|
||||
private final String envUserName;
|
||||
|
||||
int currentSearchPosition;
|
||||
|
||||
List<SqlRow> metaRows;
|
||||
|
||||
public MigrationTable(EbeanServer server, DbMigrationConfig migrationConfig, Connection connection) {
|
||||
this.connection = connection;
|
||||
this.server = server;
|
||||
|
||||
SpiServer pluginApi = server.getPluginApi();
|
||||
this.serverConfig = pluginApi.getServerConfig();
|
||||
this.databasePlatform = pluginApi.getDatabasePlatform();
|
||||
this.migrationConfig = migrationConfig;
|
||||
this.catalog = null;
|
||||
this.schema = null;
|
||||
this.table = "ebean_migration";
|
||||
|
||||
this.envUserName = System.getProperty("user.name");
|
||||
}
|
||||
|
||||
|
||||
public void createIfNeeded() throws SQLException, IOException {
|
||||
|
||||
if (!tableExists(connection)) {
|
||||
createTable(connection);
|
||||
}
|
||||
|
||||
ExternalJdbcTransaction t = new ExternalJdbcTransaction(connection);
|
||||
SqlQuery sqlQuery = server.createSqlQuery("select * from ebean_migration order by id for update");
|
||||
metaRows = server.findList(sqlQuery, t);
|
||||
}
|
||||
|
||||
private void createTable(Connection connection) throws IOException {
|
||||
|
||||
String script = getCreateTableScript();
|
||||
MigrationScriptRunner run = new MigrationScriptRunner(connection);
|
||||
run.runScript(false, script, "create migration tables");
|
||||
}
|
||||
|
||||
private String getCreateTableScript() throws IOException {
|
||||
|
||||
Enumeration<URL> resources = serverConfig.getClassLoadConfig().getResources("migration-support/create.sql");
|
||||
if (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
return IOUtils.readUtf8(url.openStream());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean tableExists(Connection connection) throws SQLException {
|
||||
|
||||
return databasePlatform.tableExists(connection, catalog, schema, table);
|
||||
}
|
||||
|
||||
|
||||
public boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationResource priorVersion) {
|
||||
|
||||
// if prior != null check previous version installed
|
||||
// if previous version not installed ... error - missing version (prior version)
|
||||
|
||||
// if (!priorVersionNotInstalled(priorVersion)) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// // if localVersion installed
|
||||
// // check checksum and return ok, or re-installable?
|
||||
// // else
|
||||
// // install version and continue
|
||||
//
|
||||
// if (runPosition >= metaRows.size()) {
|
||||
// logger.debug("No matching row");
|
||||
// runMigration(runPosition, localVersion);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkInstalled(LocalMigrationResource priorVersion) {
|
||||
if (priorVersion != null) {
|
||||
searchFor(priorVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private void searchFor(LocalMigrationResource priorVersion) {
|
||||
|
||||
}
|
||||
|
||||
public void commit() throws SQLException {
|
||||
connection.commit();
|
||||
}
|
||||
|
||||
private void runMigration(int runPosition, LocalMigrationResource localVersion) {
|
||||
|
||||
logger.debug("run migration "+localVersion.getLocation());
|
||||
|
||||
|
||||
String script = localVersion.getContent();
|
||||
|
||||
MigrationScriptRunner run = new MigrationScriptRunner(connection);
|
||||
run.runScript(false, script, "run migration version: "+localVersion.getVersion());
|
||||
|
||||
String sql = "insert into ebean_migration (id,status,run_version,dep_version,comment,checksum,run_on,run_by,run_ip) "+
|
||||
"values (?,?,?,?,?,?,?,?,?)";
|
||||
|
||||
SqlUpdate sqlUpdate = server.createSqlUpdate(sql);
|
||||
sqlUpdate.setParameter(1, runPosition+1);
|
||||
sqlUpdate.setParameter(2, "success");
|
||||
sqlUpdate.setParameter(3, localVersion.getVersion().getFull());
|
||||
sqlUpdate.setParameter(4, "na");
|
||||
sqlUpdate.setParameter(5, "comm");
|
||||
sqlUpdate.setParameter(6, 0);
|
||||
sqlUpdate.setParameter(7, new Timestamp(System.currentTimeMillis()));
|
||||
sqlUpdate.setParameter(8, envUserName);
|
||||
sqlUpdate.setParameter(9, "userIp");
|
||||
|
||||
ExternalJdbcTransaction t = new ExternalJdbcTransaction(connection);
|
||||
server.execute(sqlUpdate, t);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
/**
|
||||
* Created by rob on 1/04/16.
|
||||
*/
|
||||
public class PlaceholderBuilder {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.avaje.ebean.dbmigration.runner;
|
||||
|
||||
/**
|
||||
* Created by rob on 1/04/16.
|
||||
*/
|
||||
public class ScriptTransform {
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import com.avaje.ebean.config.EncryptKeyManager;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.dbmigration.DdlGenerator;
|
||||
import com.avaje.ebean.dbmigration.MigrationRunner;
|
||||
import com.avaje.ebean.event.BeanPersistController;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
@@ -339,6 +340,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (migrationConfig != null) {
|
||||
migrationConfig.generateOnStart(this);
|
||||
}
|
||||
|
||||
MigrationRunner migrationRunner = new MigrationRunner(this);
|
||||
migrationRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities for encoding and decoding strings.
|
||||
*/
|
||||
public final class EncodeUtil {
|
||||
|
||||
private EncodeUtil() {
|
||||
/* no instances */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* URL-encodes the specified UTF-8 string.
|
||||
*/
|
||||
public static String urlEncode(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return URLEncoder.encode(string, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* URL-decodes the specified string as a UTF-8 string.
|
||||
*/
|
||||
public static String urlDecode(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return URLDecoder.decode(string, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes corresponding to the specified ASCII string.
|
||||
*/
|
||||
public static byte[] asciiToBytes(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return string.getBytes("US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for US-ASCII is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ASCII string corresponding to the specified bytes.
|
||||
*/
|
||||
public static String bytesToAscii(byte[] data) {
|
||||
try {
|
||||
return new String(data, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for US-ASCII is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes corresponding to the specified UTF-8 string.
|
||||
*/
|
||||
public static byte[] utf8ToBytes(String string) {
|
||||
if (string == null) return null;
|
||||
try {
|
||||
return string.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UTF-8 string corresponding to the specified bytes.
|
||||
*/
|
||||
public static String bytesToUtf8(byte[] data) {
|
||||
try {
|
||||
return new String(data, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Support for UTF-8 is mandated by the Java spec", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UTF-8 string corresponding to the specified bytes.
|
||||
*/
|
||||
public static String decodeBytes(byte[] data, String encoding) {
|
||||
try {
|
||||
return new String(data, encoding);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("Error decoding bytes with "+encoding, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
/**
|
||||
* Created by rob on 5/02/16.
|
||||
*/
|
||||
public class IOUtils {
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as a byte array.
|
||||
*/
|
||||
public static byte[] read(InputStream in) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
pump(in, buffer);
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as an ASCII string.
|
||||
*/
|
||||
public static String readAscii(InputStream in) throws IOException {
|
||||
return EncodeUtil.bytesToAscii(read(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as UTF-8 string.
|
||||
*/
|
||||
public static String readUtf8(InputStream in) throws IOException {
|
||||
return EncodeUtil.bytesToUtf8(read(in));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the entire contents of the specified input stream and returns them
|
||||
* as a string using the encoding supplied.
|
||||
*/
|
||||
public static String readEncoded(InputStream in, String encoding) throws IOException {
|
||||
return EncodeUtil.decodeBytes(read(in), encoding);
|
||||
}
|
||||
|
||||
public static String read(Reader reader) throws IOException {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
char[] buffer = new char[4096];
|
||||
for (; ; ) {
|
||||
int len = reader.read(buffer);
|
||||
if (len < 0) {
|
||||
break;
|
||||
}
|
||||
sb.append(buffer, 0, len);
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the specified input stream and copies it to the specified
|
||||
* output stream, until the input stream is at EOF. Both streams are then
|
||||
* closed.
|
||||
*
|
||||
* @throws IOException if the input or output stream is <code>null</code>
|
||||
*/
|
||||
public static void pump(InputStream in, OutputStream out) throws IOException {
|
||||
|
||||
if (in == null) throw new IOException("Input stream is null");
|
||||
if (out == null) throw new IOException("Output stream is null");
|
||||
|
||||
try {
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
for (; ; ) {
|
||||
int bytes = in.read(buffer);
|
||||
if (bytes < 0) {
|
||||
break;
|
||||
}
|
||||
out.write(buffer, 0, bytes);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.avaje.ebeaninternal.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Utility for closing raw Jdbc resources.
|
||||
*/
|
||||
public class JdbcClose {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(JdbcClose.class);
|
||||
|
||||
/**
|
||||
* Close the connection logging if an error occurs.
|
||||
*/
|
||||
public static void close(Connection connection) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Error closing connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user