ORM相关问题

汇总常见技术疑问、解决思路和实践经验。

问题答案 12026年7月4日 18:07

如何在 TS 和 TypeORM 中创建泛型函数?

在TypeScript (TS) 和 TypeORM 中创建泛型函数允许代码在保持强类型的同时更具可重用性。下面,我将介绍一个例子来展示如何在TypeORM中使用TS创建泛型函数来处理数据库实体的通用操作。示例:创建一个通用的CRUD操作函数定义一个泛型接口 首先,我们需要定义一个泛型接口,它将定义所有实体必须实现的方法。这就为我们提供了一个通用的方式来处理不同的实体。实现泛型类接下来,我们定义一个泛型类,该类实现了接口。这个类将利用TypeORM的Repository来实现CRUD操作。使用泛型类现在我们可以创建特定类型的CRUD实例。例如,如果我们有一个实体,我们可以这样使用:总结通过使用泛型,我们可以创建一个强类型且可重用的CRUD类,它可以与任何TypeORM实体一起使用。这不仅减少了代码重复,也提高了代码的可维护性和类型安全。
问题答案 12026年7月4日 18:07

如何在 NestJS 中对 TypeORM 的自定义存储库进行单元测试?

在NestJS中使用TypeORM时,进行单元测试需要确保代码的可测试性和依赖项的正确管理。下面我将详细介绍如何为一个自定义的TypeORM存储库进行单元测试。步骤 1: 设置测试环境首先,需要确保你的测试环境已经搭建好了,这通常意味着你已经在你的项目中安装了Jest和@nestjs/testing模块。步骤 2: 创建存储库的Mock为了进行单元测试,我们需要模拟TypeORM的存储库。这里可以使用或者NestJS的装饰器来注入存储库的Mock。例如,假设我们有一个名为的自定义存储库:你可以创建一个Mock版本的这个存储库:步骤 3: 配置TestingModule接下来,在你的测试文件中,使用NestJS的模块来配置你的测试环境。确保你替换了真实的存储库实例为Mock实例:步骤 4: 编写单元测试现在,你可以针对你的或直接对中的方法编写单元测试。例如,测试方法:在这个测试中,我们验证了当调用时,是否返回了预期的用户对象,并且确认这个方法被正确地调用了。总结通过以上步骤,你可以有效地为NestJS中使用TypeORM的自定义存储库进行单元测试。关键在于使用Mock来隔离测试,确保不依赖外部数据库或其它服务,并且保持测试的独立性和可重复性。
问题答案 12026年7月4日 18:07

如何在带有typeorm的nestjs项目中使用外部实体?

在NestJS项目中使用TypeORM来处理外部实体主要涉及几个主要步骤,这些步骤确保你能有效地整合和使用这些实体,无论它们是定义在同一个项目中还是一个外部库中。下面我将详细阐述这一过程:步骤 1: 安装和配置TypeORM首先,确保你的NestJS项目已经安装了TypeORM。可以通过以下命令安装:接着,在你的或相关模块文件中引入:步骤 2: 引入外部实体假设你有一个外部npm包,比如,里面定义了一些你想在项目中使用的实体。首先需要安装这个包:然后,你可以在任何需要的模块中导入这些实体,并在方法中注册它们:步骤 3: 使用实体现在,你可以在服务中注入这些实体的repository,并进行数据库操作:示例:处理外部定义的实体假设有一个外部定义的实体,你需要在你的NestJS应用中增加一个功能,比如根据用户名查找用户。你可以像上面那样引入并使用这个实体:这是一个基本的流程,通过这种方式可以有效地在NestJS项目中整合和使用外部定义的typeorm实体。
问题答案 12026年7月4日 18:07

使用 typeorm 的如何对多列字段进行 Orderby

