为什么使用 JPA 而不是使用 JDBC 编写 SQL 查询?

我已经阅读了几篇关于什么是 JPA (Java Persistent API)以及支持它的供应商的文章(DataNucleus,JBossHibernate 等)

我没有使用 ORM (对象关系映射)的经验。

到目前为止,我所做的就是使用 DTO 和 DAO 编写我自己的 Database 类。到目前为止,我很高兴我有什么,但想知道 为什么人们使用包含 SQL 的 JPA over Java 文件

对我来说,我觉得编写 DAO 类可以像下面这样。

public class DAOUsers {
public void insertNewUser(DTO DtoUser) {
String query = "INSERT INTO users(username, address) " +
"VALUES(DtoUser.username , DtoUser.address)";
Executor.run(query);
}


}

我了解到 JPA 使用 JPQL,Java 持久查询语言,并且它对实体对象进行操作 而不是直接使用 db 表。

我的理解是这里的实体对象与我的 DTO 对象相同(有点像 bean?)

但是无论如何. . 相对于在我的文件中编写纯 SQL,JPA 真正的好处是什么? 似乎使用 JPA 所需的注释并使 SQL 不可读对我来说并不真正有吸引力。

请让我知道,如果你需要更多的澄清,我是新的这个话题,想听到一些意见。

74972 次浏览

如果操作正确,您可以使用 Hibernate 等 JPA 实现将 SQL 查询直接映射到 java 对象。我最近做了一个项目,其中我有一个 POJO 和3或4个注释,还有一些设置代码,可以将一个存储过程直接映射到一个对象列表(POJO 类类型)。这对我来说是 JPA 力量的一部分。

如果您像使用 SQL + JDBC 那样使用它,那么我不知道它有什么优点。

有一篇不错的文章 给你介绍了 JPA 的优点。

希望这个能帮上忙。

众所周知,对象是我们生活中最重要的事情之一,在编程中,对象是简化任何问题的非常简单的方法... ..。 如果对象在那里是可用的,那么为什么我们使用整个事物而不是那个事物的一小部分意味着对象... 。

  • 如果您手工编写 SQL 语句 在您的企业应用程序中,您 正在花费大量的 你的发展时间更新及 保持你的持久层。

在持久性方面,= = > 不再需要用于结果集或数据处理的 JDBC API。 = = > 它有助于减少代码行数, 我不知道,我不知道,我不知道 它将我们的应用程序从底层 SQL 数据库和 SQL 方言中抽象出来。切换到其他 SQL 数据库需要对 Hibernate 配置文件进行少量更改(编写 once/run-where)。

与在文件中编写纯 SQL 相比,JPA 有什么真正的好处?

以下是一些好处:

  • JPA 允许您避免使用特定于数据库的 SQL 方言编写 DDL。相反,您可以用 XML 编写“映射”,或者使用 Java 注释。

  • JPA 允许您避免使用特定于数据库的 SQL 方言编写 DML。

  • JPA 允许您在不使用任何 DML 语言的情况下加载和保存 Java 对象和图形。

  • 需要执行查询时,JPQL 允许您使用 Java 实体而不是(本机) SQL 表和列来表示查询。

一般来说,与 JDBC + SQL + 手写映射相比,JPA 更简单、更干净、更省力。数据模型越复杂,就越有益。

但是,如果性能是 重写关注的问题,那么 JPA 确实倾向于在应用程序和数据库之间添加层。如果您的应用程序要求您大量手工优化本机数据库查询和模式以最大限度地提高性能,那么 JPA 可能不是一个很好的选择。

如果您在同一个应用程序中同时使用 Java、 JDBC 和 SQL,而不是让 ORM 处理混乱的细节,那么 JPA 可能也不适合您。(但如果你是,你可能是少数... ...)

为什么使用 JPA 而不是直接使用 在 Java 文件上编写 SQL 查询(即。 直接传送到 JDBC) ?

