How to migrate databases to a new schema automatically

From Coder's Log

Jump to: navigation, search

This is a class that I put together to perform migrations of hibernate maintained schema. This is a bit much to explain in the state that it is in. but in a nutshell. For every table that is not new rename the table to temp_{tableName} create the new table schema and copy all common fields from temp_{tableName} to {tableName}. Indexes will be recreated as well.

  • propFile - defines a property file for hibernate and other variables
public class DDLConversion {
	public static void main(String[] args) throws Exception {
		// Export schema to sql.ddl
		GenericApplicationContext context = new GenericApplicationContext();
		context.setResourceLoader(new FileSystemResourceLoader());
		XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(context);

		xmlReader.loadBeanDefinitions(new FileSystemResource("WEB-INF/applicationContext.xml"));
		context.refresh();
		DataSource ds = (DataSource) context.getBean("dataSource");
		// AnnotationSessionFactoryBean sf=(AnnotationSessionFactoryBean)
		// context.getBean("sessionFactory");
		Properties props=(Properties)context.getBean("propFile");
		String dialect = props.getProperty("hibernate.dialect");
		HibernateSchemaExporter.export(context,true, dialect);
		Connection connection = ds.getConnection();
		connection.setAutoCommit(true);
		if (args.length > 0 && args[0].toUpperCase().equals("REVERT")) {
			revertStructure(connection);
			System.out.println("REVERTED");
			return;
		}
		List<String> tables = getTableNames(connection);
		for (String tableName : tables) {
			dropConstraints(connection, tableName);
			executeSql(connection, "ALTER TABLE " + tableName + " RENAME TO TEMP_" + tableName);
		}
		try {
			BufferedReader br = new BufferedReader(new FileReader("sql.ddl"));
			String sqlLine = br.readLine();
			while (sqlLine != null) {
				executeSql(connection, sqlLine);
				sqlLine = br.readLine();
			}
			try {
				executeSql(connection, "set referential_integrity FALSE");
			} catch (SQLException se) {
			}// this will file for some dialects
			for (String tableName : tables) {
				System.out.println("COPYING DATA :" + tableName);
				List<String> oldCols = getColumnNames(connection, "TEMP_" + tableName);
				List<String> newCols = getColumnNames(connection, tableName);
				String cols = "";
				for (String col : oldCols)
					if (newCols.contains(col))
						cols += "," + col;
				if (cols.length() > 0) {
					cols = cols.substring(1);
					executeSql(connection, "INSERT INTO " + tableName + "(" + cols + ") SELECT " + cols + " FROM TEMP_" + tableName);
				} else {
					System.out.println("***** WARNING NO COMMON COLUMNS IN TABLE :" + tableName);
				}
			}
			try {
				executeSql(connection, "set referential_integrity TRUE");
			} catch (SQLException se) {
			}// this will file for some dialects
			for (String tableName : tables) {
				executeSql(connection, "DROP TABLE TEMP_" + tableName);
			}
			System.out.println("DONE");
		} catch (Exception e) {
			e.printStackTrace();
			revertStructure(connection);
			System.out.println("FAILED-REVERTED");
		}
		connection.close();
	}
	public static void revertStructure(Connection connection) throws SQLException {
		List<String> tables = getTableNames(connection);
		for (String tableName : tables) {
			dropConstraints(connection, tableName);
		}
		for (String tableName : tables) {
			if (tableName.toUpperCase().startsWith("TEMP_")) {
				String newTable = tableName.substring(5);
				if (tables.contains(newTable))
					executeSql(connection, "DROP TABLE " + newTable);
				executeSql(connection, "ALTER TABLE " + tableName + " RENAME TO " + newTable);
			}
		}
	}
	public static void executeSql(Connection connection, String sql) throws SQLException {
		System.out.println(sql);
		connection.prepareStatement(sql).execute();
	}
	public static void dropConstraints(Connection connection, String tableName) throws SQLException {
		ResultSet rsConstraints = connection.getMetaData().getImportedKeys(null, null, tableName);
		while (rsConstraints.next()) {
			String fkName = rsConstraints.getString("FK_NAME");
			String sql = "ALTER TABLE " + tableName + " DROP CONSTRAINT " + fkName;
			executeSql(connection, sql);
		}
	}
	public static List<String> getTableNames(Connection connection) throws SQLException {
		ResultSet rsTables = connection.getMetaData().getTables(null, null, null, new String[] { "TABLE" });
		ArrayList<String> tables = new ArrayList();
		while (rsTables.next()) {
			String tableName = rsTables.getString("TABLE_NAME");
			tables.add(tableName);
		}
		return tables;
	}
	public static List<String> getColumnNames(Connection connection, String tableName) throws SQLException {
		ResultSet rsColumns = connection.getMetaData().getColumns(null, null, tableName, null);
		ArrayList<String> columns = new ArrayList();
		while (rsColumns.next()) {
			String columnName = rsColumns.getString("COLUMN_NAME");
			columns.add(columnName);
		}
		return columns;
	}
	public static void dumpRow(ResultSet rs) throws SQLException {
		System.out.println("=====");
		for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
			System.out.println(rs.getMetaData().getColumnName(i) + " => " + rs.getString(i));
		}
		System.out.println("=====");
	}
}
Personal tools