2

我想从与大多数提出的策略不同的方向来处理 NHibernate 中的数据库版本控制和自动升级。

由于每个对象都由 XML 映射定义,我想为每个映射文件/配置获取大小和校验和,并将其与潜在的自定义更新脚本一起存储在文档数据库(raven 或其他东西)中。如果没有找到脚本,使用 NHibernate DDL 生成器更新对象模式。通过这种方式我可以检测到更改,如果我需要在 DDL 之外进行 DML 更改,或者执行仔细排序的转换,理论上我可以以受控、可测试的方式进行。这也应该保持一定程度的持久层不可知论,尽管我认为脚本仍然必须是特定于数据库系统的。

诀窍是,从数据库中生成“旧”映射文件并将它们与当前映射文件进行比较。我不知道这是否可能。我也不知道我是否遗漏了其他任何东西,这会使这个策略变得非常不切实际。

那么我的问题是:这个策略有多实用,为什么?

4

1 回答 1

5

我做了什么来解决这个问题

  1. 在名为 SchemaVersion 的表中对数据库进行版本控制
  2. 查询表以查看架构是否是最新的(存储在 DAL 中的所需版本),如果是则转到 6。
  3. 从 resources/webservices/... 获取带有 version == versionFromBb 的 updatescript
  4. 运行脚本,该脚本还将架构版本更改为新版本
  5. 转到 2。
  6. 运行应用

生成脚本我使用了 2 个选项

  1. 支持一个 rdbms:运行 SchemaUpdate 导出到文件并手动添加 DML 语句
  2. 支持多个 rdbms:使用 Nhibernate 类表在运行时生成 ddl 以添加/更改/删除表和使用会话 DML 的代码

更新:

“你用什么方法存储当前版本”

小例子

像这样的东西

public static class Constants
{
    public static readonly Version DatabaseSchemaVersion = new Version(1, 2, 3, 4);
}

public class DBMigration
{
    private IDictionary<Version, Action> _updates = new Dictionary<Version, Action>();
    private Configuration _config;
    private Dialect _dialect;
    private IList<Action<ISession>> _actions = new List<Action<ISession>>(16);
    private string _defaultCatalog;
    private string _defaultSchema;

    private void CreateTable(string name, Action<Table> configuretable)
    {
        var table = new Table(name);
        configuretable(table);

        string createTable = table.SqlCreateString(_dialect, _config.BuildMapping(), _defaultCatalog, _defaultSchema);
        _actions.Add(session => session.CreateSQLQuery(createTable).ExecuteUpdate());
    }

    private void UpdateVersionTo(Version version)
    {
        _actions.Add(session => { session.Get<SchemaVersion>(1).Value = version; session.Flush(); });
    }

    private void WithSession(Action<session> action)
    {
        _actions.Add(action);
    }

    public void Execute(Configuration config)
    {
        _actions.Clear();
        _defaultCatalog = config.Properties[NH.Environment.DefaultCatalog];
        _defaultSchema = config.Properties[NH.Environment.DefaultSchema];
        _config = config;
        _dialect = Dialect.GetDialect(config.Properties);

        using (var sf = _config.BuildSessionFactory())
        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            Version dbVersion = session.Get<SchemaVersion>(1).Value;
            while (dbVersion < Constants.DatabaseSchemaVersion)
            {
                _actions.Clear();
                _updates[dbVersion].Invoke(); // init migration, TODO: error handling
                foreach (var action in _actions)
                {
                    action.Invoke(session);
                }
                tx.Commit();
                session.Clear();
                dbVersion = session.Get<SchemaVersion>(1).Value;
            }
        }
    }

    public DBMigration()
    {
        _updates.Add(new Version(1, 0, 0, 0), UpdateFromVersion1);
        _updates.Add(new Version(1, 0, 1, 0), UpdateFromVersion1);
        ...
    }

    private void UpdateFromVersion1()
    {
        AddTable("Users", table => table.AddColumn(...));
        WithSession(session => session.CreateSqlQuery("INSERT INTO ..."));
        UpdateVersionTo(new Version(1,0,1,0));
    }

    ...
}
于 2012-07-12T09:21:36.187 回答