某些项目要求工程师更多地关注对象模型,而不是用于访问数据存储的实际 SQL 查询。这个问题实际上可以解释为

为什么要使用 ORM 框架?

在不同的情境下可以有不同的答案。

大多数项目可以从拥有域模型中获益,持久性是第二个问题。使用 JPA (实现)或大多数其他 ORM 框架,可以将所有实体,即数据库中的表,建模为 Java 中的类。此外,还可以将行为嵌入到这些类中,从而实现行为丰富的域模型。此模型中的实体可以有多种用途,包括替换 DTO 以跨层传输数据。

也就是说,有些地方 ORM 框架可能并不直接适合这个问题,特别是当数据模型已经建立,或者当一个人使用遗留系统时,将数据库表映射到 Java 类是一个非常重要的练习。在某些情况下,如果需要彻底调优 ORM 框架生成的 SQL,那么 ORM 框架通常不适合。

相关问题

  1. JavaEEArchitecture ——当使用像 JPA2这样的 ORM 时,DAO 仍然被推荐吗?
  2. 使用 ORM 还是普通 SQL?
  3. ORM 与手工编码数据访问层

此外,正如你所知道的 Java 的口号: “写一次,到处跑”

还可以使用 JPQL 在每个数据库(DB2、 Oracle 等)上执行 JPQL 查询

虽然这是一个老问题,但我觉得它值得一个新的答案。我是 JPA 的后期使用者,已经断断续续地使用了好几年了,虽然我曾经被一个新应用程序的简单性所打动,但是对于正确使用 JPA 所需要的性能、复杂性和学习曲线,我已经完全无动于衷了。这个帖子里的答案实际上加强了我的立场。

首先,@vineet 建议“实体可以有多种用途”... ... 这是我在生产中看到的,我认为是受到 ORM 的鼓励。这就是凝聚力和单一责任原则。根据我的经验,向数据库实体添加行为是自找麻烦。我知道这些是因为我曾经做过,并且后悔了一辈子。

其次,对于 JPA 的复杂性,有一些简单的替代方案,它们提供了在 RDBMS 中使用类的能力,而没有 ORM 试图(未成功地)解决的不匹配所造成的所有繁重(和性能问题)。我们已经在一个拥有超过1,000个表的应用程序中使用非 JPA 关系类映射工具十年了,我们只是没有看到 JPA 如何比更直接地访问数据库更好。JPA 模糊了数据库的功能,同时增加了类模型的开销(以注释和 JQL 的形式) ... ... 难道它不应该以另一种方式工作吗?

@ 水暗示了许多在理论上正确但在现实中不切实际的事情。例如,在切换了三次后端数据库之后,我可以向读者保证,不存在什么配置调整之类的事情,然后就可以完成了。我建议,如果您花费了大量时间来维护持久层,那么您的数据库模型正在发展,并且您将在 JPA 中执行相同或更多的工作。特别是当 JPA 中的非平凡查询需要使用 JQL 时!

几乎每个人都假装 JPA 开发人员不需要了解 SQL。我在实践中看到的是,现在我们必须学习 SQL还有JQL。显然我们不需要做 DDL-但是在任何重要的应用程序 当然中,您需要了解 DDL。Hibernate 甚至不推荐使用自动 DDL 生成。显然,我们不必做 DML,除非当我们调用本机查询时,它当然是不可移植的,会破坏缓存,并且有与 JDBC 相同的问题..。

最终,在一个结构合理的应用程序中,领域模型独立于业务逻辑,JPA 提供的功能很少,我发现这是一个非常高的学习曲线——因为领域模型实际上非常容易构建。我不会直接使用 JDBC,但是像 Apache DBUtils 这样的软件在 JDBC 之上提供了一个简单的层,可以将行映射到对象,只需要一点努力就可以提供 JPA 的大部分优点,而且不需要隐藏和开销。

