前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DataSet导入三个坑

DataSet导入三个坑

作者头像
Antony
发布2020-12-02 10:21:07
1.1K0
发布2020-12-02 10:21:07
举报
文章被收录于专栏:软件测试那些事

1外键

外键是一个常见的保证数据库内容完整性的一种方式。当然现在出于性能考虑,在互联网企业中比较少甚至禁止使用外键。在DBRider中,提供了以下的与外键相关的功能 1)@DataSet注解中的disableConstraints属性 这个属性如果为true,则可以暂时去除外键约束,以便于数据导入操作。

代码语言:javascript
复制
@DataSet(value ="users.yml", strategy = SeedStrategy.UPDATE,
       disableConstraints = true,cleanAfter = true,transactional = true)
public void shouldLoadDataSetConfigFromAnnotation(){

 }

2)@ExportedDataSet导出时一并导出关联表 在导入某个数据库表的数据时,如果存在外键的话,经常会发生因为外键不存在导致的数据无法导入的问题。为了预防此类事件的发生,一个好的措施是在导出目标表时将依赖数据表一并导出。DBRider在@ExportDataSet中通过dependentTables提供了该功能。如下例,

代码语言:javascript
复制
@Test
@DataSet("datasets/yml/users.yml")
@ExportDataSet(format = DataSetFormat.YML, includeTables = {"USER"}, dependentTables = true, outputName = "target/exported/yml/dependentTables.yml")
public void shouldExportYMLDataSetUsingIncludesWithDependentTables() {
}

只要简单地将dependentTables设置为 true,就可以实现上述需求。虽然只是导出USER表,但是TWEET和FOLLOWER两个表也被导出了。因为USER表中使用了这两个表中的主键作为外键,表达用户粉与被粉的关系。

代码语言:javascript
复制
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
  <TWEET ID="abcdef12345" CONTENT="dbunit rules!" DATE="2020-10-02 17:07:52.0" USER_ID="1"/>
  <USER ID="1" NAME="@realpestano"/>
  <USER ID="2" NAME="@dbunit"/>
  <FOLLOWER ID="1" USER_ID="1" FOLLOWER_ID="2"/>
</dataset>

自增序列与ID主键冲突

在往数据库中导入数据时,除了因为外键约束不满足导致无法导入的问题之外,另外一种常见的问题是主键冲突,或者更确切一点说是某个带有自增ID序列带来的冲突。如果在数据库中插入该表的记录,则新插入的值不能和已有的值重复,而且必须大于其中最大的一个值。一般通过程序写入数据库记录大多是新增记录的场景,不指定该列的值,只将其他列的值插入,让ID按照自增规则由数据库自行填写的方式进行。而在通过数据库导入时,属于控制数据库上下文的场景。往往就会产生冲突, 1)导入记录中需指定自增ID的主键值,以保证被导入数据的完整性。 2)待导入的数据源自数据库之前的某一次导出的数据集。随后数据库经历了反复插入删除等操作后,自增主键值已经向后偏移。例如针对某个场景有多个测试用例需要导入数据导同一个表。后续用例的执行上下文于是受到了前面执行用例的影响。 3)导入时通过默认的CLEAN_INSERT策略进行导入,虽然删除了原先存在的数据,但是数据库的自增主键值并没有回退,这样就导致导入记录时报主键冲突。 那是否可以通过采用INSERT/UPDATE的策略呢?之前在介绍各种导入策略时有提及,只INSERT而不是先删除再导入时,会存在数据记录重复无法导入的问题,而在这个场景下,因为主键冲突带来的问题还是没有解决。那是否可以使用UPDATE策略来更新各个记录的主键ID呢?考虑到一般采用主键ID的是记录类数据的场景,无法保证原记录的存在,所以也不太适合使用UPDATE的策略。 从上述问题描述中,读者也理解到了问题产生的原因并不在主键ID和记录自身,而是因为在原数据集导出后,在保持数据不变的情况下,数据库中该表经历了插入和删除后,自增序列已经向后偏移。 于是,只要保证自增序列ID的值小于待插入数据的值,该问题就能规避掉了。利用@DataSet的伴随操作就可以了 解决办法1:利用executeStatementsBefore 重置自增序列

