SpringBoot Jpa实体继承通用属性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot Jpa实体继承通用属性相关的知识,希望对你有一定的参考价值。

参考技术A 由于项目中的表结构有很多复用的字段,比如创建时间、删除状态、主键等等,也是为个数据设计规范,我们把项目中的 实体(entity)默认都是继承一个父类(包含这些公共的属性)。

所以我们这里要简单的实现这个父类。

在项目中一般没有物理删除,为了实现逻辑删除,一般会自己实现RepositoryFactoryBean 和 Repository。但是由于多个团队开发,表的结构没有统一,会出现有的表没有基础父类对应的字段,这样就会导致自定义的 jpa repository 操作这些表就会出错。
   @MappedSuperclass 注解是重点!

只要是实体继承当前类,就可以实现通用的主键和逻辑删除状态了。

当然了,我们还可以继续继承,进行实体规范。

自此,通用实体属性规范父类就完成了,我们还可以定义主键策略和验证,这里就不列举了。

如何使用 Spring JPA 仅获取实体的选定属性?

【中文标题】如何使用 Spring JPA 仅获取实体的选定属性?【英文标题】:How to fetch only selected attributes of an entity using Spring JPA? 【发布时间】:2016-09-07 03:17:40 【问题描述】:

我在我的项目中使用 Spring Boot (1.3.3.RELEASE) 和 Hibernate JPA。我的实体如下所示:

@Data
@NoArgsConstructor
@Entity
@Table(name = "rule")
public class RuleVO 

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "name", length = 128, nullable = false, unique = true)
    private String name;

    @Column(name = "tag", length = 256)
    private String tag;

    @OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<RuleOutputArticleVO> outputArticles;

    @OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<RuleInputArticleVO> inputArticles;

我的存储库如下所示:

@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> 

在某些情况下,我只需要获取实体 RuleVO 的 idname 属性。我怎样才能做到这一点?我发现一个通知应该可以使用 Criteria API 和 Projections 但如何?提前谢谢了。沃杰科技

【问题讨论】:

我做不到,你能做的就是延迟加载集合。我想说选择性地拉回数据没有性能优势。 参见 19.1.7 docs.jboss.org/hibernate/orm/3.3/reference/en/html/…,它基本上表明,根据上面的评论,对于非收集字段,它是可行的,但你在浪费时间。 Spring Data Projection 是此类问题的最佳解决方案。 docs.spring.io/spring-data/jpa/docs/current/reference/html/… 【参考方案1】:

您可以使用@Query 注释(HQL)来做到这一点。

请参考下面的 Spring 文档:

http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query

(在spring文档中搜索@Query)

【讨论】:

【参考方案2】:

更新:

正如向我指出的那样,我很懒惰,这可以很好地完成,因此我在浏览网络后更新了我的答案。

这是一个如何获取 only id 和 only 名称的示例:

@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> 

    @Query("SELECT r.id FROM RuleVo r where r.name = :name") 
    List<Long> findIdByName(@Param("name") String name);

    @Query("SELECT r.name FROM RuleVo r where r.id = :id") 
    String findNameById(@Param("id") Long id);

希望此更新对您有所帮助


旧答案:

仅检索特定属性名称/id 是不可能的,因为这不是 spring 的设计方式,也不是任何 SQL 数据库,因为您总是选择作为实体的行。

你可以做的是查询实体中的变量,例如:

@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> 

    public RuleVo findOneByName(String name);
    public RuleVo findOneByNameOrId(String name, Long id);
    public List<RuleVo> findAllByName(String name);
    // etc, depending on what you want

您可以根据需要修改这些内容。您的需求。您可以通过自动装配的存储库直接调用这些方法

有关更多选项和示例,请参阅http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ 第 5.3 节

【讨论】:

那不是真的。 Sql 是关于获取行列的。 select * from mytable 只是我们开发者的懒惰;) 你是对的。好久没写查询了=Dselect field from table,瞧:列 如果您在该实体中有关系则不起作用【参考方案3】:

是的,您可以通过投影来实现。你有很多方法来应用它们:

如果您可以升级到 Spring Data Hopper,它会为投影提供易于使用的支持。在reference documentation 中查看如何使用它们。

否则,首先使用您要加载的属性创建一个 DTO,例如:

package org.example;

public class RuleProjection 

    private final Long id;

    private final String name;

    public RuleProjection(Long id, String name) 
        this.id = id;
        this.name = name;
    

    public Long getId() 
        return id;
    

    public String getName() 
        return name;
    