自 JDBC 1.0以来,我一直在使用各种库(在 JDBC 之前使用 iODBC 和 ESQL)开发 Java 数据库应用程序,仅仅出于性能方面的原因,我已经完成了 JPA 的开发。但是,即使性能更好,学习曲线和不完整的抽象也会让我严重犹豫。JPA 很复杂,它试图隐藏细节,在我看来,开发人员实际上需要关心这些细节。作为一个例子,我们最近看到 hibernate 向数据库发出250个删除命令,其中一个命令就足够了。JPA 的本质使得这种错误很容易发生。

我不是在提倡 JDBC,我只是在提倡反对 JPA。不会或不能使用 SQL 的开发人员可能不应该编写关系型应用程序——就像我这样的开发人员不能使用矩阵代数来挽救我的生命一样,他们也应该编写3D 游戏。那些以使用 SQL 为生的开发人员应该对那些冬眠后发送到服务器的变形的 SQL 感到震惊,因为这样做是为了避免本来就不必要的往返。

  • JPA 非常适合于面向非性能的复杂应用程序。
  • JDBC 是性能是关键执行者的最佳选择。

JDBC 很冗长

例如,这是插入某些记录的最常用方法:

int postCount = 100;


try (PreparedStatement postStatement = connection.prepareStatement("""
INSERT INTO post (
id,
title
)
VALUES (
?,
?
)
"""
)) {
for (int i = 1; i <= postCount; i++) {
int index = 0;
        

postStatement.setLong(
++index,
i
);
postStatement.setString(
++index,
String.format(
"High-Performance Java Persistence, review no. %1$d",
i
)
);
        

postStatement.executeUpdate();
}
} catch (SQLException e) {
fail(e.getMessage());
}

JDBC 批处理需要更改数据访问代码

而且,当您意识到这样做并不能很好地执行时,因为您忘记使用批处理,您必须更改以前的实现,如下所示:

int postCount = 100;
int batchSize = 50;


try (PreparedStatement postStatement = connection.prepareStatement("""
INSERT INTO post (
id,
title
)
VALUES (
?,
?
)
"""
)) {
for (int i = 1; i <= postCount; i++) {
if (i % batchSize == 0) {
postStatement.executeBatch();
}
        

int index = 0;
        

postStatement.setLong(
++index,
i
);
postStatement.setString(
++index,
String.format(
"High-Performance Java Persistence, review no. %1$d",
i
)
);
        

postStatement.addBatch();
}
postStatement.executeBatch();
} catch (SQLException e) {
fail(e.getMessage());
}

JPA 和 Hibernate 替代方案

使用 JPA,一旦映射了实体:

@Entity
@Table(name = "post")
public class Post {


@Id
private Long id;


private String title;


public Long getId() {
return id;
}


public Post setId(Long id) {
this.id = id;
return this;
}


public String getTitle() {
return title;
}


public Post setTitle(String title) {
this.title = title;
return this;
}
}

并且,设置以下 Hibernate 配置属性:

<property name="hibernate.jdbc.batch_size" value="50"/>

下面是如何插入那些 post表记录:

for (long i = 1; i <= postCount; i++) {
entityManager.persist(
new Post()
.setId(i)
.setTitle(
String.format(
"High-Performance Java Persistence, review no. %1$d",
i
)
)
);
}

简单多了,对吧?

使用 JDBC 获取数据

对于 JDBC,执行 SQL 投影的方式如下:

int maxResults = 10;


List<Post> posts = new ArrayList<>();


try (PreparedStatement preparedStatement = connection.prepareStatement("""
SELECT
p.id AS id,
p.title AS title
FROM post p
ORDER BY p.id
LIMIT ?
"""
)) {
preparedStatement.setInt(1, maxResults);


try (ResultSet resultSet = preparedStatement.executeQuery()) {
while (resultSet.next()) {
int index = 0;
            

posts.add(
new Post()
.setId(resultSet.getLong(++index))
.setTitle(resultSet.getString(++index))
);
}
}


} catch (SQLException e) {
fail(e.getMessage());
}