在使用TypeORM进行数据操作时,对多列进行排序是一个常见的需求,这可以通过在或者方法中使用选项来实现。以下是一些具体的实现方式和例子。使用方法当使用方法时,可以直接在属性中指定需要排序的字段及其方向(为升序,为降序)。例如,假设我们有一个实体,我们希望根据用户的进行升序排序,然后根据进行降序排序:使用方法使用可以提供更多的灵活性,尤其是在复杂的查询中。同样地,我们可以使用方法来指定排序的列和方向。例如,对实体进行同样的排序:在这个例子中,用于设置第一个排序条件,而可以添加更多的排序条件。这种方法在处理多列排序时非常有效,因为你可以连续调用来添加多个排序条件。混合使用关系和排序在处理含有关联关系的实体时,我们也可以对关联的列进行排序。例如,如果实体有一个关联的实体,我们想要根据中的字段进行排序,可以这样做:这样,我们不仅仅是对的直接属性进行排序,也能对其关联的的属性进行排序。总结以上就是在TypeORM中对多列进行排序的几种方法。通过方法可以快速实现简单的排序,而则提供了更高的灵活性和复杂查询的能力。在实际开发中,可以根据不同的场景选择合适的方法。
问题答案 12026年7月4日 18:07

如何在 TypeORM 中向查询生成器联接添加原始 PostgreSQL 函数?

在TypeORM中使用查询生成器添加原始的PostgreSQL函数可以让开发者直接使用数据库自带的功能进行复杂的查询操作,这是非常强大且灵活的。要在TypeORM的查询生成器中使用原始的PostgreSQL函数,我们可以使用方法。以下是一个具体的例子,展示如何在一个查询中加入PostgreSQL的函数,该函数用于将文本数据转化为小写。示例假设我们有一个名为的实体,其中包含字段和。现在我们想要基于小写的来搜索用户。我们可以这样做:在这个例子中,我们使用了函数来确保在比较时忽略大小写的差异。会将数据库中字段的每个值转换为小写,并将其与小写的输入参数相比较。扩展示例:使用更复杂的函数如果需要使用更复杂的PostgreSQL函数或者表达式,我们同样可以通过方法直接插入原始SQL语句。比如,我们想根据用户的创建日期进行筛选,使用PostgreSQL的函数来提取年份:注意事项使用原始SQL或特定函数时,需要特别注意SQL注入的风险。尽管TypeORM的参数替换功能提供了一定的安全保障,但在构建复杂的SQL语句时,确保验证和清理任何用户输入的数据是非常重要的。通过这些例子,我们可以看到在TypeORM中使用查询生成器结合原始的PostgreSQL函数是相对直接的,并能有效地利用数据库本身的功能来优化和简化数据查询。
问题答案 12026年7月4日 18:07

Typeorm 如何通过 Mongo 数据库为 boolean 提供默认值

在使用TypeORM进行MongoDB开发时,指定默认值是一种常见的需求,尤其是对于布尔类型的字段。为了在Schema中为一个布尔字段设置默认值,我们可以在实体的定义中使用装饰器的属性。下面是一个具体的例子:在这个例子中,我们定义了一个实体,其中包含一个字段。这个字段表示用户是否处于活跃状态。通过,我们设置了的默认值为。这意味着当新的实体被创建并保存到数据库中时,如果没有明确为字段提供值,它将自动被赋值为。这种设置对于确保数据完整性和简化代码逻辑非常有用,尤其是在处理大量需要默认状态标记的数据对象时。为了验证这一功能,我们可以创建一个新的用户而不显式设置字段,然后检查数据库中该字段的值确实为。通过这种方式,我们可以确保我们的应用在处理用户数据时能自动正确处理的默认状态,减少了需要手动设置此类数据的地方,使得代码更为简洁和健壮。
问题答案 12026年7月4日 18:07

如何在Nest js与 typeorm 连接时设置 autoLoadEntities : true

在使用NestJS结合TypeORM时, 的设置可以简化实体的注册过程。这个选项允许你自动将所有通过@Module装饰器或在模块内导入的实体添加到TypeORM的数据库连接中。下面是如何配置这个选项的具体步骤:步骤 1: 安装必要的包首先,确保你已经安装了NestJS和TypeORM相关的包。如果没有安装,可以通过以下命令进行安装:步骤 2: 配置TypeORMModule在你的NestJS应用中,你需要在根模块或者任何特定的模块中导入。这里是如何在根模块中使用进行配置的例子:步骤 3: 添加实体现在,你可以在应用中创建实体,并且不需要显式地将每个实体添加到数组中。只需要通过装饰器标记类,TypeORM会自动识别并加载这些实体。以下是一个实体的示例:步骤 4: 验证配置启动你的NestJS应用,并检查数据库,看看相应的表是否根据实体自动创建。如果配置正确,你应该能看到对应实体的数据库表已经被创建。总结通过设置,你可以省去手动注册每个实体的步骤,使得项目的数据库管理更为简洁和高效。这在大型项目中尤其有用,因为随着实体数量的增加,手动管理这些实体会变得越来越复杂。
问题答案 12026年7月4日 18:07

