Spring Data JDBC与Spring JDBC和Spring Data JPA对比

新成员将添加到“弹簧数据”系列中。弹簧数据 JDBC 使用两者的最佳元素定位在弹簧数据 JPA 和弹簧 JDBC 之间。这篇文章将描述这个产品的当前状态和未来。它还将解释该产品试图解决哪些问题以及如何解决。

目录

介绍

弹簧数据 JDBC 是弹簧数据系列的新成员。它的创建是为了填补位于春季JDBC和春季数据JPA之间的空白。如果你看一下Spring JDBC,你可能会说它太低了,因为它只有助于与数据库的连接。Spring Data JPA可能看起来太复杂了,因为它为您提供了很多选项,并且很难掌握所有这些选项。弹簧数据JDBC是一个框架,它试图为您提供与使用弹簧数据JPA相同的功能,但通过使用DDD原则使其更易于理解。它还通过在较低级别上工作并让您决定何时需要像Spring JDBC那样进行数据库交互来提供更多控制,但以更简单的方式。在本文的其余部分中,将显示弹簧数据 JPA、弹簧数据 JDBC 和弹簧 JDBC 之间的差异。这将希望向您展示Spring Data JDBC是一个非常好的产品,具有很大的潜力,旨在帮助您。

春季 JDBC VS 春季数据 JDBC VS 春季数据 JPA

项目差异

春季JDBC,春季数据JPA和春季数据JDBC都是基于不同思维方式的三者。基于这些思维方式,您可以看到如何使用这些技术以及某些部分需要如何构建的差异。春季 JDBC 仅帮助连接到数据库以及在此数据库上执行查询。春之数据JPA希望帮助您管理基于JPA的存储库。它希望删除样板代码,提高实现速度,并为应用程序的性能提供帮助。春季数据JDBC还希望提供访问关系数据库的帮助,但希望保持实现不那么复杂。

春季京东

春季 JDBC 提供的帮助是提供一个执行 SQL 的框架。春季 JDBC 处理与数据库的连接,并允许您使用 Jdbc 模板执行查询。此解决方案非常灵活,因为您可以完全控制已执行的查询。您还可以自由定义类结构,因为您可以完全控制映射。

春季数据JPA

Spring Data JPA使用实体,因此类结构需要与数据库结构进行比较,因为某些映射是自动完成的。在最简单的形式中,数据库表将分别表示一个实体,并且几乎可以直接映射到实体类上。此映射可以通过使用 Java 配置来完成。通过使用注释,您可以定义在哪个表上映射类,以及如何将表链接在一起。

当使用@Entity注释注释类时,该类被视为实体。最常见的链接是@OneToMany、@ManyToOne和@ManyToMany。例如,如果类 A 与类 B 具有@OneToMany关系,则数据库方案将在表 B 到表 A 中具有外键,并且 JPA 实现将在实体 A 中保留链接的 B 元素的列表。对于如何将实体链接在一起几乎没有任何限制。这意味着完全可以自由地按照自己的方式设计类结构,但这也意味着你需要警惕,不要引起循环依赖关系等问题。这也意味着没有什么可以阻止您创建不易理解的复杂结构,特别是了解您的类结构如何转换为数据库结构。因此,您将始终需要考虑类结构决策如何影响您的数据库。

例如,定义具有多对多关系或双向关系的数据结构很容易。将其转换为数据库结构时,您将需要中间表并创建对单向关系的引用。

例:

在此示例中,有一些类可以在 Spring 数据 JPA 中使用。它们表示域模型的一部分,本文的其余部分也将使用该部分。租赁公司包含租赁。这些租赁包含有关租用哪辆车以及何时租用的信息。

    @Entity
    public class Rental {
     
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
     
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "company_id")
        private RentalCompany company;
     
        // ...
     
    }
    
    @Entity
    public class RentalCompany {
     
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
     
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
        private List<Rental> rentals;
         
        // ...
    }

春季数据 JDBC

当您使用春季数据JDBC时,您还需要创建将映射到数据库的实体类。最大的区别在于,在创建类结构时,您需要遵循更多规则。类结构需要遵循 DDD 的聚合设计规则。Spring Data强制执行这一点,因为这将导致创建更简单易懂的项目。如果你不知道这个概念或为什么它是有用的,你可以查看一篇关于这个的非常好的文章:Vaugn Vernon的有效聚合设计。基本上,我们将具有强耦合的不同实体组合在一起,我们称之为聚合。聚合的顶部实体称为聚合根。还有其他一些规则需要遵循:

  • 一个实体只能是 1 个聚合的一部分。
  • 聚合中的所有关系都需要是单向的。
  • 聚合根需要管理顶级关系。

