Java tutorial
/* * Copyright 2009 The Corner Team. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package corner.migration.services.impl; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.tapestry5.hibernate.HibernateSessionSource; import org.hibernate.Criteria; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.cfg.Settings; import org.hibernate.connection.ConnectionProvider; import org.hibernate.dialect.Dialect; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.mapping.Table; import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.tool.hbm2ddl.TableMetadata; import org.hibernate.util.ArrayHelper; import org.slf4j.Logger; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateTemplate; import corner.migration.services.ConnectionAdapter; import corner.migration.services.ConnectionAdapterSource; import corner.migration.services.MigrateFragment; import corner.migration.services.MigrationService; import corner.migration.services.impl.fragment.AddColumnFragment; import corner.migration.services.impl.fragment.ChangeColumnFragment; import corner.migration.services.impl.fragment.CreateTableFragment; import corner.migration.services.impl.fragment.RemoveColumnFragment; import corner.migration.services.impl.fragment.RenameColumnFragment; /** * ??? * * @author <a href="jun.tsai@ganshane.net">Jun Tsai</a> * @version $Revision$ * @since 0.0.2 */ public class MigrationServiceImpl implements MigrationService { /** ?? * */ public static final int DB_NOTHING = -1; /** ? * */ private static final int INDEX_NOTHING = -1; public static final String defaultCatalog = Environment.getProperties() .getProperty(Environment.DEFAULT_CATALOG); public static final String defaultSchema = Environment.getProperties().getProperty(Environment.DEFAULT_SCHEMA); public final SessionFactoryImplementor sessionFactory; private Settings settings; public Dialect dialect; private Logger logger; public ConnectionAdapter adapter; private Configuration cfg; public static final String SCHEMA_INFO_TABLE_NAME = "db_schema_info"; public MigrationServiceImpl(HibernateSessionSource sessionSource, ConnectionAdapterSource adapterSource, Logger logger) { this.sessionFactory = (SessionFactoryImplementor) sessionSource.getSessionFactory(); this.settings = this.sessionFactory.getSettings(); this.cfg = sessionSource.getConfiguration(); this.dialect = this.settings.getDialect(); this.logger = logger; adapter = adapterSource.getConnectionAdapter(dialect.getClass()); if (adapter == null) { throw new RuntimeException( "?Dialect[" + dialect.getClass().getName() + "] ????!"); } } /** * ??? * * @see corner.migration.services.MigrationService#createTable(java.lang.String) */ @Override public void createTable(final String tableName) { this.executeMigrateFragment(new CreateTableFragment(this.adapter, dialect, sessionFactory, tableName)); } /** * @see corner.migration.services.MigrationService#addColumn(java.lang.String) */ @Override public void addColumn(final String tableName) { executeMigrateFragment(new AddColumnFragment(this.adapter, dialect, sessionFactory, tableName)); } /** * Column * * @param tableName * ?? * @param columnName * ?? * @see corner.migration.services.MigrationService#changeColumn(java.lang.String, java.lang.String) */ @Override public void changeColumn(final String tableName, final String columnName) { executeMigrateFragment( new ChangeColumnFragment(this.adapter, dialect, sessionFactory, tableName, columnName)); } /** * SQL * * @param tableName * ?? * @param tableColumns * ?? * @since 0.0.2 * @see corner.migration.services.MigrationService#removeColumn(java.lang.String, java.lang.String[]) */ @Override public void removeColumn(String tableName, String... tableColumns) { executeMigrateFragment( new RemoveColumnFragment(this.adapter, dialect, sessionFactory, tableName, tableColumns)); } /** * ??? * * @param tableName * ?? * @param oldColumns * ?? * @param newColumns * ?? * @since 0.0.2 * @see corner.migration.services.MigrationService#renameColumns(java.lang.String, java.lang.String[], java.lang.String[]) */ @Override public void renameColumns(String tableName, String[] oldColumns, String[] newColumns) { executeMigrateFragment( new RenameColumnFragment(this.adapter, dialect, sessionFactory, tableName, oldColumns, newColumns)); } /** * * * @param tableName * ?? * @since 0.0.2 * @see corner.migration.services.MigrationService#dropTable(java.lang.String) */ @Override public void dropTable(String tableName) { Table table = new Table(tableName); this.executeSchemaScript(table.sqlDropString(this.dialect, defaultCatalog, defaultSchema)); } public void executeSchemaScript(String... sql) { Connection conn = null; try { conn = this.settings.getConnectionProvider().getConnection(); this.executeSchemaScript(conn, sql); } catch (SQLException e) { throw new RuntimeException(e); } finally { this.closeHibernateConnection(conn); } } /** * ? * * @param fragment * ? * @since 0.0.2 */ public void executeMigrateFragment(MigrateFragment fragment) { // ?? Iterator tableMappings = this.cfg.getTableMappings(); while (tableMappings.hasNext()) { // Table table = (Table) tableMappings.next(); // ?? if (!fragment.filteTable(table)) { continue; } Connection conn = null; try { conn = this.settings.getConnectionProvider().getConnection(); DatabaseMetadata meta = new DatabaseMetadata(conn, dialect); TableMetadata tableMetadata = adapter.fetchTableInfo(meta, table, defaultCatalog, defaultSchema); // ?Table??. List<String> script = fragment.generateMigrationFragments(table, tableMetadata); final String[] sqls = ArrayHelper.toStringArray(script); // ?SQL executeSchemaScript(conn, sqls); } catch (SQLException e) { throw new RuntimeException(e); } finally { closeHibernateConnection(conn); } } } /** * {@link Connection} * <p> * {@link Connection}?{@link ConnectionProvider},{@link ConnectionProvider}closeConnection * * @param conn */ private void closeHibernateConnection(Connection conn) { if (conn != null) { try { this.settings.getConnectionProvider().closeConnection(conn); } catch (SQLException e) { logger.debug("when close connection get the Exception:" + e); } } } /** * Execute the given schema script on the given JDBC Connection. * <p> * Note that the default implementation will log unsuccessful statements and * continue to execute. Override the <code>executeSchemaStatement</code> * method to treat failures differently. * * @param con * the JDBC Connection to execute the script on * @param sql * the SQL statements to execute * @throws SQLException * if thrown by JDBC methods * @see #executeSchemaStatement * @param con * the JDBC Connection to execute the script on * @param sql * the SQL statements to execute * @throws SQLException * if thrown by JDBC methods */ protected void executeSchemaScript(Connection con, String... sql) throws SQLException { if (sql != null && sql.length > 0) { boolean oldAutoCommit = con.getAutoCommit(); if (!oldAutoCommit) { con.setAutoCommit(false); } try { Statement stmt = con.createStatement(); try { for (int i = 0; i < sql.length; i++) { try { logger.info("[db-upgrade] " + sql[i]); executeSchemaStatement(stmt, sql[i]); } catch (SQLException se) { logger.error("[db-upgrade]" + se.toString(), se); throw se; } } } finally { JdbcUtils.closeStatement(stmt); } } finally { if (!oldAutoCommit) { con.setAutoCommit(false); } } } } /** * Execute the given schema SQL on the given JDBC Statement. * <p> * Note that the default implementation will log unsuccessful statements and * continue to execute. Override this method to treat failures differently. * * @param stmt * the JDBC Statement to execute the SQL on * @param sql * the SQL statement to execute * @throws SQLException * if thrown by JDBC methods (and considered fatal) */ protected void executeSchemaStatement(Statement stmt, String sql) throws SQLException { stmt.executeUpdate(sql); } @Override public SchemaInfo initSchemaInfo() { this.executeMigrateFragment(new MigrateFragment() { @Override public boolean filteTable(Table table) { return table.getName().equalsIgnoreCase(SCHEMA_INFO_TABLE_NAME); } @Override public List<String> generateMigrationFragments(Table table, TableMetadata tableInfo) { List<String> script = new ArrayList<String>(); // ?schema_info?? if (tableInfo == null) { logger.info("?[" + SCHEMA_INFO_TABLE_NAME + "],?!"); String[] sqls = cfg.generateSchemaCreationScript(dialect); executeSchemaScript(sqls); } return script; } }); // ?? HibernateTemplate hibernateTemplate = new HibernateTemplate(this.sessionFactory); SchemaInfo info = (SchemaInfo) hibernateTemplate.execute(new HibernateCallback() { /** * @see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session) */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { Criteria criteria = session.createCriteria(SchemaInfo.class); return criteria.uniqueResult(); } }); if (info == null) {// SchemaInfo // ?,version?0 info = new SchemaInfo(); info.setDbversion(DB_NOTHING); info.setIndexversion(INDEX_NOTHING); hibernateTemplate.saveOrUpdate(info); } return info; } /** * @see corner.migration.services.MigrationService#updateDbMaxVersion(int, int) */ @Override public void updateDbMaxVersion(int scriptType, int maxVersion) { HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory); SchemaInfo info = (SchemaInfo) hibernateTemplate.execute(new HibernateCallback() { /** * @see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session) */ @Override public Object doInHibernate(Session session) throws HibernateException, SQLException { Criteria criteria = session.createCriteria(SchemaInfo.class); return criteria.uniqueResult(); } }); if (info != null) { if (scriptType == AbstractDBMigrationInitializer.DB_SCRIPT_TYPE_STR) { info.setDbversion(maxVersion); } else if (scriptType == AbstractDBMigrationInitializer.INDEX_SCRIPT_TYPE_STR) { info.setIndexversion(maxVersion); } hibernateTemplate.saveOrUpdate(info); } } @Override public void executeSql(String sql) { this.executeSchemaScript(sql); } }