如何在TypeOrm实体中添加枚举数组?

在TypeORM中,要在实体中添加枚举数组,我们可以使用关键字以及属性。这样可以确保在数据库中该字段被正确地定义为枚举类型的数组。以下是一个具体的操作步骤和示例:步骤 1: 定义枚举类型首先,我们需要定义一个枚举类型。枚举(Enumeration)类型是一种特殊的数据类型,它包含一组有限的预定义值,并且这些值在逻辑上是相互关联的。例如,如果我们想要存储一个关于用户角色的枚举,我们可以这样定义:步骤 2: 在实体中使用枚举数组接下来,在实体定义中,我们使用这个枚举类型,并通过装饰器来指定该字段是一个枚举数组。我们设置和来指定字段的类型,同时设置来表示这是一个数组类型。在这个例子中,字段被定义为枚举类型的数组,这意味着每个元素都必须是枚举中的一个值。注意事项确保数据库支持枚举数组类型。比如在PostgreSQL中,这种类型是支持的,但在一些其他数据库中可能不支持。在进行数据库迁移时,TypeORM应能够正确处理枚举数组的创建。当处理数据查询或更新时,需要确保传递给枚举数组的值符合枚举定义。这种方法在管理具有多重角色或属性的用户时非常有用,并且能够确保数据的一致性和验证简单高效。
问题答案 12026年7月4日 18:07

如何在 NestJS 中的 TypeORM 中编写 setval ?

在NestJS中使用TypeORM时,如果你需要调整序列的当前值,例如在测试期间或者在重新调整数据库时,你可能会想要使用类似PostgreSQL的 函数。 函数是PostgreSQL特有的,用于设置序列的当前值。在TypeORM中,你可以通过执行原生SQL语句来完成这个操作。以下是一个具体的步骤和示例,说明如何在NestJS中使用TypeORM来调用 :步骤 1: 注入EntityManager首先,确保你的服务中注入了 。 允许你执行原生SQL查询。步骤 2: 执行原生SQL来设置序列值使用 的 方法执行原生SQL。你需要提供序列名和你想要设置的新值。这里 的第三个参数设置为 表示序列的下一个 调用将返回设定的这个新值。如果设置为 , 将返回这个新值加上序列的步长(通常是1)。示例:调整用户ID序列假设你有一个用户表 ,其ID是自增的。在某些情况下(比如数据迁移后),你可能需要重设这个ID的序列值。在这个例子中,如果你想让下一个用户ID为100,你应该调用 。此函数会将序列设置为99,因此,下一个通过 生成的ID将会是100。结论通过上述方式,你可以在使用NestJS和TypeORM的环境中灵活地调整序列值,这对于数据库维护和特定的应用场景非常有用。不过请注意,直接操作数据库序列可能会引入风险,尤其是在并发高的生产环境中,因此需要谨慎操作。
问题答案 12026年7月4日 18:07

如何在 nestjs 中保存多对多

在使用NestJS框架开发时,处理多对多关系通常涉及到使用TypeORM或Sequelize这样的ORM(对象关系映射)库。这里我将以TypeORM为例,说明如何在NestJS中设置和管理多对多关系。以一个常见的例子,比如用户(User)和角色(Role)之间的多对多关系,来展示这个过程。步骤 1: 创建实体首先,我们需要为和创建两个实体,并在这两个实体中定义它们之间的多对多关系。在上述代码中,装饰器用来建立两个实体之间的多对多关系。装饰器则是用来指明这个关系的连接表,通常只需要在一个实体中定义。步骤 2: 数据库迁移或同步确保你的数据库能够反映这些新的实体和关系。如果你使用的是TypeORM的自动同步功能(不推荐在生产环境使用),则当应用程序启动时,TypeORM会自动创建或修改数据库表以匹配你的实体定义。步骤 3: 创建或更新数据在服务或控制器中,你可能需要写逻辑来创建或更新用户和角色的数据。例如,你可能需要添加一个用户,并将其关联到特定的角色。在这个例子中,方法首先创建一个新的对象,并根据传入的角色名称查找对应的对象。然后,它将这些角色对象赋值给用户的属性,并保存这个用户。步骤 4: 查询数据查询涉及到多对多关系的数据也很直接。在上述方法中,方法的选项告诉TypeORM在查询用户时也要抓取关联的角色。总结在NestJS中管理多对多关系涉及到定义正确的实体关系、确保数据库表正确设置、以及在业务逻辑中正确处理这些关系。以上步骤展示了如何在NestJS项目中使用TypeORM来管理多对多关系。这种结构不仅有助于保持代码的整洁,也使得数据操作更加直观易管理。
问题答案 12026年7月4日 18:07