当然,您也可以使用 Lombok 注释。

然后,您可以像这样在 JPQL 查询中使用:

select new org.example.RuleProjection(rule.id, rule.name) from RuleVO rule order by rule.name

如果您想避免在查询中使用 DTO 类名,另一种选择是使用 QueryDSL 实现您自己的查询方法。使用 Spring Data JPA,您必须:

使用新方法创建一个新接口。例如:

public interface RuleRepositoryCustom 
   public List<RuleProjection> findAllWithProjection();

更改您的存储库以扩展新界面。例如:

public interface RuleRepository extends JpaRepository<RuleVO, Long>, RuleRepositoryCustom 
...

使用 Spring Data JPA QueryDSL 支持创建自定义存储库的实现。您必须事先使用其 Maven 插件生成 QueryDSL 的 Q 类。例如:

public class RuleRepositoryImpl 

    public List<RuleProjection> findAllWithProjection() 
        QRuleVO rule = QRuleVO.ruleVO;
        JPQLQuery query = getQueryFrom(rule);     
        query.orderBy(rule.name.asc());
        return query.list(ConstructorExpression.create(RuleProjection.class, rule.id, rule.name));
    

【讨论】:

JPQLnew Projection(...) 对我有用。谢谢。【参考方案4】:

您还可以定义自定义构造函数以使用 JPQL 获取特定列。

例子:

用类的完整java包路径替换javaPackagePath 在 JPQL 中用作构造函数。

public class RuleVO 
   public RuleVO(Long id, String name) 
    this.id = id;
    this.name = name;
   



@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> 

    @Query("SELECT new javaPackagePath.RuleVO(r.id, r.name) FROM RuleVo r where r.name = :name") 
    List<RuleVO> findIdByName(@Param("name") String name);

【讨论】:

谢谢。此解决方案更简单,特别推荐用于关联实体。注意:虽然我们已经导入了实体,但不需要在查询注释中指定其包路径。 谢谢。我尝试了我们的解决方案,但我得到了 table is not mapped 错误。我总是使用nativeQuery = true,但在你的情况下它不是nativeQuery,所以我删除了它,所以我得到了错误,比如表没有映射。在不使用 nativeQuery=true 的情况下如何工作。任何帮助... @Prakash 你真的用@Entity 映射了你的表吗?看看,如果你有唯一的@Table,它只适用于本机查询。如果您同时使用 JPQL/HQL 和本机查询,则可能需要这两个注释。我可能需要有关错误和您正在使用的查询的更多信息,然后我可以提供更好的帮助。 @AbhilekhSingh 感谢您的回复。我解决了我的问题。实际上,在 JPQL 中,我没有引用实体名称,而是引用了表名,因为它仅适用于本机查询。当我将所有表名和列名更改为实体类名和类变量时,它开始正常工作。 @AbhilekhSingh 对于上述情况,您告诉创建需要从表中获取数据的不同构造函数。例如。如果我需要表中的 2 个列值,那么构造函数应该包含两个参数,依此类推。但是如果我的表中有大约 20 列,对于第一个查询,我需要名字和姓氏,所以它们都是字符串,我创建一个带有两个参数的构造函数作为字符串并获取该数据,对于下一个查询,我需要字符串下的州和城市。在下一个命令中继续.....【参考方案5】:
interface IdOnly
    String getId();


@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> 
    public List<IdOnly> findAllByName(String name);

我注意到这是一个非常古老的帖子,但如果有人仍在寻找答案,试试这个。它对我有用。

【讨论】:

很好奇这是否有效,因为它是一个避免编写 SQL 的答案。有空我会再来的 投影工程。一个注释 - 接口必须是公开的,它必须在单独的文件中docs.spring.io/spring-data/jpa/docs/current/reference/html/… 我刚刚检查过了。看起来查询选择了所有字段,所以这不是一个真正的解决方案。

以上是关于SpringBoot Jpa实体继承通用属性的主要内容,如果未能解决你的问题,请参考以下文章

如何解决从另一个实体(JPA)继承的实体的“未指定主键”?

JPA实体继承实体的映射策略

JPA实体继承实体的映射策略

spring jpa之实体属性类型转换器AttributeConverter,自定义Converter,通用Converter

在 Kotlin 中使用 Jpa 注释从基类继承父属性

spring-data-jpa实体类继承抽象类如何映射父类的属性到数据库