【问题标题】:Spring DAO vs Spring ORM vs Spring JDBCSpring DAO vs Spring ORM vs Spring JDBC
【发布时间】:2014-09-19 08:30:48
【问题描述】:

我正在浏览 Spring 支持的数据访问技术,我注意到它提到了多个选项,我不确定它们之间的区别:

据我了解,Spring JDBC 提供了用于减少样板代码的模板,以便通过简单的旧方式访问数据库——您可以编写自己的 SQL 查询。

Spring-ORM 提供了通过 ORM 技术访问数据库的简化模板,例如 Hibernate、My(i)Batis 等。

Spring-DAO 根据 Spring 的网站:

Spring 中的数据访问对象 (DAO) 支持旨在使其 易于使用 JDBC、Hibernate 或 JDO 等数据访问技术 以一致的方式

我对 ORM 与 JDBC 有点清楚,因为它们针对的是访问数据库的不同方式。但是 Spring-DAO 简直令人困惑!

谁能澄清这三者之间究竟有什么区别? 在哪些场景下应该首选哪个?

另外,还有另一个项目 Spring-DATA 也可用 (http://projects.spring.io/spring-data/) 现在,它是 Spring 支持的所有数据访问技术的父项目,还是只是 Spring-DAO 的新名称?

【问题讨论】:

    标签: spring spring-data spring-jdbc spring-orm


    【解决方案1】:

    这里是每个提到的技术的介绍。

    Spring-DAO

    Spring-DAO 不是严格意义上的 spring 模块,而是应该要求你编写 DAO 并写好它们的约定。因此,它既不提供接口也不提供实现或模板来访问您的数据。编写 DAO 时,应使用 @Repository 注释它们,以便与底层技术(JDBC、Hibernate、JPA 等)相关的异常始终转换为正确的 DataAccessException 子类。

    例如,假设您现在正在使用 Hibernate,并且您的服务层捕获 HibernateException 以便对其做出反应。如果您更改为 JPA,您的 DAO 接口不应更改,并且服务层仍将使用捕获 HibernateException 的块进行编译,但您将永远不会进入这些块,因为您的 DAO 现在正在抛出 JPA PersistenceException。通过在你的 DAO 上使用 @Repository,与底层技术相关的异常被转换为 Spring DataAccessException;您的服务层会捕获这些异常,如果您决定更改持久性技术,仍然会抛出相同的 Spring DataAccessExceptions,因为 spring 已经翻译了本机异常。

    但请注意,由于以下原因,它的使用受到限制:

    1. 您通常不应捕获持久性异常,因为提供程序可能已回滚事务(取决于确切的异常子类型),因此您不应使用替代路径继续执行。
    2. 提供程序中的异常层次结构通常比 Spring 提供的更丰富,并且从一个提供程序到另一个提供程序没有明确的映射。依赖它是危险的。 然而,使用 @Repository 注释 DAO 是一个好主意,因为扫描过程会自动添加 bean。此外,Spring 可能会在注释中添加其他有用的功能。

    Spring-JDBC

    Spring-JDBC 提供了 JdbcTemplate 类,该类删除了管道代码并帮助您专注于 SQL 查询和参数。你只需要配置一个DataSource,然后你就可以编写如下代码:

    int nbRows = jdbcTemplate.queryForObject("select count(1) from person", Integer.class);
    
    Person p = jdbcTemplate.queryForObject("select first, last from person where id=?", 
                 rs -> new Person(rs.getString(1), rs.getString(2)), 
                 134561351656L);
    

    Spring-JDBC 还提供了一个 JdbcDaoSupport,你可以扩展它来开发你的 DAO。它基本上定义了 2 个属性:一个 DataSource 和一个 JdbcTemplate,它们都可用于实现 DAO 方法。它还提供了一个从 SQL 异常到 Spring DataAccessExceptions 的异常转换器。

    如果您打算使用纯 jdbc,这是您需要使用的模块。

    Spring-ORM

    Spring-ORM 是一个涵盖许多持久性技术的伞形模块,即 JPA、JDO、Hibernate 和 iBatis。对于每一种技术,Spring 都提供了集成类,使每一种技术都可以按照 Spring 的配置原则使用,并与 Spring 事务管理顺利集成。

    对于每种技术,配置基本上包括将DataSource bean 注入某种SessionFactoryEntityManagerFactory 等bean。对于纯 JDBC,不需要这样的集成类(除了 JdbcTemplate),因为 JDBC 只依赖于 DataSource。

    如果你打算使用 JPA 或 Hibernate 之类的 ORM,则不需要 spring-jdbc,而只需要这个模块。

    Spring-Data

    Spring-Data 是一个伞形项目,它提供了一个通用 API 来定义如何以更通用的方式访问数据(DAO + 注释),涵盖 SQL 和 NOSQL 数据源。

    最初的想法是提供一种技术,以便开发人员以与技术无关的方式为 DAO(查找器方法)和实体类编写接口,并且仅基于配置(DAO 和实体的注释 + spring 配置,无论是基于xml还是基于java),决定实现技术,无论是JPA(SQL)还是redis,hadoop等(NOSQL)。

    如果你遵循 spring 为 finder 方法名称定义的命名约定,那么对于最简单的情况,你甚至不需要提供对应于 finder 方法的查询字符串。对于其他情况,您必须在 finder 方法的注释中提供查询字符串。

    当应用程序上下文被加载时,spring 为 DAO 接口提供代理,其中包含与数据访问技术相关的所有样板代码,并调用配置的查询。

    Spring-Data 专注于非 SQL 技术,但仍为 JPA(唯一的 SQL 技术)提供模块。

    接下来是什么

    了解了这一切之后,您现在必须决定选择什么。这里的好消息是,您无需为该技术做出明确的最终选择。这实际上是 Spring 的力量所在:作为开发人员,您在编写代码时专注于业务,如果您做得好,更改底层技术就是一个实现或配置细节。

    1. 为实体定义一个带有 POJO 类的数据模型,并使用 get/set 方法来表示实体属性以及与其他实体的关系。您当然需要根据技术对实体类和字段进行注释,但现在,POJO 已经足够开始了。现在只关注业务需求。
    2. 为您的 DAO 定义接口。 1 个 DAO 恰好涵盖 1 个实体,但您当然不需要为每个实体创建一个 DAO,因为您应该能够通过导航关系来加载其他实体。按照严格的命名约定定义查找器方法。
    3. 基于此,其他人可以开始在服务层上工作,并为您的 DAO 模拟。
    4. 您学习不同的持久性技术(sql、no-sql)以找到最适合您需求的技术,然后选择其中一种。在此基础上,您注释实体并实现 DAO(如果您选择使用 spring-data,则让 spring 为您实现它们)。
    5. 如果业务需求不断发展,而您的数据访问技术不足以支持它(例如,您开始使用 JDBC 和一些实体,但现在需要更丰富的数据模型,而 JPA 是更好的选择),您将不得不更改 DAO 的实现,在实体上添加一些注释并更改 spring 配置(添加 EntityManagerFactory 定义)。您的业​​务代码的其余部分不应受到您的更改的其他影响。

    注意:事务管理

    Spring 提供了一个用于事务管理的 API。如果您打算使用 spring 进行数据访问,您还应该使用 spring 进行事务管理,因为它们很好地集成在一起。对于spring支持的每一种数据访问技术,都有一个匹配的本地事务的事务管理器,或者如果需要分布式事务可以选择JTA。它们都实现了相同的 API,因此(再次)技术选择只是一个可以更改而不会对业务代码产生进一步影响的配置。

    注意:Spring 文档

    您提到的 Spring 文档的链接相当陈旧。这是最新版本(4.1.6,涵盖所有主题)的文档:

    Spring-data 不是 Spring 框架的一部分。您应该首先阅读一个通用模块以习惯这些原则。文档可以在这里找到:

    【讨论】:

    • 我很欣赏这个答案,在此处的某些描述(如 Spring Data)中使用术语“伞”,识别其中有子组件/模块(而不是更特定于域的伞)。并且在这里提到 Spring Data 在上下文中非常有用,即使问题中没有提到它。
    • spring-jdbc 不提供这里未提及的其他有用工具吗?例如,我发现SimpleJdbcInsert 非常干净,对于单项插入和批量插入都很有用(当然,要达到合理的规模)。
    【解决方案2】:

    Spring DAO(Data Access Object):是提供抽象接口的对象到 JDBC 实现框架,即 Spring DAO 是 通用 概念来访问 JDBC 和 Hibernate、MyBatis、JPA、JDO 使用它的各个 Support 类。它通过定义@Repository注解提供了generalized异常层次结构。此注解将 SQL exception translation 的 Spring 容器定义为从 SQLException 到 Spring 的数据访问策略不可知 DataAccessException 层次结构。

    即在持久框架和代码之间轻松切换,无需担心捕获特定于每种技术的异常。


    Spring JDBC:对于普通 JDBC,我们使用这个模块,它只依赖于 DataSource 和模板类,如 JdbcTemplateNamedParameterJdbcTemplate(wraps JdbcTemplate) 和 SimpleJdbcTemplate用于减少横切关注点。

    public class EmployeeDao {  
    private JdbcTemplate jdbcTemplate;  
      
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {  
        this.jdbcTemplate = jdbcTemplate;  
    }  
      
    public int saveEmployee(Employee e){  
        return jdbcTemplate.update(query);  
    }  
    public int updateEmployee(Employee e){  
        return jdbcTemplate.update(query);  
    }  
    public int deleteEmployee(Employee e){  
           return jdbcTemplate.update(query);  
    }  
      
    }  
    

    在 Spring XML 中:

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    

    Spring JDBC 还提供了JdbcDaoSupportNamedParameterJdbcDaoSupportSimpleJdbcDaoSupport,它们支持(即方便)方式来扩展和开发我们自己的DAO抽象接口如下:

    public interface EmployeeDao {
     
        public void saveEmployee(Employee emp);
    }
    
    public class EmployeeDaoImpl extends JdbcDaoSupport implements EmployeeDao{
     
        @Override
        public void saveEmployee(Employee emp) {
    
            Object[] inputs = new Object[] {emp.getName(), emp.getSalary(), emp.getDept()};
            getJdbcTemplate().update(query, inputs);
        }
    }
    

    在 Spring XML 中:

    <bean id="employeeDAO" class="EmployeeDaoImpl">
            <property name="dataSource" ref="dataSource" />
        </bean>
    

    Spring ORM:对于诸如 Hibernate、JPA、MyBatis 等 ORM 工具的支持...通过注入 DataSource 以及以下类和各自的 DaoSupport 类轻松集成 Spring。

    • SessionFactory 用于休眠
    • EntityManagerFactory 代表 JPA,
    • SqlSessionFactory MyBatis

    【讨论】:

      【解决方案3】:

      您创建类似SomeObjectDao 的接口,然后创建此接口的不同实现,例如JdbcSomeObjectDaoHibernateSomeObjectDao。然后在您的SomeObjectService 类中,您将对SomeObjectDao 接口进行操作,并在其中注入具体实现之一。所以SomeObjectDao的每一个实现都会隐藏细节,不管你是使用JDBC,还是ORM等等。

      通常是 JDBC,ORM 的不同实现会抛出不同类型的异常。 Spring 的DAO 支持可以将这些不同的、特定于技术的异常映射到常见的 Spring DAO 异常。因此,您与实际实现更加分离。此外,Spring 的 DAO 支持 提供了一组抽象的 *DataSupport 类,它们对 DAO 开发有更大的帮助。因此,除了实现 SomeObjectDao 接口之外,您还可以扩展 Spring 的 *DataSupport 类之一。

      【讨论】:

      • 所以你的意思是,spring-dao 抽象出特定于 Hibernate/JDO/JDBC 的异常并提供一组标准的异常?是否有任何templates 可以访问数据库?还是只是与其他弹簧组件一起使用的抽象?例如。是否可以编写仅使用 spring-dao 访问 db 的代码(不使用 spring-jdbc、spring-orm、hibernate 或任何其他框架)?
      【解决方案4】:

      spring-dao 库在 2.0.8 版(2008 年 1 月)中停止。 spring-dao 中的类被复制到 spring-tx 中。因此,如果您需要在 spring-dao 中找到的类,请将依赖项添加到 spring-tx。 (Source.)

      【讨论】:

        【解决方案5】:

        作为附加信息。我建议你使用 Spring Data JPA。 使用注释,例如:@Repository、@Service。 我给你举个例子:

        @Repository("customerEntitlementsRepository")
        public interface CustomerEntitlementsRepository extends CrudRepository<BbsExerul, BbsExerulPK> {
        
          @Query(value = "SELECT " + "CONTRACT_NUMBER, EXECUTIVE_NUMBER, " + "GROUP_VALUE, " + "CODE, "
              + "SUBCODE, " + "CURRENCY " + "FROM BBS_EXERUL " + "WHERE CONTRACT_NUMBER =:clientId AND "
              + "EXECUTIVE_NUMBER =:representativeId", nativeQuery = true)
          Collection<CustomerEntitlementsProjection> getFieldsExerul(@Param("clientId") String clientId,
              @Param("representativeId") String representativeId);
        
        }
        

        CustomerEntitlementsProjection 是 Spring 投影,与您的实体或 DTO pojo 链接;

        @Projection(name = "customerEntitlementsProjection", types = { BbsExerul.class })
        public interface CustomerEntitlementsProjection {
        
          String getContractNumber();
        
          String getExecutiveNumber();
        

        【讨论】:

        • 请在代码块中格式化您的代码,以便其可读。
        猜你喜欢
        • 2012-08-04
        • 1970-01-01
        • 1970-01-01
        • 2019-01-26
        • 2020-03-29
        • 1970-01-01
        • 2020-10-09
        • 2010-12-30
        • 2015-05-25
        相关资源
        最近更新 更多