测试 TypeORM 的 QueryBuilder 链接方法的方法是什么?

1. 单元测试单元测试是确保 的 方法按预期工作的基础。这包括测试各种SQL联接类型如INNER JOIN、LEFT JOIN等。例子:假设我们有一个用户(User)和订单(Order)两个实体,并且一个用户可以有多个订单。我们可以编写一个测试来验证是否正确创建了INNER JOIN语句:2. 集成测试单元测试后,集成测试是必要的,以确保 与数据库引擎正确交互。这包括验证查询是否返回正确的数据集。例子:继续上面的例子,我们可以实施一个测试来验证查询是否返回了正确的用户和订单数据:3. 性能测试性能测试确保 方法在处理大量数据时的表现。这对于大型数据库尤其重要。例子:可以使用工具如 或 来模拟高并发环境下 查询的性能。4. 错误处理测试确保代码能恰当处理错误情况,如尝试对不存在的字段进行join。例子:通过这些测试,我们可以较为全面地验证 的 方法的正确性、效率和健壷性。这些测试还帮助确保未来对代码的修改不会引入新的问题。
问题答案 12026年7月4日 18:07

如何在Type-graphql中为并集类型执行字段解析程序

在 Type-GraphQL 中实现并集类型的字段解析程序(resolver function),主要目的是能够处理并集类型中不同成员的数据,并返回正确的类型实例。并集类型(Union types)是 GraphQL 中一个有用的特性,它允许字段具有多种类型。首先,我们需要定义并集类型。假设我们有两种类型: 和 。这两种类型的共同点是它们都是媒体类型,但是它们也有各自唯一的字段。接下来,我们需要为这个并集类型创建一个解析程序:在这个例子中,我们创建了 并集类型,该类型可以是 或 类型。在 解析器中,我们通过检查是否存在某个特定的字段(例如 或 ),来决定返回的具体类型。 方法是自动被 TypeGraphQL 调用的,用来在运行时确定并集类型的具体类型。这使得 GraphQL 能够正确地返回不同的类型,并在客户端正确地查询这些类型的字段。
问题答案 12026年7月4日 18:07

如何在inversify中注入异步依赖项?

在Inversify中,要注入异步依赖项,我们通常需要将这些依赖项包装在一个可以同步返回的构造或工厂函数中。Inversify本身不直接支持异步依赖注入,因为它的设计目标是同步的依赖注入容器。然而,我们可以通过一些设计模式来间接实现异步依赖的注入。以下是实现这一功能的一种方法:使用工厂方法定义异步依赖项:首先,定义你的异步依赖项。这通常是一个返回Promise的函数或者可能是一个异步获取资源的类。创建一个工厂函数:接下来,创建一个工厂函数,这个函数负责创建异步依赖项的实例。工厂函数本身是同步的,但它返回的是一个Promise,该Promise解析为异步依赖的实例。在Inversify容器中注册工厂:将工厂函数作为绑定到Inversify容器。你可以使用方法来实现。注入并使用工厂:在需要异步依赖项的类中,你可以注入这个工厂,使用时通过工厂创建依赖项。总结虽然Inversify不直接支持异步依赖注入,通过使用工厂方法,我们可以有效地将异步行为封装在可管理的同步API中。这种方法保持了Inversify容器的同步和预测性质,同时提供了异步操作的灵活性。通过上述例子,我们可以看到整个过程中如何使用Inversify来管理那些需要异步初始化的依赖项,并确保系统的整体架构保持清晰和一致。
问题答案 12026年7月4日 18:07

如何在TypeORM中左联接JSON函数