这意味着,通过跟踪从聚合根开始的链接,可以找到聚合中的每个实体。因此,我们不需要像Spring数据JPA那样为每个实体提供存储库,而只需要为聚合根创建存储库。若要将实体类链接在一起以形成聚合,需要使用对象引用。聚合中的实体类只能具有一对一关系和一对多关系。如果具有一对一关系,则实体只需要对另一个对象的对象引用。当您具有一对多关系时,您的实体需要包含对象引用的集合。要创建与聚合外部实体的关系,需要使用 id 来获取这些类之间的低耦合。

创建弹簧数据JDBC与弹簧数据JPA使用的类的一个很大区别是,不需要使用@Entity,也不需要使用@OneToMany等关系注释。春季数据 JDBC 知道一个类是聚合根,当它包含该类的存储库时。由于聚合实体通过对象引用连接的规则,Spring Data JDBC 还知道聚合是什么,并且可以将数据作为聚合传输到数据库。

例:

在下面的示例中,您可以看到如何在弹簧数据 JDBC 中实现在弹簧数据 JPA 示例中引入的域模型。在此实现中,有 2 个聚合根 租车公司 和 汽车。租赁是租赁公司总和的一部分。本文中的其他示例也将使用此域模型来比较三个框架之间的差异。

    public class RentalCompany {
    
        @Id
        private Long id;
    
        private String name;
        private Set<Rental> rentals;
    
    }
    
    public class Rental {
    
        @Id
        private Long id;
        
        private String renter;
        private Long carId;
        private LocalDate startDate;
        private LocalDate endDate;
    }
    
    public class Car {
    
        @Id
        private Long id;
        
        private String color;
        private String brand;
        private String model;
        private String licensePlate;
        private CarType type;
    }
    

插入数据

要插入数据,您需要使用在上一节中创建的工具。如果您使用春季 JDBC,那么您将编写由 Jdbc 模板直接在数据库上执行的查询。如果使用弹簧数据 JPA 或弹簧数据 JDBC 插入数据,则可以使用已创建的实体或聚合系统。

春季京东

使用春季 JDBC,您可以自己编写插入语句,并使用 Jdbc 模板执行它们。自己编写所有查询的优点是您可以完全控制它们。

例:

    SingleConnectionDataSource dataSource = new SingleConnectionDataSource();
    dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
    dataSource.setUrl("jdbc:hsqldb:data/jdbcexample");
    dataSource.setUsername("sa");
    dataSource.setPassword("");
        
    JdbcTemplate template = new JdbcTemplate(ds);
    template.execute("create table car (id int, model varchar)");
    template.execute("insert into car (id, model) values (1, 'Volkswagen Beetle')");
    
    dataSource.destroy();
    

春季数据JPA

如果使用 Spring 数据 JPA 插入数据,则需要使用存储库和实体。这样就可以在更高的层次上进行思考,并让Spring数据JPA处理查询的创建。当您要为实体创建数据时,您唯一需要做的就是创建一个具有正确值的对象,并在Spring数据存储库上调用 save 方法。然后,Spring Data JPA将查看您的实体及其所有注释,以将其映射到必要的插入或更新语句。

下面的示例演示如何使用弹簧数据JPA在数据库中插入数据。Rental

    @Service
    public class RentalService{
        
        private RentalRepository rentalRepository;
        
        public RentalService(RentalRepository rentalRepository){
            this.rentalRepository = rentalRepository;
        }
        
        public Rental create(Rental rental){
            return rentalRepository.save(rental);
        }
    }
    

春季数据 JDBC

弹簧数据 JDBC 使用的语法可与弹簧数据 JPA 相媲美。最大的区别是在引擎盖下。持久性的管理由存储库处理,就像在Spring数据JPA中一样,但只有聚合根具有存储库。这意味着,如果要插入或更新数据,则需要保存整个聚合。您将需要调用聚合根的存储库的 save 方法,这将首先保存聚合根,然后保存所有引用的实体。如果只想插入聚合的一部分(例如,仅创建新的租赁),则将更新整个聚合,并且将删除并再次插入引用的实体。

