jimmer icon indicating copy to clipboard operation
jimmer copied to clipboard

[Feature Request] - Let ManyToManyView support recursion

Open zzxzz12345 opened this issue 1 year ago • 8 comments

Reason

目前erp产品里有这样一种关系 Product 产品,和BOM呈一对一关系 BOM,维护Product产品的关联关系以及product的相对数量 这两个表都是基础表

create table if not exists product
(
    id           bigint primary key generated by default as identity,
    product_type varchar(64)         not null,
    product_no   varchar(64)         not null,
    name         varchar(128)        not null,
    constraint product_no_uk unique (product_no)
);

create table if not exists bom
(
    id                   bigint primary key generated by default as identity,
    parent_product_id    bigint,
    component_product_id bigint                       not null,
    nums                 double precision not null,
    unique (parent_product_id, component_product_id),
    foreign key (parent_product_id) references product (id),
    foreign key (component_product_id) references product (id)
);

如果想做bom的递归查询,就需要有这样的一组结构

@Entity
@Table(name = "product")
interface Product {

    val id: Long

    val name: String

    @ManyToMany
    @JoinTable(
        name = "bom",
        joinColumnName = "parent_product_id",
        inverseJoinColumnName = "component_product_id"
    )
    val children: List<Product>
}

@Entity
@Table(name = "bom")
interface BOM{
    val id:Long
    
    @ManyToOne
    @Key
    val parentProduct: Product?

    @OneToOne
    @Key
    val componentProduct: Product

    val nums: Double
}

但是这种情况下如果bom上也声明了@Table 就会出现报错

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.babyfish.jimmer.sql.kt.KSqlClient]: Factory method 'kotlinSqlClient' threw exception with message: Illegal entity manager, the table "BOM" is shared by both  "BOM" and "MiddleTable(Product.children)"

Description

虽然目前通过创建一个view绕过去了,但是理论上一个表能同时为中间表和基础表

Existing solutions

No response

zzxzz12345 avatar Feb 21 '25 01:02 zzxzz12345

是否考虑使用 @ManyToManyView

TrueNine avatar Feb 22 '25 11:02 TrueNine

是否考虑使用 @ManyToManyView

我一直是当让ManyToManyView支持递归来理解的

babyfish-ct avatar Feb 22 '25 13:02 babyfish-ct

是否考虑使用 @ManyToManyView

我一直是当让ManyToManyView支持递归来理解的

无论如何,不可能一表多映的。因为Jimmer是支持【触发器】的,任何binlog事件推送归来,都应该只有一种解释,而不是无穷钟解释,这个异常也防御这种问题。

babyfish-ct avatar Feb 22 '25 13:02 babyfish-ct

我不知道题主 @zzxzz12345 的问题和我想的是否一样:如何使用 jimmer 实现以某个 Product 为根,递归查询所有子产品的物料清单?

如果 @ManyToManyView 支持递归查询子产品,那我们该如何获取这些子产品对应的 BOM 的 nums 属性呢?我感觉 Product 与 BOM 似乎只能是 “多对一” 和 “一对多”关系,无法直接为每个子产品获取对应的上下文的 BOM 中的 nums 属性。

所以,我的一个想法是,先通过 Product 和 BOM 的一对多关联查询 BOM 获取子产品和 nums 属性,然后继续通过一对多关联查询 BOM...,变成了递归查询。

让我用 Fetcher 来描述一下(伪代码):

ProductFetcher.$
    .name()
    .bomList(// Product 与 BOM “一对多” mappedBy = "parentProduct"
        BOMFetcher.$
            .nums()
            // BOM 与 Product “多对一”
            .componentProduct(ctx ->
                ctx.recursiveRoot()
            )
    )

照这样分析,似乎这个问题的关键是:如何实现这种通过间接的自关联而不是直接的自关联属性定义的递归查询

如果我的理解有误,希望能得到指教🙏🏻,谢谢

runnableAir avatar Feb 23 '25 18:02 runnableAir

是否考虑使用 @ManyToManyView

使用@ManyToManyView看上去不会生成children*,就没法做递归查询

Image Image

zzxzz12345 avatar Feb 24 '25 01:02 zzxzz12345

我不知道题主 @zzxzz12345 的问题和我想的是否一样:如何使用 jimmer 实现以某个 Product 为根,递归查询所有子产品的物料清单?

如果 @ManyToManyView 支持递归查询子产品,那我们该如何获取这些子产品对应的 BOM 的 nums 属性呢?我感觉 Product 与 BOM 似乎只能是 “多对一” 和 “一对多”关系,无法直接为每个子产品获取对应的上下文的 BOM 中的 nums 属性。

所以,我的一个想法是,先通过 Product 和 BOM 的一对多关联查询 BOM 获取子产品和 nums 属性,然后继续通过一对多关联查询 BOM...,变成了递归查询。

让我用 Fetcher 来描述一下(伪代码):

ProductFetcher.$ .name() .bomList(// Product 与 BOM “一对多” mappedBy = "parentProduct" BOMFetcher.$ .nums() // BOM 与 Product “多对一” .componentProduct(ctx -> ctx.recursiveRoot() ) ) 照这样分析,似乎这个问题的关键是:如何实现这种通过间接的自关联而不是直接的自关联属性定义的递归查询

如果我的理解有误,希望能得到指教🙏🏻,谢谢

不需要直接从product上获取nums属性,因为product本身还可以在声明一个one to one的结构来支持,但是目前看上只有ManyToMany和OneToMany能生成递归结构的api,ManyToManyView似乎不会触发

create view opta.product_relations as
(
select parent_product_id, component_product_id
from opta.bom);
Image Image Image

zzxzz12345 avatar Feb 24 '25 01:02 zzxzz12345

@zzxzz12345 谢谢你的回复,目前 @ManyToManyView 确实是不支持递归的。不过,我不太理解的是 BOM 实体的关联属性 componentProduct 为什么是一对一,我的理解是:Product 可以作为 componentProduct 与多个 parentProduct 产生关联,所以 BOM 的关联属性 componentProduct 是多对一,而不是一对一。

除此之外,如果使用 @ManyToManyView 注解,其属性 deeperProp 应该为 componentProduct,但 Jimmer 不允许 deeperPropprop 之间是一对一关联,这在编译时没有问题,但在运行时会报错并提示用户应该为 deeperProp 对应的关联属性声明 @ManyToOne

runnableAir avatar Feb 24 '25 07:02 runnableAir

@zzxzz12345 谢谢你的回复,目前 @ManyToManyView 确实是不支持递归的。不过,我不太理解的是 BOM 实体的关联属性 componentProduct 为什么是一对一,我的理解是:Product 可以作为 componentProduct 与多个 parentProduct 产生关联,所以 BOM 的关联属性 componentProduct 是多对一,而不是一对一。

除此之外,如果使用 @ManyToManyView 注解,其属性 deeperProp 应该为 componentProduct,但 Jimmer 不允许 deeperPropprop 之间是一对一关联,这在编译时没有问题,但在运行时会报错并提示用户应该为 deeperProp 对应的关联属性声明 @ManyToOne

这里是临时代码写的有点问题,主要还是如果不支持递归就需要自己去写递归遍历数据了

zzxzz12345 avatar Feb 24 '25 08:02 zzxzz12345