在TypeORM中进行左联接JSON函数的操作,通常涉及到两个主要的步骤:设置实体关系和使用查询构建器执行联接。这里,我将详细介绍如何操作,并提供一个具体的例子来更好地说明过程。步骤1: 设置实体关系首先确保你的实体之间的关系是正确配置的。例如,假设有两个实体,一个是,另一个是,用户和资料是一对一关系:在这个例子中, 实体有一个列,该列的类型为 JSON。步骤2: 使用查询构建器执行左联接接下来,使用TypeORM的查询构建器来执行左联接,并访问 JSON 列。这里的关键是使用正确的表达式来处理 JSON 数据。假设我们想要获取所有用户及其详细资料中的某个特定信息,例如居住城市:在这个查询中,我们使用 来连接 实体,并在选择时使用了 PostgreSQL 的 JSON 操作符 来访问 JSON 对象中的城市信息。注意,这里我们使用了 方法来处理原生结果,因为结果包含了从 JSON 列中解析出来的数据。总结通过以上步骤,你可以在TypeORM中使用左联接来操作和访问关联实体中的 JSON 数据。在实际应用中,这种方法对于处理具有 JSON 属性的复杂数据模型特别有用。希望这个例子可以帮助你更好地理解如何在 TypeORM 中操作 JSON 函数和实现高级查询。
问题答案 12026年7月4日 18:07

如何在TypeORM postgres中使用leftJoinAndSelect查询?

在TypeORM中,使用方法可以方便地执行联接查询,并且选择联接表中的特定字段。这在处理数据库中的关系数据时非常有用,尤其是想要在单个查询中从多个表中检索数据时。基本用法假设我们有两个实体: 和 ,每个用户可以有多张照片。我们希望查询用户以及他们的所有照片。在TypeORM中,我们可以使用来实现这一点。这里是如何使用的示例代码:在上面的代码中,是实体中定义的与实体相关的属性名称。是我们为联接表指定的别名,我们可以用它来选择或条件过滤特定的字段。进一步选择字段如果你不需要表的所有字段,你可以指定需要选择的具体字段:这将只检索的和字段以及的字段。带条件的查询你还可以在查询中添加条件,比如只获取那些已经验证的用户的照片:这里的方法添加了一个条件,只选择那些属性为的用户。使用leftJoinAndMap如果你需要更复杂的操作,比如自定义返回的结构或者联合多个字段,你可以使用方法。这个方法允许你映射选定的结果到一个新的对象或实体属性中。这个例子中,我们选择了作为个人资料照片(字段为)的那些照片。总结是TypeORM中一个非常强大的工具,它让管理和查询关系型数据变得简单。它通过允许你在一个查询中联接并选择来自不同表的数据,从而优化了数据检索过程,并减少了需要写的代码量和潜在的错误。
问题答案 12026年7月4日 18:07

如何使用 Typeorm 从 Postgres 数据库返回 ROW_NUMBER

在使用Typeorm查询Postgres数据库时,我们可以通过原生的SQL查询来实现使用函数。在这个函数中,我们通常需要一个窗口函数,其中基于一定的排序来分配一个唯一的序列号给每一行。假设您有一个名为的表,并且您想要基于用户的注册日期来获取每个用户的行号。下面是如何使用Typeorm来实现:首先,您需要确保您的Typeorm连接已成功设置并且连接到您的Postgres数据库。接下来,我们可以使用来构建一个查询:这个例子中,我们使用了窗口函数,通过子句指定了排序的规则(这里是根据降序排序)。会为每一行分配一个唯一的连续整数,从1开始。注意点:使用原生SQL查询时,确保您对输入进行适当的验证和清理,以防止SQL注入攻击。在生产环境中,还需要考虑数据库的性能和优化查询。这种方式允许您尽量利用Postgres数据库的功能,同时在Typeorm中实现复杂的查询操作。
问题答案 12026年7月4日 18:07

如何在NestJS服务器上动态更改TypeORM配置?