该示例演示如何添加 。如果要创建聚合根的新实例,则该代码可与Spring数据JPA的代码相媲美。Rental

    @Service
    public class RentalCompanyService{
        
        private RentalCompanyRepository rentalCompanyRepository;
        
        public RentalCompanyService(RentalCompanyRepository rentalCompanyRepository){
            this.rentalCompanyRepository = rentalCompanyRepository;
        }
        
        public RentalCompany addRental(Rental rental, Long rentalCompanyId){
            RentalCompany rentalCompany = rentalRepository.findById(rentalCompanyId);
            rentalCompany.getRentals().add(rental);
            return rentalRepository.save(rentalCompany);
        }
    }

查询数据库

为了从我们的数据库中检索数据,我们编写查询。春季 JDBC 将允许您使用 并允许您使用行映射器映射结果。弹簧数据 JDBC 和弹簧数据 JPA 还允许您使用 JPQL 或 SQL 查询创建查询,但您将在存储库中编写它们,框架将帮助您进行映射。JdbcTemplate

春季京东

春季 JDBC 用于查询的主要工具是 。使用它的缺点是它只提供连接,以及您需要自己做的其他所有事情。如果搜索对象,则需要通过实现 将结果映射到 Java 对象。您还需要通过创建异常转换器来执行异常处理。JdbcTemplateRowMapper

我将向您展示一个有关如何创建查询的简单示例。

例子

需要通过实现 来映射此响应。RowMapper

    public class CarRowMapper implements RowMapper<Car> {
    
        @Override
        public Car mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
            Car car = new Car();
     
            car.setId(resultSet.getInt("ID"));
            car.setColor(resultSet.getString("COLOR"));
            car.setBrand(resultSet.getString("BRAND"));
            car.setModel(resultSet.getString("MODEL"));
     
            return car;
        }
    }
    

此映射器可以传递给 将使用它来创建填充的 Java 对象的 。JdbcTemplate

    List<Car> cars = jdbcTemplate.queryForObject(
        "SELECT * FROM CAR WHERE ID = ?", new Object[] {id}, new CarRowMapper());
    

春季数据JPA

当您使用Spring数据框架时,它将帮助您构建查询并获取正确的数据。弹簧数据JPA框架使用JPA规范的实现,如休眠。它们使得使用用户友好的界面查询数据库成为可能。当您想要查询数据库时,Hibernate 将为您提供帮助,而不是自己编写整个查询。有多种方法可以使用Spring数据JPA查询数据库,但它们都需要您扩展要查询的实体的存储库。

可以使用派生查询编写一些基本查询。这方面的一个例子是查找字节。对于这些方法,弹簧数据将完全自行生成SQL。有关如何定义派生查询的详细信息,请参阅 Spring 数据文档

如果需要编写无法轻松定义为派生查询的更高级查询,可以使用@Query批注自行定义查询。在@Query注释中,您可以编写 JPQL 或 SQL 语句。JPQL 是一种类似 SQL 的语法,它在常规 SQL 之上提供了一个抽象层。当使用JPQL时,弹簧数据可以帮助您处理数据。例如,只需添加一个参数即可完成分页和排序。

如果要进行更多的控制,可以通过将批注的本机属性设置为 true 来使用 SQL。然后你不再使用这个额外的层,但它也不能再帮助你了。请注意,即使您直接使用 SQL,您仍将返回由休眠管理的实体。@Query

除了帮助您更轻松地定义要执行的查询外,它还可以帮助您微调性能。当您使用弹簧数据JPA进行查询时,您正在使用实体。这些实体与其他实体有连接。Spring Data JPA将帮助您定义是否要直接返回这些连接的实体。它可以帮助您在需要时搜索这些实体。这被称为急切和延迟加载,这都可以由Spring数据JPA管理。

它还会尝试通过为您提供打开查询缓存的选项来提高性能。启用此功能后,Spring 数据 JPA 将尝试重用生成的 SQL 查询,如果可能的话,还会重用这些查询的结果。

例子

