ORM 谁可以动态生成脚本

Posted

技术标签:

【中文标题】ORM 谁可以动态生成脚本【英文标题】:ORM Who can generate script dynamically 【发布时间】:2019-08-26 09:24:21 【问题描述】:

所以基本上我正在检查休眠的工作,以及评估 ORMs JOOQ。 JPA 或多或少是所有基于 Java 的 ORM 实现的主要事实标准。

我并不是说它们不完美或不好,但我的问题是我在使用以下这些提供的各种选项时发现了许多问题和问题:

    休眠:

    一个。它现在已经过时了标准 API,所以现在我们必须遵循 JPA 方式, 在这种情况下,我不能对我的 bean pojo 没有特定构造函数的一个表的几列使用 multiselect() 或 Selection。

例如。我的 Bean/POJO 有将近 80 列

在一个地方我只需要 10 列,而在另一个地方我需要 Bean/POJO 映射到的表的 20 列。 现在在这种情况下,我必须创建两个单独的包含选择字段的 DTO,或者我必须在我的主映射 Bean/POJO 中创建两个不同的重载构造函数。

这是根据 JPA,没有办法只使用 getter setterof 字段来初始化我的 Bean/POJO。

这背后的想法是减少我们必须创建不必要的类。

另外,如果假设我有这样的情况,我必须从表中获取几乎 25 个不同的组合,那么就 JPA 而言,我必须在一个 Bean/POJO 或 25 个不同的 DTO (WTH) 中创建 25 个不同的构造函数。

b.当我的 Bean/Pojo 构造函数如下所示时,Hibernate 失败

MyBean(String name, String email)

如果我愿意,在选择时

query.multiselect(new Selection[]mappingBean.get("email"),mappingBean.get("name"));

它以错误的顺序初始化,这是我的类型地狱缺陷。 (当然开发者确实有责任,但仍然是错误的)

    搜索条件模式: 无论我们在哪里需要数据,我们都必须立即编写方法,或者我们必须编写 JPQL、HQL 或任何抽象脚本,只是为了隐藏 SQL

现在,如果我在一张表上有 25 种不同脚本的不同组合,我必须为这些脚本编写 25 种不同的方法,因为标准 API 无论是 Hibernate 还是 JPS 都具有较差且复杂的脚本构建系统。

如果 JOOQ,它的所有 java 都在脚本中解释。(WTH)

现在没有简单的 API 或 ORM 可以满足以下要求 我发现如果你能帮上忙,我会很满足你。

    我不想使用任何抽象语言的 HQL JPQL 而不是像 SQL 这样的

我创建了一些代码,它作为对象传递给我的 API 应该能够执行此操作的方法。

    如果存在连接的情况(我必须说不是每个数据库都经过适当设计,以防意外更改突然发生),在这种情况下,我们必须对连接有一定的灵活性,每次都没有可能维护适当的关系(因为有管理类型的限制,他们专注于交付而不是可维护的系统)。

    如果有我必须编写的 SQL,我应该编写特定部分,例如 where 子句的一部分,或者只是选择部分

4.没有人能正确支持动态联合交叉等复杂脚本。

等等 清单很长,但如果你能帮我找到至少一个合适的可以帮助我...

编辑:

现在正在编辑 JOOQ 部分

我还没有探索过 JOOQ 的内部结构,它也使用 AST,但我的问题是它遵循的模式可以check here

如果我发现普通 JDBC 出了什么问题,如果我必须从数据库中以整数或字符串或单个数据类型的形式作为单个字段获取我的数据,那么我的 JDBC 是我必须的任何方式在 JDBC 中注意类型安全,我必须用 JOOQ DSL 来做。 至少我希望 Bean/POJO 的结果像 Hibernate Criteria API 提供或 JPA 提供的那样,不同之处在于它们也有滞后。 如果 JOOQ 有我不知道,因为正如我所看到的示例一样,在其教程中找到了 SQL 类型代码。