代码语言:javascript
复制
@Test
@DataSet(value = "datasets/yml/users.yml",
executeStatementsBefore = "alter table USER auto_increment = 100;")
public void shouldSeedDataSetDisablingContraintsViaStatement() {
//......
}

通过重置该表的Sequence到一个小于待导入数据集中最小ID的值,再配合随后的CLEAN_INSERT操作,就可以规避该问题了。当然这个操作需要在准备好数据集后,根据具体内容进行修改上述executeStatementsBefore 。因为很有可能待导入数据源自某一份导出数据,根据测试用例需求稍加修改而来,因此该部分修改也具备一定的通用性,工作量可控。 另外,上述示例代码是MYSQL语法,如果目标数据库是ORACLE,重置Sequence的写法略有差别。 解决办法2:利用TRUNCATE_INSERT的SeedStrategy 通过源码分析,可以了解到DBRider还额外提供了TRUNCATE_INSERT的操作,虽然该功能未在其官方文档中说明,但是也可以直接使用。

代码语言:javascript
复制
public enum SeedStrategy {
    CLEAN_INSERT(DatabaseOperation.CLEAN_INSERT),
    TRUNCATE_INSERT(new CompositeOperation(DatabaseOperation.TRUNCATE_TABLE, DatabaseOperation.INSERT)),
    INSERT(DatabaseOperation.INSERT),
    REFRESH(DatabaseOperation.REFRESH),
    UPDATE(DatabaseOperation.UPDATE);
//......
}

利用TRUNCATE操作提供的重置序列的功能,强制重新初始化,进而保证了数据导入时不再会发生自增主键冲突的问题。 当然也可以参考被测系统向数据库插入数据时不指定ID,而是由数据库自行决定的方式,不过这个方案相比前面的来说略显复杂,涉及到导出数据时剔除该列数据,工作量较大,不是很推荐。感兴趣的读者可以自行尝试。

Null处理

数据库中最容易让程序员踩坑的问题如果进行一个排名,估计Nullable会排在最前面。如果一个数据列是Nullable,在导入导出时会遇到不少问题。 首先DBRider 在使用JSON格式在导出null时,会在该条记录的最后位置额外多一个逗号,导致导出内容不符合JSON格式,需要手工修改。当然,该问题在报告之后很快就被修复了。详见fix bug with extra comma in json object #160,但似乎还是未解决,如果遇到,需要手工处理,或尝试最新的DBRider版本。 其次是在数据导入时的问题,DBUnit一个著名的bug是在导入XML、CSV格式的文件时,如果待导入文件的第一条记录的Nullable列的数据正好是Null,那么DBUnit会忽略该列,整列数据都会被丢失,即使后续数据记录中该列不为Null,也会被忽略而不导入进数据库。 解决办法1:调整数据行顺序,让第一条记录包含不为Null 这样做是最简单的处理方式,正所谓将问题解决在发生前。不过数据文件较多时手工调整也比较麻烦,或者记录顺序调整会影响测试用例执行结果时,这样调整就会带来麻烦了。 解决办法2:XML导入时指定DTD DBUnit给出的一个解决办法是,在导出XML文件的同时,再导出一份XML_DTD,来指明数据库的列。导入数据时,利用DTD来指定数据列,如下例:

代码语言:javascript
复制
<!--ELEMENT COMPANY EMPTY-->
<!--ATTLIST COMPANY
    ID CDATA #REQUIRED
    NAME CDATA #REQUIRED
    PARENT CDATA #IMPLIED
  -->
]>
<dataset>
  <company id="1" name="HQ"></company>
  <company id="2" name="spain hq" parent="1"></company>
  <company id="3" name="barcelona" parent="2"></company>
</dataset>

这样就不会发生因为Null导致的数据列丢失问题了。 解决办法3:利用DBRider提供的JSON/YAML文件格式进行导入 新的数据类型规避了上述DBUnit的缺陷,因此不会再发生整列数据丢失的问题了。这也是笔者喜欢DBRier的原因之一。 还有什么样的数据导入导出的坑呢?欢迎与笔者讨论。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 软件测试那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1外键
  • 自增序列与ID主键冲突
  • Null处理
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档