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

mas*_*san 51 java database orm jpa

我一直在阅读几篇文章是什么JPA (Java Persistent API)以及哪些供应商支持它(DataNucleus,JBoss Hibernate等)

我没有ORM(对象关系映射)的经验.

到目前为止我所做的是使用DTO和DAO编写自己的数据库类.到目前为止,我对我所拥有的内容感到高兴,但我想知道为什么人们使用JPA而不是包含SQL的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);
     }

}
Run Code Online (Sandbox Code Playgroud)

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

我的理解(纠正我,如果我错了)是这里的实体对象和我的DTO对象一样(有点像bean吗?)

但无论如何...... JPA真正有益于在我的文件中编写纯SQL吗?看起来像使用JPA所需的注释并使SQL不可读对我来说不是很有吸引力..

如果您需要更多说明,请告诉我,我是这个主题的新手,并希望听到一些意见.

Vin*_*lds 42

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

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

为什么要使用ORM框架?

在不同的环境中可以有不同的答案.

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

也就是说,有些地方的ORM框架可能不是直接适应问题的,特别是当数据模型已经建立时,或者正在使用遗留系统时,将数据库表映射到Java类是一项非常重要的工作.在某些情况下,如果需要绝对调整ORM框架生成的SQL,那么ORM框架通常是不合适的.

相关问题

  1. Java EE架构 - 使用像JPA 2这样的ORM时,仍然建议使用DAO吗?
  2. 使用ORM或纯SQL?
  3. ORM与手动编码数据访问层


Ste*_*n C 23

JPA在我的文件中编写纯SQL的真正好处是什么?

以下是一些好处:

  • JPA允许您避免在SQL的数据库特定方言中编写DDL.相反,您可以在XML中编写"映射",或使用Java注释.

  • JPA允许您避免在SQL的数据库特定方言中编写DML.

  • JPA允许您加载和保存Java对象和图形,而不需要任何DML语言.

  • 当您确实需要执行查询时,JPQL允许您根据Java实体而不是(本机)SQL表和列来表达查询.

一般来说,JPA比JDBC + SQL +手写映射更简单,更清晰,劳动强度更低.数据模型越复杂,它就越有益.

但是,如果性能是最重要的考虑因素,JPA确实会通过在应用程序和数据库之间添加层来阻碍性能.如果您的应用程序要求您广泛手动优化本机数据库查询和模式以最大限度地提高性能,那么JPA可能不太合适.

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


Vla*_*cea 22

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());
}
Run Code Online (Sandbox Code Playgroud)

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());
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<property name="hibernate.jdbc.batch_size" value="50"/>
Run Code Online (Sandbox Code Playgroud)

这是插入这些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
                )
            )
    );
}
Run Code Online (Sandbox Code Playgroud)

简单多了,对吧?

使用 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());
}
Run Code Online (Sandbox Code Playgroud)

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

使用 JPA 获取数据

使用JPA,您可以获取ListPost这样的记录:

int maxResults = 10;

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

而且,不仅编写起来更简单,而且它适用于 Hibernate 支持的每个数据库,因为分页语法是根据底层数据库方言进行调整的。

JPA 相对于 JDBC 的其他优势

  • 您可以获取实体或 DTO。您甚至可以获取分层父子 DTO 投影
  • 您可以在不更改数据访问代码的情况下启用 JDBC 批处理。
  • 您支持乐观锁定。
  • 您有一个独立于底层数据库特定语法的悲观锁定抽象,因此您可以获得 READ 和 WRITE LOCK 甚至SKIP LOCK
  • 您有一个独立于数据库的分页 API。
  • hibernate.query.in_clause_parameter_padding.
  • 您可以使用强一致性缓存解决方案,让您卸载主节点,对于读写事务,只能垂直调用。
  • 您通过 Hibernate Envers 内置了对审计日志的支持。
  • 您具有对多租户的内置支持。
  • 您可以使用 Hibernate hbm2ddl工具从实体映射生成初始架构脚本,您可以将其提供给自动架构迁移工具,例如Flyway
  • 您不仅可以自由执行任何本机 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。


Doc*_*val 16

虽然这是一个老问题,但我觉得它值得一个新的答案.我是JPA的后期使用者,我已经使用它几年了,虽然我有一些时间让我对于新应用程序的简单性感到印象深刻,但我已经变得毫无印象具有正确执行JPA所需的性能,复杂性和学习曲线.这个帖子的答案实际上强化了我的立场.

首先,@vineet建议"实体可以有多种用途"......我在生产中看到过这种情况,而且我会说ORM会鼓励它.凝聚力和单一的责任主体.根据我的经验,向数据库实体添加行为是一件麻烦事.我知道这是因为我已经做到了,并为此感到后悔.

其次,JPA的复杂性有一些简单的替代方案,它们能够使用具有RDBMS的类,而不会由于ORM尝试(不成功)解决的不匹配而导致的所有沉重(和性能问题).我们在具有超过1,000个表的应用程序中使用非JPA关系类映射工具已经十年了,我们根本看不到JPA是如何改进而不是更直接地访问数据库.JPA模糊了数据库的功能,同时将开销(以注释和JQL的形式)添加到类模型中...不应该以其他方式工作吗?

@water提出了许多在理论上属实但在现实中不切实际的事情.例如,已经三次切换后端数据库,我可以向读者保证,没有一些配置调整,你已经完成了.我建议,如果你花了很多时间维护你的持久层,你的数据库模型正在发展,你将在JPA中做相同或更多的工作.特别是当JPA中的非平凡查询需要使用JQL时!

几乎每个人都假装JPA开发人员不需要知道SQL.我在实践中看到的是,现在我们必须学习SQL JQL.显然我们不需要做DDL - 但在任何非平凡的应用程序当然你需要知道DDL.Hibernate甚至不推荐使用自动DDL生成.显然我们不需要做DML,除非我们调用Native Query,这当然是非可移植的,破坏了缓存,并且与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而感到震惊,因为它会向服务器发送休眠状态,以避免一开始就不需要的往返.

  • ORM 仇恨者 * 做 * 明白了 :-) -- 说得好! (2认同)
  • @Inanc Cakil Spring Data JPA 只是 JPA 之上的一个抽象层,因此提到的有关 JPA 的所有内容仍然适用于它。 (2认同)

jav*_*y79 6

如果正确完成,您可以使用JPA实现(如hibernate)将SQL查询直接映射到Java对象.我最近做了一个项目,我有一个POJO和3或4个注释和一点点设置代码来获取存储过程并将其直接映射到对象列表(POJO类类型).这对我来说是JPA力量的一部分.

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

有一个像样的文章在这里对JPA的优势.

希望这可以帮助.


Rav*_*mar 6

众所周知,对象是我们生活中最重要的东西之一,在编程对象中很容易简化任何问题....如果对象可用,那么为什么我们使用整个东西而不是那个小部分事物意味着对象....

  • 如果您在企业应用程序中手动编写SQL语句,则需要花费大量的开发时间来更新和维护持久层.

在持久性方面,==>不再需要JDBC API来进行结果集或数据处理.==>它有助于减少代码行,&&&&&&==>它将我们的应用程序从基础SQL数据库和sql方言中抽象出来.切换到其他SQL数据库需要在Hibernate配置文件中进行少量更改(一次写入/随处运行).


小智 6

  • JPA 非常适合非性能导向的复杂应用程序。
  • JDBC 最适合性能是关键执行者的情况。