Changeset 7455


Ignore:
Timestamp:
Mar 6, 2018, 10:12:30 AM (4 years ago)
Author:
Nicklas Nordborg
Message:

References #2048: Upgrade to Hibernate 5.2

I hope this fixes the schema update problem. The SchemaMigrator has been replaced with code that goes directly into the internals of Hibernate and tries to check for an existing table using both quoted and unquoted table names. The generated SQL is then modified back to always use quoted table names. A good thing is that the "hack" mentioned in #2090 is no longer needed (due to the search-and-replace operation).

Location:
branches/3.12-stable/src/core/net/sf/basedb/core
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/3.12-stable/src/core/net/sf/basedb/core/ServiceSessionControl.java

    r7396 r7455  
    346346       
    347347        // Check if the schema exists or not
    348         boolean schemaExists = HibernateUtil.doReturningWork(session, new SchemaExistsWork(catalog, schema));
     348        boolean schemaExists = HibernateUtil.doReturningWork(session, new SchemaExistsWork(actualCatalog, actualSchema));
    349349        installMode = schemaExists ? SchemaGenerator.Mode.UPDATE : SchemaGenerator.Mode.INSTALL;
    350350        if (log.isDebugEnabled()) log.debug("Mode: " +installMode);
  • branches/3.12-stable/src/core/net/sf/basedb/core/hibernate/SchemaGenerator.java

    r7397 r7455  
    2222package net.sf.basedb.core.hibernate;
    2323
    24 import java.lang.reflect.Field;
    2524import java.sql.Connection;
     25import java.sql.DatabaseMetaData;
    2626import java.sql.SQLException;
    2727import java.sql.Statement;
     
    3030import java.util.EnumSet;
    3131import java.util.HashMap;
     32import java.util.Iterator;
    3233import java.util.List;
    3334import java.util.Map;
    3435
     36import net.sf.basedb.core.DatabaseException;
    3537import net.sf.basedb.core.ProgressReporter;
    3638import net.sf.basedb.core.StringUtil;
    3739import net.sf.basedb.core.dbengine.DbEngine;
     40import net.sf.basedb.core.dbengine.TableInfo;
    3841import net.sf.basedb.util.EqualsHelper;
    3942
    4043import org.hibernate.boot.Metadata;
    4144import org.hibernate.boot.model.naming.Identifier;
    42 import org.hibernate.boot.model.relational.QualifiedName;
    43 import org.hibernate.boot.model.relational.QualifiedSequenceName;
    4445import org.hibernate.boot.model.relational.QualifiedTableName;
    4546import org.hibernate.boot.spi.MetadataImplementor;
    4647import org.hibernate.dialect.Dialect;
    47 import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl;
     48import org.hibernate.engine.jdbc.env.internal.QualifiedObjectNameFormatterStandardImpl;
    4849import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
    4950import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter;
    5051import org.hibernate.jdbc.Work;
     52import org.hibernate.mapping.Table;
    5153import org.hibernate.service.ServiceRegistry;
    5254import org.hibernate.service.spi.ServiceRegistryImplementor;
    5355import org.hibernate.tool.schema.SourceType;
    5456import org.hibernate.tool.schema.TargetType;
     57import org.hibernate.tool.schema.extract.spi.DatabaseInformation;
     58import org.hibernate.tool.schema.extract.spi.TableInformation;
    5559import org.hibernate.tool.schema.internal.SchemaCreatorImpl;
     60import org.hibernate.tool.schema.internal.exec.JdbcContext;
    5661import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl;
     62import org.hibernate.tool.schema.internal.Helper;
    5763import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
    58 import org.hibernate.tool.schema.internal.IndividuallySchemaMigratorImpl;
    5964import org.hibernate.tool.schema.spi.ExceptionHandler;
    6065import org.hibernate.tool.schema.spi.ExecutionOptions;
    6166import org.hibernate.tool.schema.spi.SchemaCreator;
    62 import org.hibernate.tool.schema.spi.SchemaMigrator;
    6367import org.hibernate.tool.schema.spi.ScriptSourceInput;
    6468import org.hibernate.tool.schema.spi.ScriptTargetOutput;
     
    117121    -----------------------
    118122  */
     123  @SuppressWarnings("unchecked")
    119124  @Override
    120125  public void execute(Connection connection)
     
    131136    String currentSql = null;
    132137    boolean autoCommit = connection.getAutoCommit();
    133     ForceQuotedTableNameFormatter tableNameFormatter = new ForceQuotedTableNameFormatter(metadata);
     138
    134139    try
    135140    {
     
    144149      {
    145150        log.info("Generating schema update script");
    146         tableNameFormatter.applyWorkaroundForHibernateBug();
    147         SchemaMigrator schemaMigrator = new IndividuallySchemaMigratorImpl(tool, null);
    148         schemaMigrator.doMigration(metadata, schemaUpdates, schemaUpdates);
     151       
     152        DatabaseMetaData jdbcMetaData = connection.getMetaData();
     153        JdbcContext jdbcContext = tool.resolveJdbcContext(schemaUpdates.getConfigurationValues());
     154        JdbcEnvironment jdbcEnvironment = metadata.getDatabase().getJdbcEnvironment();
     155       
     156        QualifiedObjectNameFormatter tableNameFormatter = new QualifiedObjectNameFormatterStandardImpl(jdbcEnvironment.getNameQualifierSupport());
     157       
     158        DatabaseInformation hibernateDbInfo = Helper.buildDatabaseInformation(serviceRegistry,
     159            tool.getDdlTransactionIsolator(jdbcContext),
     160            metadata.getDatabase().getDefaultNamespace().getName());
     161       
     162        Iterator<Table> tables = metadata.collectTableMappings().iterator();
     163        while (tables.hasNext())
     164        {
     165          Table table = tables.next();
     166          TableInfo jdbcTableInfo = new TableInfo(table, jdbcMetaData);
     167         
     168          log.debug("Table: " + table + (jdbcTableInfo.existsInDb() ? " EXISTS" : "NOT FOUND"));
     169         
     170          if (jdbcTableInfo.existsInDb())
     171          {
     172            // This table exists in the database, we need to force Hibernate to check and ALTER it if needed
     173            QualifiedTableName qft = table.getQualifiedTableName();
     174           
     175            // First try: let Hibernate use the default settings to find the table
     176            TableInformation hibernateTableInfo = hibernateDbInfo.getTableInformation(qft);
     177           
     178            if (log.isDebugEnabled()) log.debug(qft + ": " + hibernateTableInfo);
     179           
     180            if (hibernateTableInfo == null)
     181            {
     182              // If that fails, we need to try again with unquoted table name
     183              qft = new QualifiedTableName(qft.getCatalogName(), qft.getSchemaName(), Identifier.toIdentifier(table.getName(), false));
     184              hibernateTableInfo = hibernateDbInfo.getTableInformation(qft);
     185              if (log.isDebugEnabled()) log.debug(qft + ": " + hibernateTableInfo);
     186             
     187              // If this is null here we are in trouble!!!
     188              if (hibernateTableInfo == null)
     189              {
     190                throw new DatabaseException("Table '" + table.getName() + "' exists, but Hibernate can't find any information about it. "+
     191                    "Please report as a bug to the BASE developers");
     192              }
     193            }
     194           
     195            // To get the quoting, upper- and lower-case correct we need to re-create a new instance
     196            // and do a search-and-replace in the SQL generated by Hibernate
     197            qft = new QualifiedTableName(hibernateTableInfo.getName().getCatalogName(), hibernateTableInfo.getName().getSchemaName(), table.getNameIdentifier());
     198            String correctTableName = tableNameFormatter.format(qft, dialect);
     199            String tableNameGeneratedByHibernate = tableNameFormatter.format(hibernateTableInfo.getName(), dialect);
     200            if (log.isDebugEnabled())
     201            {
     202              log.debug("Search-and-replace: " + tableNameGeneratedByHibernate + "-->" + correctTableName);
     203            }
     204            Iterator<String> it = (Iterator<String>)table.sqlAlterStrings(dialect, metadata, hibernateTableInfo, null, null);
     205            while (it.hasNext())
     206            {
     207              String sql = it.next();
     208              String modifiedSql = sql.replace(tableNameGeneratedByHibernate, correctTableName);
     209              if (!sql.equals(modifiedSql))
     210              {
     211                log.debug("Hibernate SQL: " + sql);
     212                log.debug("Modified  SQL: " + modifiedSql);
     213              }
     214              schemaUpdates.accept(modifiedSql);
     215            }
     216          }
     217          else
     218          {
     219            String[] sql = dialect.getTableExporter().getSqlCreateStrings(table, metadata);
     220            for (String s : sql)
     221            {
     222              schemaUpdates.accept(s);
     223            }
     224          }
     225        }
    149226      }
    150227      else if (mode == Mode.INSTALL)
     
    223300    {
    224301      if (autoCommit != connection.getAutoCommit()) connection.setAutoCommit(autoCommit);
    225       tableNameFormatter.resetWorkaroundForHibernateBug();
    226302      if (stmt != null)
    227303      {
     
    416492  }
    417493 
    418 
    419   /**
    420     Replace the 'QualifiedObjectNameFormatter' with an implementation that forces
    421     all table names to be flagged as quoted. This is a hack to work around a bug
    422     in Hibernate which generates "alter table table-name add column ..."
    423     statements without quotes around the table-name which causes an error
    424    
    425     TODO - When (if?) this issue is fixed in Hibernate we can remove this workaround
    426   */
    427   static class ForceQuotedTableNameFormatter
    428     implements QualifiedObjectNameFormatter
    429   {
    430 
    431     private final Metadata metadata;
    432     private final JdbcEnvironment jdbc;
    433     private QualifiedObjectNameFormatter originalFormatter;
    434    
    435     ForceQuotedTableNameFormatter(Metadata metadata)
    436     {
    437       this.metadata = metadata;
    438       this.jdbc = metadata.getDatabase().getJdbcEnvironment();
    439     }
    440    
    441     @Override
    442     public String format(QualifiedTableName tableName, Dialect dialect)
    443     {
    444       QualifiedTableName quotedTableName = new QualifiedTableName(
    445         tableName.getCatalogName(),
    446         tableName.getSchemaName(),
    447         Identifier.toIdentifier(tableName.getTableName().getText(), true)
    448       );
    449       return originalFormatter.format(quotedTableName, dialect);
    450     }
    451 
    452     @Override
    453     public String format(QualifiedSequenceName sequenceName, Dialect dialect)
    454     {
    455       return originalFormatter.format(sequenceName, dialect);
    456     }
    457 
    458     @Override
    459     public String format(QualifiedName name, Dialect dialect)
    460     {
    461       return originalFormatter.format(name, dialect);
    462     }
    463    
    464     void applyWorkaroundForHibernateBug()
    465     {
    466       this.originalFormatter = jdbc.getQualifiedObjectNameFormatter();
    467       setFormatter(this);
    468     }
    469    
    470     void resetWorkaroundForHibernateBug()
    471     {
    472       if (originalFormatter != null) setFormatter(originalFormatter);
    473     }
    474    
    475     private void setFormatter(QualifiedObjectNameFormatter formatter)
    476     {
    477       try
    478       {
    479         Field f = JdbcEnvironmentImpl.class.getDeclaredField("qualifiedObjectNameFormatter");
    480         f.setAccessible(true);
    481         f.set(jdbc, formatter);
    482       }
    483       catch (Exception ex)
    484       {
    485         throw new RuntimeException(ex);
    486       }
    487     }
    488    
    489   }
    490494 
    491495}
Note: See TracChangeset for help on using the changeset viewer.