[ActiveRecord] 之:多数据库配置

本文介绍如何使用ActiveRecord实现多数据库配置,通过继承基类并配置XML文件来区分不同数据库连接,适用于NHibernate环境下多种数据库的支持。


ActiveRecord 的多数据库配置基本沿袭了 NHibernate 的思想,只不过在配置文件结构上作了些调整。
1. 采用继承方式,归纳使用同一数据库的类型。比如 A、B、C、D、E 中 A、B连接到数据库Test1,C、D连接到Test2,而E连接到缺省的Test,那么具体的代码就会是下面这种方式。

public abstract class Test1Base : ActiveRecordBase
{
}

public abstract class Test2Base : ActiveRecordBase
{
}

[ActiveRecord("A")]
public class A : Test1Base
{
}

[ActiveRecord("B")]
public class B : Test1Base
{
}

[ActiveRecord("C")]
public class C : Test2Base
{
}

[ActiveRecord("D")]
public class D : Test2Base
{
}

[ActiveRecord("E")]
public class E : ActiveRecordBase
{
}


config.xml

<?xml version="1.0" encoding="utf-8" ?>
<activerecord>

    <config>
        <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
        <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
        <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
        <add key="hibernate.connection.connection_string" value="Data Source=localhost;Initial Catalog=Test;UID=sa;Password=sa" />
    </config>

    <config type="ConsoleApplication1.CastleActiveRecord.Test1Base , Learn.CUI">
        <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
        <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
        <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
        <add key="hibernate.connection.connection_string" value="Data Source=localhost;Initial Catalog=Test1;UID=sa;Password=sa" />
    </config>

    <config type="ConsoleApplication1.CastleActiveRecord.Test2Base , Learn.CUI">
        <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
        <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
        <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
        <add key="hibernate.connection.connection_string" value="Data Source=localhost;Initial Catalog=Test2;UID=sa;Password=sa" />
    </config>
</activerecord>


我们会发现,ActiveRecord 通过使用公用基类的方式来实现多个数据库连接配置的。只要继承自指定的基类,我们就可以使用不同的目标数据库。在配置文件中我们通过增加多个 Config Section,同时指定 Config Type 属性,就可以让 ActiveRecord 使用多数据库配置。Config Type 就是我们编写的基类,所有继承自该基类的类型都会使用该连接配置,而那些直接继承自 ActiveRecordBase 的类型还是使用缺省的连接配置。

上面的例子使用了 SQL Server 2000 的不同数据库,同样我们也可以连接到不同的数据库系统,如 A、B 连接到 SQL Server,C、D 连接到 DB2 等等。

2. 基类需要遵循一定的规则。

(1) 必须继承自 ActiveRecordBase。
(2) 必须是抽象类。
(3) 可以不添加 [ActiveRecord()] 特性。
(4) 必须初始化抽象基类。如 "ActiveRecordStarter.Initialize(source, typeof(Test1Base ));" ,如果使用 "ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(), source);" 那么就给抽象基类添加 "[ActiveRecord()]" 即可。

下面是一个相对完整的代码演示。

namespace ConsoleApplication1.CastleActiveRecord
{
    [ActiveRecord()]
    public abstract class Base : ActiveRecordBase
    { 
    }

    [ActiveRecord("Users")]
    public class User : Base
    {
        private int id;

        [PrimaryKey(PrimaryKeyType.Identity)]
        public int Id
        {
            get { Console.WriteLine("Id..."); return id; }
            set { id = value; }
        }

        private string name;

        [Property(Unique=true)]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }

    public class ActiveRecordTest
    {
        static ActiveRecordTest()
        {
            // 获取数据库连接配置
            XmlConfigurationSource source = new XmlConfigurationSource(@"Config/ActiveRecord.xml");
            
            // 载入程序集中所有 ActiveRecord 类。
            ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(), source);

            // 自主载入指定类型
            //ActiveRecordStarter.Initialize(source, typeof(ActiveRecordBase), typeof(User), typeof(Base));

            // 删除数据库架构
            //ActiveRecordStarter.DropSchema();
            
            // 创建数据库架构(该方法会删除同名表后再创建)
            ActiveRecordStarter.CreateSchema();
        }

        public static void Test()
        {
            User user = new User();
            user.Name = "tom" + new Random(DateTime.Now.Millisecond).Next();

            ActiveRecordMediator.Create(user);
        }
    }
}


ActiveRecord.xml

<?xml version="1.0" encoding="utf-8" ?>
<activerecord>
    <config>
        <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
        <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
        <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
        <add key="hibernate.connection.connection_string" value="Data Source=localhost;Initial Catalog=Test2;UID=sa;Password=sa" />
    </config>
    
    <config type="ConsoleApplication1.CastleActiveRecord.Base, Learn.CUI">
        <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
        <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
        <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
        <add key="hibernate.connection.connection_string" value="Data Source=localhost;Initial Catalog=Test;UID=sa;Password=sa" />
    </config>
</activerecord>


-----------------

附:本文所有演示代码使用 2006-01-01 发布的 Castle ActiveRecord Beta3 版本。
Castle ActiveRecord 在发布 1.0 版本前可能有很多较大的变化,如演示代码无法编译,建议您参考最新版本的相关文档。

1. 问题描述 在使用 activerecord-sqlserver-adapter 时,事务块内抛出 ActiveRecord::Rollback 异常或直接触发异常后,事务未正确回滚,而是被提交(日志显示 COMMIT TRANSACTION)。此行为与预期不符,导致数据一致性问题。 2. 重现步骤 使用以下代码逻辑执行事务: ActiveRecord::Base.transaction do Contract.create!(result) # 假设 result 是有效数据 AnotherModel.create!(another_result) # 假设 another_result 是有效数据 raise ActiveRecord::Rollback # 或 raise "Test error" end 观察日志输出: TRANSACTION (2.3ms) COMMIT TRANSACTION => nil 检查数据库表中的数据是否已被提交(而非回滚)。 3. 预期行为 当事务块内抛出 ActiveRecord::Rollback 或非 ActiveRecord::Rollback 异常时,事务应被回滚(日志显示 ROLLBACK TRANSACTION),且数据库中的数据应无变化。 4. 实际行为 事务被提交(日志显示 COMMIT TRANSACTION),数据被持久化到数据库,未回滚。 5. 环境信息 项目 详细信息 activerecord-sqlserver-adapter version 7.0.7 Rails version 7.0.8.6 Ruby version 3.3.3 SQL Server version 2014 Database Configuration 数据库连接配置 production: sqlserver: adapter: sqlserver database: my_databse username: user password: psword! host: 192.168.28.102 port: 1433 timeout: 5000 options: "XACT_ABORT=true" # Setting or not setting it here has no effect 6. 日志输出 请提供完整的日志片段(脱敏敏感信息): [事务开始前的日志] TRANSACTION (1.7ms) BEGIN TRANSACTION [数据库操作日志] TRANSACTION (2.3ms) COMMIT TRANSACTION 7. 已尝试的解决方案 ✅ 使用 ActiveRecord::Base.transaction 替代手动调用 begin_transaction。 ✅ 在事务块内抛出 ActiveRecord::Rollback。 ✅ 在 database.yml 中添加 options: "XACT_ABORT=true"。 ✅ 升级 activerecord-sqlserver-adapter 到最新版本(运行 bundle update activerecord-sqlserver-adapter)。
05-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值