当前位置: 首页 > 知识库问答 >
问题:

删除自定义代码生成器中的外键

那谦
2023-03-14

我正在编写一个自定义代码生成器,其中每个主数据表都有自己的java枚举。我使用JOOQ配置中的forcedType设置将表的每次使用映射到枚举,这很好。我正在努力删除不必要的生成类,如POJO、DAO和Records,并提出了2个选项。

  1. 从代码生成中排除表

使用

这个想法是检查每一个相关的生成方法,如果是应该转换成枚举的表,就跳过这个表。这很适合

  • 具有生成 POJO 的 POJO
  • 具有生成AO的可观性发光二极管
  • 带有生成记录
  • 的记录

为了防止生成表,我从数据库对象中删除了表(令我惊讶的是,返回的列表是可变的)。

protected void generateSchema(SchemaDefinition schema) {
    //enum generation logic
    List<TableDefinition> tableDefinitions = db.getTables(schema);
    TableDefinition toRemove = db.getTable(schema, "table_name_which_is_an_enum_in_java", true);
    boolean removed = tableDefinitions.remove(toRemove);
    if(removed) {
        log.info("Removed table {} from code generation", toRemove);
    }
}

但我很难消除外键关系。当我重写 printForeignKey 并且仅在外键引用的表不是应转换为枚举的表时才调用父方法时,则生成的 Keys 类不会生成外键,但它仍然试图导入不存在的记录。

@Override
protected void printForeignKey(JavaWriter out, int foreignKeyCounter, ForeignKeyDefinition foreignKey, boolean distributeForeignKey) {
    if(foreignKey.getReferencedTable().getName().equalsIgnoreCase("table_name_which_is_an_enum_in_java")) {
        return;
    }
    super.printForeignKey(out, foreignKeyCounter, foreignKey, distributeForeignKey);
}

另一个问题是外键所在的表。正在创建引用方法和带有另一种方法的瞬态字段。这只是一个例子:

//class StammdatenZustand and Keys.FK_SPIELZEUG_STAMMDATEN_ZUSTAND do not exist anymore
@Override
@Nonnull
public List<ForeignKey<SpielzeugRecord, ?>> getReferences() {
    return Arrays.asList(Keys.FK_SPIELZEUG_STAMMDATEN_ZUSTAND);
}

private transient StammdatenZustand _stammdatenZustand; 

public StammdatenZustand stammdatenZustand() {
    if (_stammdatenZustand == null)
        _stammdatenZustand = new StammdatenZustand(this, Keys.FK_SPIELZEUG_STAMMDATEN_ZUSTAND);

    return _stammdatenZustand;
}

我深入研究了JavaGenerator的源代码,发现了造成麻烦的代码,但我不知道如何找到解决方法。最简单的解决方案是能够修改用tableDefinition.getForeignKeys(tableWhichWillBeAnEnum)返回的列表,然后调用Clear(),但遗憾的是这次列表是不可变的。

编辑:我目前的结果如下:

这是我想转换为枚举的表(表名为“stammdaten_zustand”)

使用以下数据

这是引用pk的表(表名为“spielzeug”)

我的自定义生成器为第一个表生成以下枚举

public enum StammdatenZustandEnum {
    GUT("Das Produkt ist fast neuwertig.", 1, false),
    MITTEL("Einige Gebrauchsspuren sind vorhanden, die vermutlich einen Einfluss auf den Preis haben.", 2, true),
    SCHLECHT("Das Produkt ist in einem schlechten Zustand. Verkaufen wird sich kaum lohnen.", 3, true),
    UNBEKANNT("Zustand des Produkts ist nicht bekannt.", 4, true);

    private final String beschreibung;
    private final Integer testInt;
    private final boolean testBool;

    private StammdatenZustandEnum(String beschreibung,Integer testInt,boolean testBool) {
        this.beschreibung = beschreibung;
        this.testInt = testInt;
        this.testBool = testBool;
    }

    public String getBeschreibung() {
        return this.beschreibung;
    }

    public Integer getTestInt() {
        return this.testInt;
    }

    public boolean isTestBool() {
        return this.testBool;
    }

}

在舞会上。xml,我还使用转换器将所有内容映射到生成的枚举。

<forcedType>
    <userType>model.db.jooq.StammdatenZustandEnum</userType>
    <enumConverter>true</enumConverter>
    <includeExpression>.*\.Zustand_Kuerzel</includeExpression>
    <includeTypes>.*</includeTypes>
</forcedType>

例如,生成的POJO“Spielzeug”被正确映射。

public class Spielzeug implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer               produktId;
    private String                produktname;
    private Double                kaufpreis;
    private Boolean               verkaeuflich;
    private StammdatenZustandEnum zustandKuerzel; // mapped to enum
...

这意味着如果我正确理解你的意思,我已经按照你的建议做了?

但是,为“Spielzeug”生成的表仍会生成以下片段

@Override
@Nonnull
public List<ForeignKey<SpielzeugRecord, ?>> getReferences() {
    return Arrays.asList(Keys.FK_SPIELZEUG_STAMMDATEN_ZUSTAND);
}

private transient StammdatenZustand _stammdatenZustand;

public StammdatenZustand stammdatenZustand() {
    if (_stammdatenZustand == null)
        _stammdatenZustand = new StammdatenZustand(this, Keys.FK_SPIELZEUG_STAMMDATEN_ZUSTAND);

    return _stammdatenZustand;
}