现在让我们来看另一个我有的例子(到目前为止,你可以帮助我找出我的一些问题,你可以通过 Hibernate 在这里找到我试图向其社区提出的问题,检查 here 和 @ 987654323@(感谢您并参考了这些API作为灵感),

例如。 JOOQ 的一个例子 [![Example From JOOQ][1]][1]

现在,如果我们观察,我必须编写 1 个 sql 或只编写一组列选择,我最终会使用它,但想象一下我必须在 N 个位置使用具有不同列集的同一张表,然后我必须编写 N 个 JOOQ DSL 的脚本(这里的代码是可互换的 JOOQ SCRIPT 检查图像我的意思是,你的 SQL 和代码看起来互相模仿),

所以我分析了以下几点:

    如果我必须编写 N 个不同的选择,那么我将编写一个类,它将所有选择包装在 N 个不同的方法中,并且无论何时我需要在我的业务逻辑层调用这些方法来获取数据(这是我在 HQL、JPQL 和 JOOQ DSL 中看到的缺陷(根据图片)。

在这里,我将通过创建 N 个方法来制造混乱,因为我必须弄清楚哦,对于 4 列我必须使用方法 X,对于 5 列我必须使用方法 Y,当你有团队时这会更糟100 名开发人员,因为每个人可能都没有努力寻找正确的方法,而是他/她在那里创建了自己,这将创建冗余和重复的代码。

我相信你也不想在你的业务逻辑层中调用像 DSL 这样的脚本,因为你只需要基于原因的东西,而“如何和从哪里”是 ORM 及其盟友的任务(这里我指的是使用 DAO 调用作为盟友或 ORM 来获取数据的 DAO 和工厂方法。

    现在另一种方法是,我将有一组单独的条件类,这些条件类与我的 Bean/POJO 松散耦合,完全与 ORM 分离,它只会遵循 ORM 的一些标准规则,因此它可以利用搜索条件在 DB 中查找数据并返回一个 Beans/POJO 列表。

因此,对于任何选择,我只需要编写一种方法,例如。我们在工厂类中将其称为 List findData(SearchCriteria sc) (M 将其称为工厂,因为它将具有来自 DAO 的方法和对象,并且使用 DAO 它将提供 Bean 列表,因此它成为利用 ORM 的数据库的对象生成器类。

现在此方法有责任根据标准为我提供所需的输出,在这种情况下,无论在我的业务层中,无论我需要一组特定的数据,我都可以创建搜索条件并将其传递给此方法,该方法将为我提供以下列表Beans/POJO。

因此,我可能会根据业务逻辑层不同部分的要求创建搜索条件,无论 N 次获取数据,但至少我确定获取数据的责任仅在我的一种方法上工厂类是我的业务层和我的 ORM 的 API 之间的一种分隔符(例如,你采取的那种),工厂事务将由 ORM 处理,而不是由用户处理,或者我说是开发人员自己。

希望你明白我的意思。

这些东西 hibernate 或 JPA 已经解决了一点,但还没有完全解决,因为它们的术语也是相同的,无论您需要在哪里使用标准构建器,在您的业务层中使用标准查询,这不是一个好的代码结构。

编辑 2:

卢卡斯,我明白你的意思了,我有以下几点

    查看下面的 JOOQ 代码:
public static ResultQuery<Record2<String, String>> actors(
    Function<Actor, Condition> where
) 
    return ctx.select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
              .from(ACTOR)
              .where(where.apply(ACTOR)));

这里的 ACTOR 看起来像实际的表名和 FIRST_NAME 作为它的列, 那么是否有任何标准我们必须遵循某些代码实践或者我必须自己设计它

像一个 Bean/POJO/DTO 例如 UserBean.java 可以保存从 DB 获取的数据,因为它是表 USER 的默认映射,我们可以使用 using 获取数据

ctx.select( *).from(UserBean.class)  //I m not sure how do * but will figgure out that separately

所以在这种情况下,理想情况下必须有标准映射器,例如在上述情况下 UserDAO.java 将具有静态最终字符串形式的字段,其中包含表的确切列名和表名。

这可以被工厂用来在更广泛的层面上调用,(它只是一个适当的代码结构的想法),

那么 JOOQ 是否列出了任何实践或模式建议? 我只是给出了我的解释。

    对于第 2 点,我没有考虑设计良好的数据库,因为我的数据库不在那些完善的关系和约束维护架构中,

它有很多表,这些表具有包含在两个表之间提供适当关系的列的公共数据,但没有在数据库级别应用任何外键关系。

现在在这种情况下,在 hibernate 中,Join 变得非常困难,我们必须借助包含跨表数据映射和 HQL 或普通 SQL 的 DTO 来处理。

我的想法是,如果我必须将两个在数据库级别没有任何适当关系的实体相互连接,是否有可能,JOOQ 是否有如下安排?

如果有UserBean和BankAccountBean,一个用户可以拥有多个银行账户,

所以对于下面的脚本

Select A.USER_ID, A.USER_NAME, B.BANK_NAME, B.BANK_ACCT_TYPE, B.BANK_ACCT_NUMBER 
FROM USER A LEFT JOIN USER_BANK_ACCOUNT B On A.USER_ID = B.USER_ID

现在在这种情况下,最好不要创建一个单独的全新 Bean 我的想法是,必须有 UserBankAcctJoinBean 应该能够处理这个数据获取(这里必须考虑动态选择)

现在,如果我们谈论 UserBankAcctJoinBean,它应该扩展 UserBean(因为在我的联接中,主表是 User)所以默认情况下它将继承 Users 的所有属性,并且它应该包含一个引用变量 UserBankAccountBean 因此属于 USER_BANK_ACCOUNT 的选择部分应通过反射在此引用上初始化

这里它与 Hibernate 不同,因为它确实允许拥有辅助表的列表对象,这不是基本思想,因为 hibernate 包含架构中表之间正确建立的关系。

JOOQ有这种或类似的方式吗?

    此外,如果 JOOQ 遵循 JPA 模式,它在动态选择的情况下如何在内部工作,因为正如我在动态选择的情况下探索 JPA 一样,它的规范说直接使用特定的构造函数初始化对象,这是最大的挑战,因为如果我想对多个不同的列使用 4-5 种不同的方式,然后我必须使用 4-5 个不同的重载构造函数,或者必须创建 4-5 个不同的 Bean/POJO/DTO(这一点在我之前的编辑中已经存在)李>

此外,在休眠源代码的情况下,它首先将数据从 ResultSet 加载到 Object[],然后使用字段的 setter 方法初始化 Bean/POJO/DTO,所以在这里如果您看到双重滞后,因为不是直接初始化对象通过调用字段的 getter setter,它首先将数据存储到 Object 数组,然后从该数组加载到 bean,最终每个 fetch 行迭代创建两次。

这真的很浪费资源,因为对于同一组数据重复两次相同的任务不是一种有效的方法,

那么 JOOQ 在内部是如何运作的呢?它是使用 getter setter 直接将数据初始化为 DTO,还是也可以像 Hibernate 一样工作?

【问题讨论】:

我可能会回答 jOOQ 部分,但首先,您介意解释一下 “如果 JOOQ 它的所有 java 都在脚本中解释。” 是什么意思吗? “在脚本中解释”是什么意思 我正在查看 JOOQ 作为 Hibernate 的附加功能,但发现了这个...啊...由于字数限制无法在此处输入,让我稍微编辑一下我的问题。 嘿 @Lukas Eder 检查发布我的 EDIT 我希望我想说的,你会明白的...... 这读起来像是漫无边际的咆哮,我不太清楚你在问什么。我感觉你在要求图书馆的建议(这是题外话),但我没有在你的文章中发现一个实际的问题。 @Mark Rotteveel 我的每一个字都只与问题有关,我的观点是在 ORM 中我必须在 Java 代码中编写 SQL 或类似 SQL 的表达式,理想情况下这必须是 ORM 而不是开发人员的责任,因为很少有我能理解的情况,但大多数情况下,在 JOOQ 中,它给出的结果就像 Simple JDBC 一样,而在 Hibernate 的情况下,它非常严格,我现在必须灵活地使用 String 自定义 SQL。除此之外,我没有中途可以结合标准 API 和 SQL 脚本。 【参考方案1】:

我会尽量回答你的 jOOQ 部分

预编辑 2

如果我发现普通 JDBC 出了什么问题,如果我必须从数据库中以整数或字符串或单个数据类型的形式作为单个字段获取我的数据,那么我的 JDBC 是我必须的任何方式在 JDBC 中注意类型安全,我必须用 JOOQ DSL 来做。至少我希望 Bean/POJO 的结果像 Hibernate Criteria API 提供或 JPA 提供的那样,不同之处在于它们也有滞后。如果 JOOQ 有我不知道,因为正如我所看到的示例一样,在其教程中找到了 SQL 类型代码。

jOOQ 可以将您的元组投影到任意“Beans/POJO”中。只需使用DefaultRecordMapper。一个例子:

class DTO 
  int a;
  int b;
  int c;

然后

List<DTO> dtos =
ctx.select(T.A, T.B)
   .from(T)
   .fetchInto(DTO.class);

如果我必须编写 N 个不同的选择,那么要么我将编写一个类,它将所有选择包装在 N 个不同的方法中,并且无论何时我需要在我的业务逻辑层调用这些方法来获取数据(这是我在 HQL、JPQL 和 JOOQ DSL 中看到的缺陷(根据图片)。

您没有发布任何图片,但根据我对您问题的理解,在我上面的示例中,我重用了 DTO 来获取两列而不是 3 列。无需为每个查询创建一个 DTO。当然,您可以按如下方式使用无类型记录:

Result<Record> result =
ctx.select(T.A, T.B)
   .from(T)
   .fetch();

然后

for (Record record : result)
    System.out.println(record.get(T.A) + ":" + record.get(T.B));

在这里,我将通过创建 N 个方法来制造混乱,因为我必须弄清楚哦,对于 4 列我必须使用方法 X,对于 5 列我必须使用方法 Y,当你有团队时这会更糟100 名开发人员,因为每个人可能都没有努力找到正确的方法,而是他/她在那里创建了自己,这将创建冗余和重复的代码。

仅仅因为许多 jOOQ 示例为了简单起见使用准静态 SQL 语法并不意味着您不能编写动态 SQL。事实上,每一个 jOOQ 语句都是动态的 SQL 语句。例如:

List<Field<?>> select = new ArrayList<>();
if (something)
    select.add(T.A);
if (somethingElse)
    select.add(T.B);
Result<?> result = ctx.select(select).from(T).fetch();

在此处进一步阅读:https://blog.jooq.org/2017/01/16/a-functional-programming-approach-to-dynamic-sql-with-jooq

因此,对于任何选择,我只需要编写一种方法,例如。我们在工厂类中将其称为 List findData(SearchCriteria sc)(M 将其称为工厂,因为它将具有来自 DAO 的方法和对象,并且使用 DAO 它将提供 Bean 列表,因此它成为利用 ORM 的数据库中的对象生成器类) .

您是否正在寻找 Spring Data?

希望你明白我的意思。

不是真的,但我希望你能得到我的:)

后期编辑 2

所以,您添加了大量其他问题。以下是我的答案:

这里的 ACTOR 看起来像实际的表名和 FIRST_NAME 作为它的列,所以有什么标准我们必须遵循某些代码实践或者我必须自己设计它

jOOQ 让您可以使用它提供的开箱即用的功能,或者您可以花几天时间根据自己的风格定制 jOOQ。如果您不知道这里的答案,只需使用 jOOQ 的默认值即可。他们选得很好。

//我不知道怎么做*,但会单独弄清楚

您可以将select() 列表留空,或致电selectFrom(),或使用DSL.asterisk()

那么 JOOQ 是否列出了任何实践或模式建议?

同样,jOOQ 不会根据您自己的风格品味来评判您,它可以让您随心所欲地做自己想做的事,而不会妨碍您。但是 jOOQ 有“明显的”默认值

现在在这种情况下,最好不要创建一个单独的全新 Bean 我的想法是,必须有 UserBankAcctJoinBean 应该能够处理此数据获取(这里必须考虑动态选择)

如果您认为这是一个非常好的和可扩展的想法,那就去做吧。您可以编写自己的 bean。或者只使用 jOOQ 的记录,它们与元组没有什么不同。

如果 JOOQ 遵循 JPA 模式

我不知道那是什么意思,也不知道你为什么认为 jOOQ 会那样做。

因为如果我想对多个不同的列使用 4-5 种不同的方式,那么我必须使用 4-5 个不同的重载构造函数,或者必须创建 4-5 个不同的 Bean/POJO/DTO(这一点已经在我的以前的编辑)

如果您担心这一点,请不要使用不可变的 POJO,将可变的 POJO 与 JavaBeans 样式的 setter 和 getter 一起使用,jOOQ 只会调用结果集中存在匹配列的那些。

【讨论】:

谢谢@Lukas Eder 你选对了,我的讨论部分是准确的,可以进一步检查我的 Edit 2: 部分,我得到了你解释的 JOOQ 部分,我需要进一步探索一些方法和标准,但我在 Edit 2: 中还有一个也是最后一个问题 很抱歉当时我正在添加该部分现在可以检查 @AtulSinghRathore:我认为您的其他问题并不新鲜,我已经回答过了。为免生疑问,我再次尝试回答。 @Luke Eder 感谢您的回复,我解决了所有疑问:)

以上是关于ORM 谁可以动态生成脚本的主要内容,如果未能解决你的问题,请参考以下文章

C 语言调用 pybind11 生成的动态链接库

SQLAlchemy 中的动态类创建

unity诡异的问题---使用脚本动态生成的物体无法用射线检测到,但是进入攻击范围后又可以被检测到

Java进阶学习第24天——动态代理与类加载器

按时间动态生成分区脚本

如何在 shell 脚本中动态生成新的变量名?