用于数据库视图(而非表)的JPA / SpringBoot存储库

时间:2018-11-27 21:09:32

标签: java hibernate jpa spring-data-jpa

我正在尝试为视图创建JPA实体。在数据库层中,表和视图应该相同。

但是,问题开始出现并且有两个方面:

  1. 尝试设置正确的注释时。视图没有与之关联的主键,但是如果没有在字段上注释正确的@javax.persistence.Id,您将在运行时抛出org.hibernate.AnnotationException: No identifier specified for entity

  2. Spring Boot JpaRepository接口定义要求ID类型扩展Serializable,从而避免了java.lang.Void作为缺乏解决方案的变通方法。视图实体上的ID。

与缺乏主键的视图进行交互的正确JPA / SpringBoot / Hibernate方法是什么?

5 个答案:

答案 0 :(得分:5)

1。在数据库中使用本地SQL创建视图,

create or replace view hunters_summary as 
select 
em.id as emp_id, hh.id as hh_id
from employee em 
inner join employee_type et on em.employee_type_id = et.id  
inner join head_hunter hh on hh.id = em.head_hunter_id;

2。将其映射为“不可变实体”

package inc.manpower.domain;

import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Subselect;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;

@Entity
@Immutable
@Table(name = "`hunters_summary`")
@Subselect("select uuid() as id, hs.* from hunters_summary hs")
public class HuntersSummary implements Serializable {

    @Id
    private String id;
    private Long empId;
    private String hhId;

    ...
}

3。现在,使用所需的方法创建存储库,

package inc.manpower.repository;

import inc.manpower.domain.HuntersSummary;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

import javax.transaction.Transactional;
import java.util.Date;
import java.util.List;

@Repository
@Transactional
public interface HuntersSummaryRepository extends PagingAndSortingRepository<HuntersSummary, String> {
    List<HuntersSummary> findByEmpRecruitedDateBetweenAndHhId(Date startDate, Date endDate, String hhId);
}

答案 1 :(得分:2)

我也在探索这个话题。我最终将Spring Data JPA Interface-based Projections与本机查询一起使用。

我创建了一个接口,确保大写部分与数据库列名称匹配:

public interface R11Dto {

   String getTITLE();

   Integer getAMOUNT();

   LocalDate getDATE_CREATED();
}

然后,我为一个与视图没有任何关系的实体(用户)创建了一个存储库。在该存储库中,我创建了一个简单的本地查询。 vReport1_1是我的观点。

public interface RaportRepository extends JpaRepository<User, Long> {

   @Query(nativeQuery = true, value = "SELECT * FROM vReport1_1 ORDER BY DATE_CREATED, AMOUNT")
   List<R11Dto> getR11();

}

答案 2 :(得分:1)

关于实体ID映射

如果可以更改视图定义,则可以使用将rownum添加为列。
它通常特定于DBMS。想法是使表中的行号成为实体的ID。
或者,您可以使用唯一ID的生成器。 UUID是可能的。 最后,您可以坚持使用本机SQL,同时受益于JPA / Hibernate将本机查询结果映射到表示视图数据的特定类。

关于Spring数据存储库

如果视图不能自然地满足Spring Data Repository的要求,则可能意味着没有必要使用视图代替表。

独立的Spring Data存储库类(例如CrudRepositoryJpaRepository)旨在为特定的实体类提供开箱即用的CRUD操作和一些其他处理。
选择DBMS时,不包含有关DBMS的视图,但您不会从视图中直接更新,插入或删除任何行。
此外,将这种Repository Bean用于视图的附加值是什么? 您几乎不会使用Spring提供的生成的实现。

在您的情况下,如果您将视图定义为实体,我认为使用JpaTemplate会更有意义。
它只是JPA API之上的一层。
来自the documentation

  

JpaTemplate可以被认为是与   本机JPA EntityManager API(通过共享的EntityManager   参考,如上所述)。主要优点是其自动   转换为DataAccessExceptions;主要缺点是它   在本机JPA API之上引入了另一个薄层。注意   异常翻译也可以通过AOP建议来实现;校验   出PersistenceExceptionTranslationPostProcessor

答案 3 :(得分:1)

希望对您有帮助,您可以将ID分配给视图中的统一值。

我们通过以下方式将视图映射到JPA对象:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name = "my_view")
public class MyView implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "my_view_id")
private Long myViewId;
@NotNull
@Column(name = "my_view_name")
private String myViewName;
}

然后我们创建一个存储库:

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

public interface MyViewRepository extends JpaRepository<View, Long> {
}

答案 4 :(得分:0)

如果您的视图没有候选键,则可以使用数据库UUID函数之类的内容在创建查询中添加一个候选键,然后将UUID用作实体ID的类型。

如果您需要将实体设置为只读,则可以使用

注释字段
 @Column(insertable = false, updatable = false)

或者使用org.hibernate.annotations注释您的实体类。如果您的提供者是Hibernate> = 5.2,则不可变。