调用存储库以获取具有给定 CarType 的所有租赁的服务。

    @Service
    public class RentalService {
    
        private RentalRepository rentalRepository;
    
        public RentalService(RentalRepository rentalRepository){
            this.rentalRepository = rentalRepository;
        }
    
        public List<Rental> getRentalsByCarType(Long rentalCompanyId, CarType carType) {
            return rentalRepository.findByCompanyIdAndCarType(rentalCompanyId, carType);
        }
    }
    

存储库使用派生查询来公开此功能。

    public interface RentalRepository extends PagingAndSortingRepository<Rental, Long> {
    
        List<Rental> findByCompanyIdAndCarType(Long rentalCompanyId, CarType carType);
    }
    

春季数据 JDBC

弹簧数据 JDBC 的抽象比弹簧数据 JPA 少,但使用弹簧数据概念来使执行 CRUD 操作比弹簧 JDBC 更容易。

它更靠近数据库,因为它在查询数据库时不包含大部分Spring数据魔术。每个查询都直接在 JDBC 上执行,并且没有延迟加载或缓存,因此当您查询数据库时,它将返回整个聚合。

目前,当您想在Spring数据JDBC中向存储库添加方法时,您需要添加一个包含查询的@Query注释。这需要是纯 SQL,而不是春季数据 JPA 中使用的 JPQL。您可以将此查询与添加到Spring数据JPA中的本机查询进行最佳比较。由于春季数据JDBC距离数据库如此之近,因此与春季数据JPA中的本机查询一样,不可能具有分页和排序存储库。这些类型的存储库将在将来添加。这就像派生查询功能一样,它尚未实现,但将来会实现。

当您使用 Spring 数据 JDBC 查询应用程序时,您将收到整个聚合,而不是实体。这使得应用程序更易于理解。应用程序不需要依赖应用程序上下文来获取返回实体的属性状态。由于整个对象都是提取的,因此不需要额外的调用来接收尚未加载的对象的字段值,因为所有字段都已填充。此系统的缺点可能是将加载太多数据。但是,如果发生这种情况,则可能是聚合的边界太大,可能需要拆分聚合。

例子

当我们尝试在春季数据JDBC中执行与春季数据JPA相同的操作时,您需要以不同的方式进行操作。第一个区别是我们不能使用租赁存储库,因为它不存在。它不存在,因为租赁不是聚合根,而是具有聚合根的聚合的一部分 租赁公司。这就是为什么我们现在需要使用租赁公司存储库的原因。

    @Service
    public class RentalCompanyService {
    
        public RentalCompanyRepository rentalCompanyRepository;
    
        public RentalCompanyService(RentalCompanyRepository rentalCompanyRepository){
            this.rentalCompanyRepository = rentalCompanyRepository;
        }
    
        public List<Rental> getRentalsByCarType(Long rentalCompanyId, CarType carType) {
            return rentalRepository.findByIdAndCarType(rentalCompanyId, carType);
        }
    }
    

另一个区别是我们不能使用派生查询,因此我们需要使用@Query注释。

    public interface RentalCompanyRepository extends CrudRepository<RentalCompany, Long> {
    
        @Query(value = "SELECT * " +
                "FROM Rental rental " +
                "JOIN Car car ON car.id = rental.car_id " +
                "WHERE rental.rental_company = :companyId " +
                "AND car.type = :carType")
        List<Rental> findRentalsByIdAndCarType(@Param("companyId") Long companyId, @Param("carType")String carType);
    }
    

更新实例

当您要更新数据库中的实例时,您需要编写一个查询并使用弹簧数据JPA或弹簧数据JDBC中的域模型执行它。JdbcTemplate

春季京东

春季JDBC再次仅在更新数据库中的数据时提供框架。公开更新方法。此方法可以接受查询和可选参数。JdbcTemplate

一个简单的代码示例,我们更新给定汽车的颜色。

    String query = "update Car set color = ? where id = ?";
    jdbcTemplateObject.update(query, color, id);

春季数据JPA

Spring数据JPA提供了更多工具来更新数据,例如实体周围的代理。这些实体的状态存储在持久性上下文中。通过使用这个,弹簧数据JPA能够跟踪对这些实体的更改。它使用这些更改的信息使数据库保持最新。弹簧数据JPA从这些实体创建托管实体。我们不必总是需要创建查询来更新数据库中的数据,而是可以编辑这些实体。然后,这些更改将始终自动保留。此跟踪称为脏跟踪,因为当您更改实体时,这些更新会使实体“脏”,因为状态与数据库中的状态不同。刷新休眠会话时,这些更改将被保留,并且实体将再次“干净”。这只会对事务中的更改执行此操作。如果未在事务上下文中完成更改,则必须调用存储库的 save 方法来保留这些更改。

