Rafy 框架 - 大批量导入实体

某些场景下,开发者希望能够大批量地把实体的数据导入到数据库中。虽然使用实体仓库保存实体列表非常方便,但是其内部实现机制是一条一条的保存到数据库,当实体的个数较多时,效率就会很低。所以 Rafy 设计了批量导入插件程序,其内部使用 ADO.NET 及 ODP.NET 中的批量导入机制来把大量数据一次性导入到数据库中。

使用方法

步骤
  1. 由于批量导入功能是一个额外的程序集,所以在使用该功能时,需要先使用 NuGet 引用最新版本的 Rafy.Domain.ORM.BatchSubmit 程序集。
  2. 如果准备导入 ORACLE 数据库,则也需要引用 Oracle.ManagedDataAccess(12.1.022 以上版本) 程序集。
  3. 修改需要保存大量实体的代码,例如,原代码如下:
var books = new BookList();
for (int i = 0; i < 1000000; i++)
{
    var book = new Book
    {
        ChapterList =
        {
            new Chapter(),
            new Chapter(),
        }
    };
    books.Add(book);
}

//直接使用实体仓库进行保存。
repo.Save(books);

需要把最后一行使用仓库保存实体列表,修改为创建导入器来保存实体列表:

//创建一个批量导入器进行保存。
repo.CreateImporter().Save(books);

注意

  • 从上面的代码可以看出,批量导入程序是面向整个聚合的。也就是说,批量导入父实体时,同时也会批量导入父实体下的所有子实体。
  • 批量导入不但支持添加新实体,同时也支持批量更新、批量删除。使用方法与使用仓库保持一致。
  • 对于大批量的数据,使用批量导入,比直接使用仓库来保存实体,速度要快两个数据级左右。
  • 目前批量导入实体的功能,只支持 Oracle 和 SqlServer 两个数据库。
  • 在使用 Oracle 数据库时,还需要在数据库生成完成后,特别地调用以下代码以启用某个聚合实体的批量导入功能,否则导入过程中会抛出异常(原因请见后面的实现原理章节)。代码如下:
Rafy.Domain.ORM.BatchSubmit.Oracle.OracleBatchImporter.EnableBatchSequence(
    RF.Concrete<OriginalDataRepository>()
    );

实现原理

下面简要介绍批量导入的原理。

Sql Server

对于 Sql Server 数据库的批量保存:

  • 批量新增数据,是使用 System.Data.SqlClient.SqlBulkCopy 来实现的。
  • 批量更新数据,是使用 System.Data.SqlClient.SqlDataAdapter 来实现的。
  • 批量删除数据,则是直接拼接 SQL 语句,把需要删除的实体的 Id 放到 In 语句中进行删除。例如:
DELETE FROM Books WHERE Id IN (1,3,5,7......);
Oracle

对于 Oracle 数据库的批量保存:

  • 新增数据、更新数据都是使用 ODP.NET 中原生的批量导入功能。 参见:Oracle.ManagedDataAccess.Client.OracleCommand.ArrayBindCount 属性。
  • 而删除数据的实现则和 SQLServer 的实现一致,均是拼接 DELETE 语句。
新增大量实体时,实体的 Id 生成

一般情况下,使用仓库保存一个新增的实体时,仓库会使用数据库本身的机制来为实体生成 Id,在 SQLServer 中是使用 IDENTITY 列,在 ORACLE 中则是使用每个表对应的 SEQUENCE 来生成。但是,批量导入大量新实体时,为了性能上的考虑,则需要一次性为需要保存的所有新实体统一生成 Id。

在 SQLServer 中,可以方便地使用 SQL 语句调整表中 IDENTITY 下一次的值,所以实现比较简单。只需要设置 IDENTITY 下一次的值 + 100000,并使用中间跳过的这些值来作为实体的 Id 即可。

但是在 ORACLE 中,如果去调整 SEQUENCE 的值,则属于 DDL 语句,会隐式自动提交事务,会造成数据的错误。所以我们最终决定:如果在 ORACLE 中要使用批量导入功能,数据表对应的 SEQUENCE 必须以较大的数字为步距(如 ALTER SEQUENCE "SEQ_TABLE_ID" INCREMENT BY 100000 NOCACHE)。这样,在批量导入时,就不再需要增修改 SEQUENCE 的步距,而直接使用中间跳过的这些值作为实体的 Id。这样做也比较方便,但是负面效果则是使用仓库保存单一实体时,两次保存不同实体生成的 Id 会相差 100000,不再是连续的。

PS:该文已经纳入《 Rafy 用户手册》中。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小文章

使用mysql事务不同场景导致的死锁问题以及解决方法

MySQL各存储引擎使用了三种级别的锁定机制:table-level(表级锁定),row-level(行级锁定)和page-level(页级...

1544
来自专栏BigNerdCoding

SQLite 并发的四种处理方式

SQLite 是一款轻型的嵌入式数据库它占用资源非常的低,处理速度快,高效而且可靠。在嵌入式设备中,可能只需要几百 K 的内存就够了。因此在移动设备爆发时,它依...

9777
来自专栏c#开发者

Oracle 开放源代码项目

Oracle 开放源代码项目 这是无数个可扩展、使用以及构建于 Oracle 技术的开放源代码项目中的一个简短的示例。如果您有自己喜欢的开放源代码项目未在此处列...

8108
来自专栏MYSQL轻松学

Mysql5.5&Mysql5.6&Mysql5.7特性

Mysql5.5 特性,相对于Mysql5.1 性能提升 默认InnoDB plugin引擎。具有提交、回滚和crash恢复功能、ACID兼容。 行级锁(一致性...

4275
来自专栏数据库

存储过程和触发器的应用

实验案例三:创建视图 方法一:在图形界面下创建视图(以Myschool数据库为例) 创建一个视图,分别来自三个的表的三个列,并重命名列,生成的视图名为stude...

22210
来自专栏架构师之路

InnoDB并发如此高,原因竟然在这?

《InnoDB行锁,如何锁住一条不存在的记录?》埋了一个坑,没想到评论反响剧烈,大家都希望深挖下去。原计划写写InnoDB的锁结束这个case,既然呼声这么高,...

1403
来自专栏做全栈攻城狮

C#(Net)软件开发常用工具汇总,提高你的开发效率

作为C#语言官方的开发工具,VS的强大只有在多种语言开发工具使用之后,你才会明白VS的强大之处。可谓神器。其中,开发工具尽量选择版本高的。数据库尽量选择版本低的...

1342
来自专栏逸鹏说道

保障MySQL数据安全的14个最佳方法

有的企业在安装MySQL时用的是默认选项,由此造成其数据不安全,且服务器也面临被入侵的风险,并有可能在短时间内就出现性能问题。本文将提供保障MySQL安全的最佳...

50810
来自专栏「3306 Pai」社区

海豚 VS 大象 功能对比

MySQL为多线程架构后台有多个线程处理内部操作例如:刷脏、Undo purge、checkpoint等,整体上MySQL分为两层Server/存储引擎。存储引...

2503
来自专栏DHUtoBUAA

Python读取SQLite文件数据

  近日在做项目时,意外听说有一种SQLite的数据库,相比自己之前使用的SQL Service甚是轻便,在对数据完整性、并发性要求不高的场景下可以尝试!   ...

5359

扫码关注云+社区

领取腾讯云代金券