在NestJS中动态更改TypeORM的配置通常涉及到几个步骤,主要包括使用自定义的服务来动态创建数据库连接。下面我将详细介绍如何实现这一功能。步骤 1: 创建一个动态数据库配置服务首先,我们需要创建一个服务,该服务负责基于动态数据(例如,来自API请求或环境变量)生成数据库配置。步骤 2: 动态连接数据库在中,我们通常会使用静态方法来初始化数据库连接。不过,为了实现动态配置,我们将使用方法,并利用上面创建的。步骤 3: 测试更改现在,每次应用启动或需要重新连接数据库时,将基于当前环境变量或其他动态数据源生成新的数据库配置。这使得在运行时根据需要调整数据库配置变得可能。例子假设我们需要根据用户的选择动态连接到不同的数据库。我们可以扩展,使其接受用户输入,并据此生成配置:在这个例子中, 是一个假设的服务,用于从某个API请求中获取用户数据。这种方式可以让每个用户根据自己的需求连接到特定的数据库实例。这在多租户应用中特别有用。
问题答案 12026年7月4日 18:07

如何在Typeorm中仅从联接实体中选择单个/多个字段

在Typeorm中,如果您想从联接的实体中选择单个或多个字段,您可以使用QueryBuilder来构建一个更加灵活的SQL查询。这样做可以帮助您精确地控制查询过程中的数据选择和传输。我将通过一个具体的例子来展示如何实现这一点。假设我们有两个实体: 和 ,它们之间是一对一的关系。我们现在的目标是查询每个用户的用户名及其对应的电子邮件,这里的电子邮件存储在实体中。首先,我们需要确保我们的实体之间的关系是正确设置的。例如, 实体可能如下所示:而 实体可能是这样的:现在,为了仅从实体中选择字段,我们可以使用TypeORM的QueryBuilder来构建以下查询:在这个查询中:是实体的别名。表示与实体进行左外连接,并为之设置别名。用于指定我们只想从这次查询中获取和字段。则执行查询并返回多个结果。这种方法使得数据加载更加高效,因为它只选择了必要的字段,减少了数据传输量。此外,使用QueryBuilder可以灵活地调整查询,以适应更复杂的业务需求。
问题答案 12026年7月4日 18:07

如何使用 express 和 typeorm 正确更新实体数据

要在Express路由处理函数中实现更新逻辑。一个典型的更新实体的API可能是一个PUT或PATCH请求。例如,我们要更新用户信息,可以设置如下路由:3. 实现更新逻辑在 函数中,我们将使用TypeORM的功能来查找、验证并更新实体。首先,我们需要根据ID查找现有用户。4. 数据验证确保更新的数据符合业务规则和数据完整性要求。TypeORM可以与class-validator库配合使用来自动化验证过程。5. 错误处理在整个处理过程中,错误处理是很重要的一部分。在Express中,应确保捕获并恰当地响应各种错误情况,如找不到用户、验证失败等。6. 测试在部署代码之前,应该进行充分的测试,包括单元测试和集成测试,确保API按预期工作。总结上述步骤展示了如何在使用Express和TypeORM的环境下更新一个实体,包括准备路由、实现更新逻辑、进行数据验证和错误处理。在实际应用中,代码需要根据具体的实体属性和业务需求进行调整和优化。
问题答案 12026年7月4日 18:07

如何使用TypeORM在React Native中处理SQLite迁移?

在React Native项目中使用TypeORM处理SQLite的迁移包含几个关键步骤。我将详细解释每个步骤,并提供相应的代码示例或操作方式。步骤 1: 安装和配置TypeORM首先,确保你的React Native项目中已经安装了和。TypeORM 依赖于这个库来处理SQLite数据库。接下来,你需要在项目中配置TypeORM。这通常在一个专门的数据库配置文件中完成。例如,你可以创建一个文件,用于初始化数据库连接。步骤 2: 创建迁移在TypeORM中,迁移是用来管理数据库结构变化的脚本。你可以手动创建迁移文件或者使用TypeORM CLI生成它们。为了生成一个迁移,你可以运行以下命令:这将在指定的迁移文件夹中创建一个新的迁移文件。你需要编辑这个文件,定义需要进行的数据库结构变更。步骤 3: 执行迁移一旦你的迁移文件准备好了,你可以在应用启动时调用迁移来更新数据库结构。步骤 4: 迁移回滚(可选)如果需要的话,TypeORM也支持迁移回滚,这可以通过调用方法实现。总结使用TypeORM处理SQLite数据库迁移,需要先设置好TypeORM和相关依赖,编写迁移脚本,然后在适当的时机执行这些迁移。这种方式可以帮助你有效地管理React Native应用中的数据库结构变化。