如果要进行更大的更改,也可以在存储库中创建更新方法。与查询数据库和创建实体一样,您还可以在存储库中创建方法。使用 JPQL,您可以创建一个可以一次更新多个实体的查询。请确保添加@Modifying注释。这是一项安全措施,因此您无法错误地修改某些内容。

如果您想更改所有掀背车的颜色,可以通过不同的方式执行此操作。我会给三个。

如果您在事务中执行此操作,则肮脏的跟踪将处理更改。

       @Service
       public class CarService {
       
           public CarRepository carRepository;
       
           public CarService(CarRepository carRepository){
               this.carRepository = carRepository;
           }
       
           @Transactional
           public List<Car> updateColorOfCarsWithCarType(CarType carType, String color) {
               List<Car> carsWithType = carRepository.findByCarType(rentalCompanyId, carType);
               for(Car currentCar : carsWithType){
                    currentCar.setColor(color);
               }
               return carsWithType;
           }
       }

如果没有事务,则还需要添加一个实现来指示 Hibernate 保留更改。

       @Service
       public class CarService {
       
           public CarRepository carRepository;
       
           public CarService(CarRepository carRepository){
               this.carRepository = carRepository;
           }
       
           public List<Car> updateColorOfCarsWithCarType(CarType carType, String color) {
               List<Car> carsWithType = carRepository.findByCarType(rentalCompanyId, carType);
               for(Car currentCar : carsWithType){
                    currentCar.setColor(color);
                    carRepository.save(currentCar);
               }
               return carsWithType;
           }
       }

第三种方法是通过查询。

       @Service
       public class CarService {
       
           public CarRepository carRepository;
       
           public CarService(CarRepository carRepository){
               this.carRepository = carRepository;
           }
       
           @Transactional
           public List<Car> updateColorOfCarsWithCarType(CarType carType, String color) {
               return carRepository.updateColorOfCarsWithCarType(rentalCompanyId, carType);
           }
       }
       
       public interface CarRepository extends CrudRepository<Rental, Long> {
       
           @Modifying
           @Query(value = "Update Car car " +
                   "SET car.color = :color "
                   "WHERE car.type = :carType ")
           List<Car> updateColorOfCarsWithCarType(@Param("color") String color, @Param("carType")String carType);
       }

春季数据 JDBC

弹簧数据 JDBC 没有像弹簧数据 JPA 那样的持久性上下文。在我看来,这使得春季数据JDBC比春季数据JPA更简单。如果要对数据进行更改,则负责处理持久性。如果不在存储库中调用 save 方法,则不会保留更改。

您还可以选择使用自写查询更新聚合。正如我已经提到的,这些查询直接在数据库上执行,而不使用像Hibernate这样的抽象。更新数据是通过在聚合的存储库上调用 save 方法完成的。

由于弹簧数据 JDBC 不包含像弹簧数据 JPA 这样的持久性上下文,因此它不知道聚合的哪个部分已更新。因此,它将更新聚合根并删除所有引用的实体,然后再次保存它们。作为缺点,即使实体未更新,有时也会被删除和插入,这可能会浪费资源。最大的优点是,您可以确保整个实体在保存聚合后都是最新的。

例子

假设我们要更改相同类型的所有颜色的颜色,就像我们在Spring数据JPA示例中所做的那样。

您可以像春季数据JPA的示例2一样执行此操作。

       @Service
       public class CarService {
       
           public CarRepository carRepository;
       
           public CarService(CarRepository carRepository){
               this.carRepository = carRepository;
           }
       
           public List<Car> updateColorOfCarsWithCarType(CarType carType, String color) {
               List<Car> carsWithType = carRepository.findByCarType(rentalCompanyId, carType);
               for(Car currentCar : carsWithType){
                    currentCar.setColor(color);
                    carRepository.save(currentCar);
               }
               return carsWithType;
           }
       }
       
       public interface CarRepository extends CrudRepository<Rental, Long> {
              
          @Query(value = "SELECT * "
                  "FROM Car car " +
                  "WHERE car.type = :carType ")
          List<Car> findByCarType(@Param("color") String color, @Param("carType")String carType);
      }