这相当冗长,因为您必须将 ResultSet转换为您的应用程序正在使用的数据结构(例如,DTO、 JSON web response)。

使用 JPA 获取数据

使用 JPA,您可以像下面这样获取 Post记录的 List:

int maxResults = 10;


List<Post> posts = entityManager.createQuery("""
select p
from post p
order by p.id
""", Post.class)
.setMaxResults(maxResults)
.getResultList();

而且,它不仅编写起来更简单,而且可以在 Hibernate 支持的每个数据库上运行,因为分页语法是基于底层数据库方言改编的。

JPA 相对于 JDBC 的其他优势

  • 您可以获取实体或 DTO,甚至可以获取 分层父子 DTO 投影
  • 可以在不更改数据访问代码的情况下启用 JDBC 批处理。
  • 您可以支持乐观锁定。
  • 您有一个独立于底层数据库特定语法的悲观锁抽象,因此可以获取 READ 和 WRITE 锁甚至 跳锁
  • 您有一个独立于数据库的分页 API。
  • hibernate.query.in_clause_parameter_padding .
  • 您可以使用一个强一致性的缓存解决方案,该解决方案允许您卸载主节点,对于读写事务,该节点只能垂直调用。
  • 您内置了对通过 Hibernate Envers 进行审计日志记录的支持。
  • 您已经内置了对 多租户的支持。
  • 您可以使用 Hibernate 我不知道工具从实体映射生成初始模式脚本,您可以将其提供给自动模式迁移工具,如 飞行路线
  • 不仅可以自由地执行任何 原生 SQL 查询,而且可以使用 SqlResultSetMapping将 JDBC ResultSet转换为 JPA 实体或 DTO。

JPA 的缺点

使用 JPA 和 Hibernate 的缺点如下:

  • 虽然开始使用 JPA 非常容易,但是成为一名专家需要大量的时间投入,因为除了阅读它的手册之外,你还必须学习数据库系统是如何工作的,SQL 标准以及项目关系数据库使用的特定 SQL 风格。
  • 有一些不太直观的行为可能会让初学者感到惊讶,比如 冲洗操作顺序冲洗操作顺序
  • Criteria API 相当冗长,因此需要使用像 Codota这样的工具来更轻松地编写动态查询。

结论

Java 生态系统最伟大的事情之一是大量高质量的框架。如果 JPA 和 Hibernate 不适合您的用例,您可以使用以下任何框架:

  • MyBatis ,这是一个非常轻量级的 SQL 查询映射器框架。
  • QueryDSL ,允许动态构建 SQL、 JPA、 Lucene 和 MongoDB 查询。
  • JOOQ ,它为底层表、存储过程和函数提供了 Java 元模型,并允许您使用非常直观的 DSL 以类型安全的方式动态构建 SQL 查询。

虽然 JPA 带来了许多优势,但是如果 JPA 和 Hibernate 不能最好地满足您当前的应用程序需求,您还可以使用许多其他高质量的替代品。因此,现在,除非您正在开发一个数据访问框架,否则实际上并不需要使用普通的 JDBC。

在使用 JDBC 时,我们有一个映射层来将 DB 联接的表映射到业务对象。但是 JPA 以某种方式或其他方式迫使数据对象单独维护,这增加了维护和重构 DB 表的额外工作,使生活变得困难。

也同意 https://stackoverflow.com/a/57766861/3343801

从我的经验来看,学习 ORM 映射是值得的,特别是在微服务架构中,较小的数据库可以抵消一些错误。

然而,我讨厌的是 JPQL 和 HQL。我总是发现直接使用 SQL 查询更好。我们必须了解 SQL 才能实现 ORM 映射,所以为什么我们必须学习一种可怕的 ORM 映射混合体,而干净的 SQL 做得非常好,这总是超出我的能力范围!