并且<code>Keys</code>类尝试生成外键

    public static final ForeignKey<SpielzeugRecord, StammdatenZustandRecord> FK_SPIELZEUG_STAMMDATEN_ZUSTAND = Internal.createForeignKey(Spielzeug.SPIELZEUG, DSL.name("FK_spielzeug_stammdaten_zustand"), new TableField[] { Spielzeug.SPIELZEUG.ZUSTAND_KUERZEL }, Keys.KEY_STAMMDATEN_ZUSTAND_PRIMARY, new TableField[] { StammdatenZustand.STAMMDATEN_ZUSTAND.KUERZEL }, true);

但是这两个代码片段都会抛出错误,因为“StammdatenZustand”POJO和Record不存在。

附带说明:一般来说,映射是有效的,当我离开生成的Dao、POJO和Record时没有错误。在这种情况下,我只需要告诉最终用户基本上忽略这些类,并支持生成的枚举。


共有1个答案

文寒
2023-03-14
匿名用户

有趣的是,jOOQ 1. x:#123中曾经有过这样的功能。

它在jOOQ 3.0中再次被删除,因为它过于复杂(正如您自己所注意到的,通过您自己的实现尝试),并导致大量代码生成错误,同时没有回答希望该功能更加强大的实际用例:#1740

为了回答您的具体问题:与其尝试删除有关此类查找表的元数据,为什么不直接在外键和主键上使用Converter来引用枚举类型,而不是例如整数值。您仍然可以在生成的代码中保留查找表,但是一旦您替换了类型,您就不再需要加入。

当然,您仍然可以从查找表中的实际值生成枚举。

但是这里有一个“革命性”的想法,可以帮助你非常简单地解决这个问题。不如定义一个自然键,并从所有外键中引用它。

这样,您仍然可以在列上使用< code>Converter将列绑定到您的枚举类型(就像我之前描述的那样),从而永远避免连接到查找表,即使是在普通的SQL查询中,因为所有外键都已经引用了有趣的查找键。请注意,这根本不违反规范化原则!它只是删除无用的代理键。

例如:

而不是。。。

CREATE TABLE currency (
  id BIGINT NOT NULL PRIMARY KEY,
  iso_code CHAR(3) NOT NULL,
  description TEXT NOT NULL
);

CREATE TABLE transactions (
  id BIGINT NOT NULL PRIMARY KEY,
  amount NUMERIC NOT NULL,
  currency_id NOT NULL REFERENCES currency
)

…这样做

CREATE TABLE currency (
  iso_code CHAR(3) NOT NULL PRIMARY KEY,
  description TEXT NOT NULL
);

CREATE TABLE transactions (
  id BIGINT NOT NULL PRIMARY KEY,
  amount NUMERIC NOT NULL,
  currency_iso_code NOT NULL REFERENCES currency
)

以下是一些关于这个主题的博客文章:

  • 关系表中无用的代理键的成本
  • 如果性能对您来说真的很重要,请对过度使用代理键说“不”
  • 通过偶尔选择自然键而不是代理键来加快 SQL 速度

 类似资料:
  • 我们有一个用例,在这个用例中,我们定义了许多不同的RPC。我们使用google的protobuf java生成了一个基于java的grpc存根代码 要转换为like 在java生成的代码中,每个原始服务应该有2个服务。我们只希望最终java生成的代码具有2个服务,解析器可能会/可能不会更新原始服务。原型文件。 当前protoc是否可以进行这种定制?我们可以扩展插件并编写我们的插件吗-

  • 我正在尝试从多个 WSDL 文件生成 Axis2 (v1.6.1) Web 服务客户端代码,以便我可以在单个项目中拥有多个 Web 服务客户端。我正在使用 Ant 通过代码生成任务生成源代码。 因此,我需要使用自定义命名空间来打包映射,以使每个 Web 服务客户端的代码与其他客户端的代码分开,以避免冲突。 下面是一个示例 WSDL 文件中的命名空间: 我已经设法将生成代码的主体放入适当的包中。我已

  • 我有一个自定义文件,其中包含我所有图像的路径及其标签,我在一个数据框中加载使用: MyIndex有两列感兴趣的ImagePath和ClassName 接下来我做一些训练测试拆分和编码输出标签为: 我面临的问题是一次性加载的数据太大,无法放入当前的机器内存,因此我无法处理完整的数据集。 我曾尝试使用datagenerator,但不想遵循它遵循的目录约定,也无法消除增强部分。 问题是,是否有一种方法可

  • 这类似于这个问题,但我们用Gradle代替。假设我们在构建脚本中直接使用jOOQ的代码生成,正如文档中所描述的那样。 有两个问题。首先,我们将向生成器配置中添加一个新的策略,这应该很简单: 然而,如果我们打印得到的XML配置,名称元素就会被神秘地省略(也会被悄悄地省略): 其次,可以使用什么机制来预编译生成器策略类()并使其在构建脚本的类路径中可用?这里有一个可能的问题:策略代码依赖于jOOQ的c

  • 是否有方法重写build方法返回SampleClass以避免执行build.build?

  • 使用jOOQ,我可能希望将jOOQ代码生成器与Maven和自定义生成器策略结合使用。看起来这可以这样做(省略不相关的部分): 上面的配置描述了问题。jOOQ的代码生成器挂钩到Maven生命周期的生成目标,它发生在生命周期的编译目标之前。但是,对于代码生成,它需要一个预编译的自定义策略类,否则我会得到一个。如何使用Maven解决这个问题?我可以在执行目标之前编译单个类吗?