【问题标题】:JPA entity with extra supportive View具有额外支持视图的 JPA 实体
【发布时间】:2022-01-18 01:54:52
【问题描述】:

给定表模式如下(这是mysql语法,但没关系)

-- base table keeping all subscription data
CREATE TABLE user_subscription (
  id         INTEGER       NOT NULL AUTO_INCREMENT PRIMARY KEY,
  user_id    INTEGER       NOT NULL,
  data       VARCHAR(2000) NULL,
  created_at DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- supportive view focusing on every user's latest row
CREATE VIEW user_subscription_latest AS
  SELECT s1.*
  FROM user_subscription s1
  LEFT JOIN user_subscription s2
    ON s2.user_id = s1.user_id AND s2.updated_at > s1.updated_at
  WHERE s2.id IS NULL;

我有以下基表的实体类user_subscription

import javax.persistence.*;

@Entity
@Table(name = "user_subscription")
public class UserSubscription implements java.io.Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id", nullable = false, updatable = false, insertable = false)
  private Integer id;

  @Column(name = "user_id", nullable = false)
  private Integer userId;

  @Column(name = "data", nullable = true, length = 2000)
  private String data;

  @Temporal(TemporalType.TIMESTAMP)
  @Column(name = "created_at", nullable = false, updatable = false, insertable = false)
  private java.time.LocalDateTime createdAt;

  @Temporal(TemporalType.TIMESTAMP)
  @Column(name = "updated_at", nullable = false, updatable = false, insertable = false)
  private java.time.LocalDateTime updatedAt;

}

并遵循 Spring Data JPA 存储库(仍然没关系。它可以是任何 JPA 场景)

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface UserSubscriptionRepository extends JpaRepository<UserSubscription, Integer> {

  // TODO How can I achieve this without using nativeQuery?
  @Query(value = "SELECT * FROM user_subscription_latest", nativeQuery = true)
  java.util.List<UserSubscription> findLatest();

}

由于视图user_subscription_latestuser_subscription 的子集,它们可以互换,但我不知道如何将它们组合在一起。

我的问题是设计 JPA 实体的正确/首选方法是什么,以便我可以在查询时利用支持视图 user_subscription_latest,同时保持对基表 user_subscription 的可访问性,而无需 nativeQuery?

【问题讨论】:

    标签: java sql jpa


    【解决方案1】:

    要回答您关于什么是推荐的方式(除了使用 nativeQuery)来获得对您的视图的可访问性以及保持您对用户表的现有可访问性的问题,最好的方法是创建另一个类似的实体下面,

    import javax.persistence.*;
    
    @Entity
    @Immutable
    public class UserSubscriptionLatest implements java.io.Serializable {
    
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = "id", nullable = false, updatable = false, insertable = false)
      private Integer id;
    
      @Column(name = "user_id", nullable = false)
      private Integer userId;
    
      @Column(name = "data", nullable = true, length = 2000)
      private String data;
    
      @Temporal(TemporalType.TIMESTAMP)
      @Column(name = "created_at", nullable = false, updatable = false, insertable = false)
      private java.time.LocalDateTime createdAt;
    
      @Temporal(TemporalType.TIMESTAMP)
      @Column(name = "updated_at", nullable = false, updatable = false, insertable = false)
      private java.time.LocalDateTime updatedAt;
    
    }
    

    使您的实体类不可变是在 Spring Data JPA 中使用数据库视图的最重要的一步。基于此映射,Hibernate 将确保您的应用程序不会对视图执行任何写入操作。为了使您的持久层更易于使用,您应该更进一步,并从您的存储库中排除所有持久、更新或删除实体的方法。

    然后创建一个 readOnlyRepository,定义一个扩展 Repository 接口的接口,并从 Spring Data JPA 的标准存储库中复制一些方法定义,如下所示

    @NoRepositoryBean
    public interface ReadOnlyRepository<T, ID> extends Repository<T, ID> {
     
        List<T> findAll();
     
        List<T> findAll(Sort sort);
     
        Page<T> findAll(Pageable pageable);
     
        Optional<T> findById(ID id);
         
        long count();
    }
    

    然后通过扩展我们刚刚创建的 readOnlyRepository 来创建您的 JPArepository,这可以确保您的存储库可用于只读目的,并告诉 hibernate 避免任何与写入/更新有关的检查

    public interface UserSubscriptionLatestRepository extends  ReadOnlyRepository<UserSubscriptionLatest, Integer> {    
    List<UserSubscriptionLatest> findById();
    }
    

    希望我已经回答了你的问题

    【讨论】:

    • 但我想让它们可以互换,因为视图只是表的一个子集,没有转换。使用新实体,我可以在视图上查询,但是 JPA 以不同的方式对待它们,我无法使用 TableRepository 持久化 ViewEntity。
    • 是的,我可以声明实体之间的关系并通过复制到其他实体来保持它们,但是同步(在缓存中)变得有问题,我正在寻找更优雅的东西。
    • 我担心你不能像这样持久化 ViewEntity,因为视图只是实际表之上的关系。因此,如果您希望更改视图中的数据,您实际上应该更新表。至少据我所知,我看不到您直接更新视图并希望您的实际表具有不同值的可能性。我明白你的观点,你不想每次都刷新实体并检索记录,但即使优雅的解决方案也会做同样的事情。如果您一次性完成所有事情,它不会减少查询。
    猜你喜欢
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    相关资源
    最近更新 更多