近期项目中需要支持国产数据库,需要liquibase底层支持达梦数据库,记录下操作的详细步骤以便分享。
1.liquibase支持的数据库很多如MySQL, PostgreSQL, Oracle, Sql Server, DB2。但是达梦数据库并不支持,所以需要新建DMDatabase类来适配达梦数据库。达梦语法和Oracle很像所以只需要复制liquibase-core下的Oracle Database做些修改就可了。以下为需要修改的地方
public class DMDatabase extends AbstractJdbcDatabase {
public static final String PRODUCT_NAME = "DM DBMS";
@Override
public String getShortName() {
//noinspection HardCodedStringLiteral
return "dm";
}
@Override
protected String getDefaultDatabaseProductName() {
//noinspection HardCodedStringLiteral
return PRODUCT_NAME;
}
@Override
public Integer getDefaultPort() {
return 5236;
}
@Override
public String getDefaultDriver(String url) {
//noinspection HardCodedStringLiteral
if (url.startsWith("jdbc:dm")) {
return "dm.jdbc.driver.DmDriver";
}
return null;
}
}
2.达梦不支持booelan类型需要转为int,所以需要修改Boolean类,修改地方为://支持达梦自增列
@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT)
public class BooleanType extends LiquibaseDataType {
@Override
public DatabaseDataType toDatabaseDataType(Database database) {
String originalDefinition = StringUtils.trimToEmpty(getRawDefinition());
if ((database instanceof AbstractDb2Database) || (database instanceof FirebirdDatabase)) {
return new DatabaseDataType("SMALLINT");
} else if (database instanceof MSSQLDatabase) {
return new DatabaseDataType(database.escapeDataTypeName("bit"));
} else if (database instanceof MySQLDatabase) {
if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
return new DatabaseDataType("BIT", getParameters());
}
return new DatabaseDataType("BIT", 1);
} else if (database instanceof OracleDatabase) {
return new DatabaseDataType("NUMBER", 1);
} else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) {
return new DatabaseDataType("BIT");
} else if (database instanceof DerbyDatabase) {
if (((DerbyDatabase) database).supportsBooleanDataType()) {
return new DatabaseDataType("BOOLEAN");
} else {
return new DatabaseDataType("SMALLINT");
}
} else if (database instanceof HsqlDatabase) {
return new DatabaseDataType("BOOLEAN");
} else if (database instanceof PostgresDatabase) {
if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
return new DatabaseDataType("BIT", getParameters());
}
}//转int
else if (database instanceof DMDatabase){
return new DatabaseDataType("INT");
}
return super.toDatabaseDataType(database);
}
protected boolean isNumericBoolean(Database database) {
if (database instanceof DerbyDatabase) {
return !((DerbyDatabase) database).supportsBooleanDataType();
}
return (database instanceof AbstractDb2Database) || (database instanceof FirebirdDatabase) || (database instanceof
MSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof OracleDatabase) ||
(database instanceof SQLiteDatabase) || (database instanceof SybaseASADatabase) || (database instanceof
SybaseDatabase) || (database instanceof DMDatabase);
}
}
- 达梦的自增列语法与其他不同需要修改CreateTableGenerator类
@Override
public Sql[] generateSql(CreateTableStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
List<Sql> additionalSql = new ArrayList<>();
StringBuffer buffer = new StringBuffer();
buffer.append("CREATE TABLE ").append(database.escapeTableName(statement.getCatalogName(),
statement.getSchemaName(), statement.getTableName())).append(" ");
buffer.append("(");
boolean isSinglePrimaryKeyColumn = (statement.getPrimaryKeyConstraint() != null) && (statement
.getPrimaryKeyConstraint().getColumns().size() == 1);
boolean isPrimaryKeyAutoIncrement = false;
Iterator<String> columnIterator = statement.getColumns().iterator();
BigInteger mysqlTableOptionStartWith = null;
/* We have reached the point after "CREATE TABLE ... (" and will now iterate through the column list. */
while (columnIterator.hasNext()) {
String column = columnIterator.next();
DatabaseDataType columnType = statement.getColumnTypes().get(column).toDatabaseDataType(database);
buffer.append(database.escapeColumnName(statement.getCatalogName(), statement.getSchemaName(), statement.getTableName(), column, true));
buffer.append(" ").append(columnType);
AutoIncrementConstraint autoIncrementConstraint = null;
for (AutoIncrementConstraint currentAutoIncrementConstraint : statement.getAutoIncrementConstraints()) {
if (column.equals(currentAutoIncrementConstraint.getColumnName())) {
autoIncrementConstraint = currentAutoIncrementConstraint;
break;
}
}
boolean isAutoIncrementColumn = autoIncrementConstraint != null;
boolean isPrimaryKeyColumn = (statement.getPrimaryKeyConstraint() != null) && statement
.getPrimaryKeyConstraint().getColumns().contains(column);
isPrimaryKeyAutoIncrement = isPrimaryKeyAutoIncrement || (isPrimaryKeyColumn && isAutoIncrementColumn);
if ((database instanceof SQLiteDatabase) &&
isSinglePrimaryKeyColumn &&
isPrimaryKeyColumn &&
isAutoIncrementColumn) {
String pkName = StringUtils.trimToNull(statement.getPrimaryKeyConstraint().getConstraintName());
if (pkName == null) {
pkName = database.generatePrimaryKeyName(statement.getTableName());
}
if (pkName != null) {
buffer.append(" CONSTRAINT ");
buffer.append(database.escapeConstraintName(pkName));
}
buffer.append(" PRIMARY KEY");
}
// for the serial data type in postgres, there should be no default value
if (!columnType.isAutoIncrement() && (statement.getDefaultValue(column) != null)) {
Object defaultValue = statement.getDefaultValue(column);
if (database instanceof MSSQLDatabase) {
String constraintName = statement.getDefaultValueConstraintName(column);
if (constraintName == null) {
constraintName = ((MSSQLDatabase) database).generateDefaultConstraintName(statement.getTableName(), column);
}
buffer.append(" CONSTRAINT ").append(database.escapeObjectName(constraintName, ForeignKey.class));
}
if ((database instanceof OracleDatabase) && statement.getDefaultValue(column).toString().startsWith
("GENERATED ALWAYS ")) {
buffer.append(" ");
} else if (database instanceof Db2zDatabase && statement.getDefaultValue(column).toString().contains("CURRENT TIMESTAMP")
|| statement.getDefaultValue(column).toString().contains("IDENTITY GENERATED BY DEFAULT")) {
buffer.append(" ");
} else {
buffer.append(" DEFAULT ");
}
if (defaultValue instanceof DatabaseFunction) {
buffer.append(database.generateDatabaseFunctionValue((DatabaseFunction) defaultValue));
} else if (database instanceof Db2zDatabase) {
if (statement.getDefaultValue(column).toString().contains("CURRENT TIMESTAMP")) {
buffer.append("");
}
if (statement.getDefaultValue(column).toString().contains("IDENTITY GENERATED BY DEFAULT")) {
buffer.append("GENERATED BY DEFAULT AS IDENTITY");
}
if (statement.getDefaultValue(column).toString().contains("CURRENT USER")) {
buffer.append("SESSION_USER ");
}
if (statement.getDefaultValue(column).toString().contains("CURRENT SQLID")) {
buffer.append("CURRENT SQLID ");
}
} else {
buffer.append(statement.getColumnTypes().get(column).objectToSql(defaultValue, database));
}
}
if (isAutoIncrementColumn) {
// TODO: check if database supports auto increment on non primary key column
if (database.supportsAutoIncrement()) {
String autoIncrementClause = database.getAutoIncrementClause(autoIncrementConstraint.getStartWith(), autoIncrementConstraint.getIncrementBy());
if (!"".equals(autoIncrementClause)) {
buffer.append(" ").append(autoIncrementClause);
}
if( autoIncrementConstraint.getStartWith() != null ){
if (database instanceof PostgresDatabase) {
String sequenceName = statement.getTableName()+"_"+column+"_seq";
additionalSql.add(new UnparsedSql("alter sequence "+database.escapeSequenceName(statement.getCatalogName(), statement.getSchemaName(), sequenceName)+" start with "+autoIncrementConstraint.getStartWith(), new Sequence().setName(sequenceName).setSchema(statement.getCatalogName(), statement.getSchemaName())));
}else if(database instanceof MySQLDatabase){
mysqlTableOptionStartWith = autoIncrementConstraint.getStartWith();
}
}
}
//支持达梦自增列
else if(isAutoIncrementColumn && database instanceof DMDatabase){
buffer.append(" identity(1,1)");
}
else {
LogService.getLog(getClass()).warning(LogType.LOG, database.getShortName()+" does not support autoincrement columns as requested for "+(database.escapeTableName(statement.getCatalogName(), statement.getSchemaName(), statement.getTableName())));
}
}
// Do we have a NOT NULL constraint for this column?
if (statement.getNotNullColumns().get(column) != null) {
if (! database.supportsNotNullConstraintNames()) {
buffer.append(" NOT NULL");
} else {
/* Determine if the NOT NULL constraint has a name. */
NotNullConstraint nnConstraintForThisColumn = statement.getNotNullColumns().get(column);
String nncName = StringUtils.trimToNull(nnConstraintForThisColumn.getName());
if (nncName == null) {
buffer.append(" NOT NULL");
} else {
buffer.append(" CONSTRAINT ");
buffer.append(database.escapeConstraintName(nncName));
buffer.append(" NOT NULL");
} // do we have a NN constraint name?
} // does the DB support constraint names?
} else {
if ((database instanceof SybaseDatabase) || (database instanceof SybaseASADatabase) || (database
instanceof MySQLDatabase) || ((database instanceof MSSQLDatabase) && columnType.toString()
.toLowerCase().contains("timestamp"))) {
buffer.append(" NULL");
} // Do we need to specify NULL explicitly?
} // Do we have a NOT NULL constraint for this column?
if ((database instanceof MySQLDatabase) && (statement.getColumnRemarks(column) != null)) {
buffer.append(" COMMENT '" + database.escapeStringForDatabase(statement.getColumnRemarks(column)) + "'");
}
if (columnIterator.hasNext()) {
buffer.append(", ");
}
}
buffer.append(",");
if (!( (database instanceof SQLiteDatabase) &&
isSinglePrimaryKeyColumn &&
isPrimaryKeyAutoIncrement) ) {
if ((statement.getPrimaryKeyConstraint() != null) && !statement.getPrimaryKeyConstraint().getColumns()
.isEmpty()) {
if (database.supportsPrimaryKeyNames()) {
String pkName = StringUtils.trimToNull(statement.getPrimaryKeyConstraint().getConstraintName());
if (pkName == null) {
// TODO ORA-00972: identifier is too long
// If tableName lenght is more then 28 symbols
// then generated pkName will be incorrect
pkName = database.generatePrimaryKeyName(statement.getTableName());
}
if (pkName != null) {
buffer.append(" CONSTRAINT ");
buffer.append(database.escapeConstraintName(pkName));
}
}
buffer.append(" PRIMARY KEY (");
buffer.append(database.escapeColumnNameList(StringUtils.join(statement.getPrimaryKeyConstraint().getColumns(), ", ")));
buffer.append(")");
// Setting up table space for PK's index if it exist
if (((database instanceof OracleDatabase) || (database instanceof PostgresDatabase)) && (statement
.getPrimaryKeyConstraint().getTablespace() != null)) {
buffer.append(" USING INDEX TABLESPACE ");
buffer.append(statement.getPrimaryKeyConstraint().getTablespace());
}
buffer.append(",");
}
}
for (ForeignKeyConstraint fkConstraint : statement.getForeignKeyConstraints()) {
if (!(database instanceof InformixDatabase)) {
buffer.append(" CONSTRAINT ");
buffer.append(database.escapeConstraintName(fkConstraint.getForeignKeyName()));
}
String referencesString = fkConstraint.getReferences();
buffer.append(" FOREIGN KEY (")
.append(database.escapeColumnName(statement.getCatalogName(), statement.getSchemaName(), statement.getTableName(), fkConstraint.getColumn()))
.append(") REFERENCES ");
if (referencesString != null) {
if (!referencesString.contains(".") && (database.getDefaultSchemaName() != null) && database
.getOutputDefaultSchema()) {
referencesString = database.escapeObjectName(database.getDefaultSchemaName(), Schema.class) +"."+referencesString;
}
buffer.append(referencesString);
} else {
buffer.append(database.escapeObjectName(fkConstraint.getReferencedTableCatalogName(), fkConstraint.getReferencedTableSchemaName(), fkConstraint.getReferencedTableName(), Table.class))
.append("(")
.append(database.escapeColumnNameList(fkConstraint.getReferencedColumnNames()))
.append(")");
}
if (fkConstraint.isDeleteCascade()) {
buffer.append(" ON DELETE CASCADE");
}
if ((database instanceof InformixDatabase)) {
buffer.append(" CONSTRAINT ");
buffer.append(database.escapeConstraintName(fkConstraint.getForeignKeyName()));
}
if (fkConstraint.isInitiallyDeferred()) {
buffer.append(" INITIALLY DEFERRED");
}
if (fkConstraint.isDeferrable()) {
buffer.append(" DEFERRABLE");
}
buffer.append(",");
}
for (UniqueConstraint uniqueConstraint : statement.getUniqueConstraints()) {
if (uniqueConstraint.getConstraintName() != null) {
buffer.append(" CONSTRAINT ");
buffer.append(database.escapeConstraintName(uniqueConstraint.getConstraintName()));
}
buffer.append(" UNIQUE (");
buffer.append(database.escapeColumnNameList(StringUtils.join(uniqueConstraint.getColumns(), ", ")));
buffer.append(")");
buffer.append(",");
}
/*
* Here, the list of columns and constraints in the form
* ( column1, ..., columnN, constraint1, ..., constraintN,
* ends. We cannot leave an expression like ", )", so we remove the last comma.
*/
String sql = buffer.toString().replaceFirst(",\\s*$", "")+")";
if ((database instanceof MySQLDatabase) && (mysqlTableOptionStartWith != null)){
LogService.getLog(getClass()).info(LogType.LOG, "[MySQL] Using last startWith statement ("+mysqlTableOptionStartWith.toString()+") as table option.");
sql += " "+((MySQLDatabase)database).getTableOptionAutoIncrementStartWithClause(mysqlTableOptionStartWith);
}
if ((statement.getTablespace() != null) && database.supportsTablespaces()) {
if ((database instanceof MSSQLDatabase) || (database instanceof SybaseASADatabase)) {
sql += " ON " + statement.getTablespace();
} else if ((database instanceof AbstractDb2Database) || (database instanceof InformixDatabase)) {
sql += " IN " + statement.getTablespace();
} else {
sql += " TABLESPACE " + statement.getTablespace();
}
}
if((database instanceof MySQLDatabase) && (statement.getRemarks() != null)) {
sql += " COMMENT='"+database.escapeStringForDatabase(statement.getRemarks())+"' ";
}
additionalSql.add(0, new UnparsedSql(sql, getAffectedTable(statement)));
return additionalSql.toArray(new Sql[additionalSql.size()]);
}
4.达梦不支持liquibase xm中的loadData标签需要做适配修改ModifyDataTypeGenerator类
protected String getModifyString(Database database) {
if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase) || (database instanceof
MySQLDatabase) || (database instanceof OracleDatabase) || (database instanceof InformixDatabase) || (database instanceof DMDatabase)
) {
return "MODIFY";
} else {
return "ALTER COLUMN";
}
}
protected String getPreDataTypeString(Database database) {
if ((database instanceof DerbyDatabase) || (database instanceof AbstractDb2Database)) {
return " SET DATA TYPE ";
} else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase) || (database
instanceof MSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof HsqlDatabase) ||
(database instanceof H2Database) || (database instanceof OracleDatabase) || (database instanceof
InformixDatabase) || (database instanceof DMDatabase )) {
return " ";
} else {
return " TYPE ";
}
}
关于达梦数据库的问题可以去达梦社区提问https://eco.dameng.com/community/question/