diff --git a/go.mod b/go.mod
index fc63353..6ee0e28 100644
--- a/go.mod
+++ b/go.mod
@@ -4,5 +4,5 @@ go 1.14
 
 require (
 	github.com/go-sql-driver/mysql v1.7.0
-	gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55
+	gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde
 )
diff --git a/go.sum b/go.sum
index 5db00fb..8165163 100644
--- a/go.sum
+++ b/go.sum
@@ -7,4 +7,8 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
 gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
 gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
 gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 h1:sC1Xj4TYrLqg1n3AN10w871An7wJM0gzgcm8jkIkECQ=
-gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
\ No newline at end of file
+gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+gorm.io/gorm v1.25.6-0.20231115133256-3207ad6033aa h1:cGDODlWbAjO6jreDwDD34SsdQVhuFE5KjXO1LmpKZYQ=
+gorm.io/gorm v1.25.6-0.20231115133256-3207ad6033aa/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde h1:9DShaph9qhkIYw7QF91I/ynrr4cOO2PZra2PFD7Mfeg=
+gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
diff --git a/migrator.go b/migrator.go
index d29d204..cd492b2 100644
--- a/migrator.go
+++ b/migrator.go
@@ -47,6 +47,78 @@ func (m Migrator) FullDataTypeOf(field *schema.Field) clause.Expr {
 	return expr
 }
 
+// MigrateColumnUnique migrate column's UNIQUE constraint.
+// In MySQL, ColumnType's Unique is affected by UniqueIndex, so we have to take care of the UniqueIndex.
+func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
+	unique, ok := columnType.Unique()
+	if !ok || field.PrimaryKey {
+		return nil // skip primary key
+	}
+
+	queryTx, execTx := m.GetQueryAndExecTx()
+	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
+		// We're currently only receiving boolean values on `Unique` tag,
+		// so the UniqueConstraint name is fixed
+		constraint := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName)
+		if unique {
+			// Clean up redundant unique indexes
+			indexes, _ := queryTx.Migrator().GetIndexes(value)
+			for _, index := range indexes {
+				if uni, ok := index.Unique(); !ok || !uni {
+					continue
+				}
+				if columns := index.Columns(); len(columns) != 1 || columns[0] != field.DBName {
+					continue
+				}
+				if name := index.Name(); name == constraint || name == field.UniqueIndex {
+					continue
+				}
+				if err := execTx.Migrator().DropIndex(value, index.Name()); err != nil {
+					return err
+				}
+			}
+
+			hasConstraint := queryTx.Migrator().HasConstraint(value, constraint)
+			switch {
+			case field.Unique && !hasConstraint:
+				if field.Unique {
+					if err := execTx.Migrator().CreateConstraint(value, constraint); err != nil {
+						return err
+					}
+				}
+			// field isn't Unique but ColumnType's Unique is reported by UniqueConstraint.
+			case !field.Unique && hasConstraint:
+				if err := execTx.Migrator().DropConstraint(value, constraint); err != nil {
+					return err
+				}
+				if field.UniqueIndex != "" {
+					if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
+						return err
+					}
+				}
+			}
+
+			if field.UniqueIndex != "" && !queryTx.Migrator().HasIndex(value, field.UniqueIndex) {
+				if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
+					return err
+				}
+			}
+		} else {
+			if field.Unique {
+				if err := execTx.Migrator().CreateConstraint(value, constraint); err != nil {
+					return err
+				}
+			}
+			if field.UniqueIndex != "" {
+				if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
+					return err
+				}
+			}
+		}
+		return nil
+	})
+}
+
 func (m Migrator) AddColumn(value interface{}, name string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
 		// avoid using the same name field
@@ -209,22 +281,6 @@ func (m Migrator) DropTable(values ...interface{}) error {
 	})
 }
 
-func (m Migrator) DropConstraint(value interface{}, name string) error {
-	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
-		if chk != nil {
-			return m.DB.Exec("ALTER TABLE ? DROP CHECK ?", clause.Table{Name: stmt.Table}, clause.Column{Name: chk.Name}).Error
-		}
-		if constraint != nil {
-			name = constraint.Name
-		}
-
-		return m.DB.Exec(
-			"ALTER TABLE ? DROP FOREIGN KEY ?", clause.Table{Name: table}, clause.Column{Name: name},
-		).Error
-	})
-}
-
 // ColumnTypes column types return columnTypes,error
 func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
 	columnTypes := make([]gorm.ColumnType, 0)