另一种方法是通过更新查询,该查询与Spring数据JPA示例相同。如果要更新聚合的一部分,则需要遍历聚合根,因为聚合根保持整个聚合的一致性。

       @Service
       public class CarService {
       
           public CarRepository carRepository;
       
           public CarService(CarRepository carRepository){
               this.carRepository = carRepository;
           }
       
           @Transactional
           public List<Car> updateColorOfCarsWithCarType(CarType carType, String color) {
               return carRepository.updateColorOfCarsWithCarType(rentalCompanyId, carType);
           }
       }
       
       public interface CarRepository extends CrudRepository<Rental, Long> {
       
           @Query(value = "UPDATE Car car " +
                   "SET car.color = :color
                   "WHERE car.type = :carType ")
           List<Rental> updateColorOfCarsWithCarType(@Param("color") String color, @Param("carType")String carType);
       }

使用弹簧数据JDBC的优点

更好的设计

使用Spring Data JDBC的最大优势之一是,它将迫使您遵循DDD设计的规则,例如使用聚合。可以在这里找到为什么这种设计可以帮助您的一个很好的解释。

由于仅使用一对多关系,因此可以更轻松地查看类之间的确切关系。类只能是 1 个聚合的一部分。再加上一对多规则,这使得不可能创建循环依赖关系。如果需要在聚合之间创建关系,则需要使用 id。这使得聚合体之间的耦合尽可能小。

在哪里可以找到与数据交互的逻辑也很清楚,因为只有聚合根具有存储库,因为它们负责这些交互。

更易于理解

只有聚合根负责处理持久性。这清楚地表明了需要坚持什么以及谁负责这样做。由于持久性始终需要通过调用 save 方法启动,因此与使用 Spring Data JPA 相比,它更容易理解何时将保留更改。

很容易看出哪些类是聚合的一部分,因为聚合是使用对象引用连接的。使用 id 时,这些类是不同聚合的一部分。当您查询数据库时,使用Spring数据JDBC的每次调用都是使用预先加载完成的,而不是春季数据JPA中标准的延迟加载。每次需要数据时,都会对数据库进行调用,因为不使用缓存。这些规则结合在一起,确保很容易知道何时(总是)对数据库的调用,从数据库调用(整个聚合)返回数据的哪些部分,并且很容易知道这些聚合由什么组成。

因为您负责在需要保存某些内容时进行保存,并且当您通过存储库执行调用时,将返回整个聚合。这样做的结果是,您需要自己做更多的事情,但它也使您可以完全控制整个数据流。

性能

使用Spring数据JDBC,您可以更好地控制将在数据库上执行的查询,因为它是直接在JDBC上执行的,而不是通过中间层。所有查询都是急切的,这也是一个优点,因为需要发送到数据库的查询更少。

当您通过 Spring 数据 JDBC 在聚合中创建或更新实体时,它将通过删除并再次保存这些实体来执行此操作。Spring Data JDBC需要这样做,因为它没有持久性上下文,并希望确保所有内容都是最新的。这样做的缺点是,有时可能会执行不必要的操作。

使用弹簧数据JPA,您可以更好地微调性能。例如,使用延迟加载和使用缓存的可能性。由于这些可能性,创建配置以获得良好性能也更加困难。

我应该使用它吗?

在我看来,弹簧数据JDBC是弹簧数据系列的一个非常好的补充,具有很大的潜力。随着春季数据JDBC 1.1的发布,它变得更加稳定。如果您知道要遵循的规则,现在使用此技术创建一个简单的应用程序非常容易。使用Spring数据JDBC已经有很多优点,这些优势只有在添加功能时才会增长。

也有一些缺点。最大的缺点是它相当新,版本1.0是在不到2年前发布的。找到有关该技术的大量信息仍然相当困难,因此,如果您要使用该技术,则需要预留一些时间进行实验。

在这种情况下,我建议至少尝试一下这项技术,因为它的潜力很大。我在尝试这项技术时玩得很开心,我注意到它确实帮助我创建了一个好的应用程序。该技术通过使用两全其美的优势,在春季JDBC和春季数据JPA之间达到最佳状态。除此之外,他们还添加了一些不错的DDD原则。

所以简而言之,出去试试吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值