如何在Spring JPA/Hibernate中使用JoinTable设置子实体仅通过ID引用父实体。

huangapple go评论88阅读模式
英文:

How to set up a child entity referencing parent by only ID using a JoinTable in Spring JPA/Hibernate

问题

问题

  1. 我有一个具有一对多关系的父实体和子实体。
  2. 正在使用 Mapstruct 将实体映射到 DTO,因此仅获取所有实例(甚至是惰性地)将导致从持久性存储中实例化/获取每个实例(这不是我们想要的,我只关心 Spring JPA/Hibernate 类之外的 ID)
  3. 父实体和子实体的表都没有彼此的引用,关系由连接表处理。

我只关心关联的 ID。我不希望 DTO 作为属性具有相应的关联类。我希望 ParentSet<String> childIds,而 Child 在 DTO 中有 String parentId。因此,我不想在持久性层加载整个对象,以便将其他所有内容都映射掉。

尝试

  1. 使实体类引用另一个实体类。Entity → DTO 是可以的,但是 DTO → Entity Hibernate/Spring JPA 报错实体已分离(因为它刚刚来自 ID)。我考虑的一个解决方案是调用 EntityManager.getReference,但如果它是一个新实体,它会抛出错误,那么我如何保存新实体?我将不得不执行 existsById,但现在我们正在进行更多的数据库调用,这变得昂贵起来。
  2. 多种组合的 JoinColumnJoinColumnsJoinTable 等。

Java 类

实体

@Entity
@LombokGettersAndSetters
public class ParentEntity {
    
    @Id
    private Long id;
    
    @ElementCollection
    private Set<Long> childIds; || private Set<ChildEntity> children;
}
@Entity
@LombokGettersAndSetters
public class ChildEntity {
    
    @Id
    private Long id;

    @Column(name = "parent_id")
    private Long parentId; || private ParentEntity parent;
}

映射器

(Mapstruct 映射器)

DTOs

public class ParentDTO {
    private Long id;
    private Set<Long> childIds;
}
public class ChildDTO {
    private Long id;
    private Long parentId;
}

表格

Parent       Child      Parent_join_Child
-------      -----      -----------------
id           id         parent_id
                        child_id

提前致谢!

编辑 @ +1m:我应该注意到,我在示例中删除了一些注释,比如下面的

@Column(name = "parent_id", columnDefinition = CustomColumnDefinition.UNSIGNED_INT)
@GeneratedValue(strategy = GenerationType.IDENTITY)
英文:

Problem

  1. I have a parent entity with a one-to-many relationship with
    child entities.
  2. Mapstruct is being used to map entities to DTOs, so just fetching all (even lazily) will result in every instance being instantiated/fetched from persistence (which we do not want, I only care about IDs outside of the Spring JPA/Hibernate classes)
  3. Both parent and child's tables have no reference of the other, the relationship is handled by a join table

I only care about the IDs for the associations. I do not want the DTO to have the respective associated class as an attribute. I want Parent to have Set&lt;String&gt; childIds and Child to have String parentId in the DTOs. Because of this, I do not want to load the entire object in the persistence layer just to map away everything else.

Attempts

  1. Having the entity classes reference the other entity class. Entity → DTO is ok, but DTO → Entity Hibernate/Spring JPA complains that the entity is detached (because it just came from an ID). One solution I thought of is calling EntityManager.getReference but that throws an error if it's a new entity, so how can I save new ones? I'd have to do an existsById but now we're making even more database calls, this is getting expensive
  2. A bunch of combinations of JoinColumn, JoinColumns, JoinTable, etc.

Java classes

Entities

@Entity
@(LombokGettersAndSetters)
public class ParentEntity {
    
    @Id
    private Long id;
    
    @(???)
    private Set&lt;Long&gt; childIds; || private Set&lt;ChildEntity&gt; children;
}
@Entity
@(LombokGettersAndSetters)
public class ChildEntity {
    
    @Id
    private Long id;

    @(???)
    private Long parentId; || private ParentEntity parent;
}

Mappers

(Mapstruct mappers)

DTOs

public class ParentDTO {
    private Long id;
    private Set&lt;Long&gt; childIds;
}
public class ChildDTO {
    private Long id;
    private Long parentId;
}

Tables

Parent       Child      Parent_join_Child
-------      -----      -----------------
id           id         parent_id
                        child_id

Thanks in advance!

Edit @ +1m: I should note, I removed some annotations in my examples such as the below

@Column(name = &quot;parent_id&quot;, columnDefinition = CustomColumnDefinition.UNSIGNED_INT)
@GeneratedValue(strategy = GenerationType.IDENTITY)

答案1

得分: 2

你可以使用@JoinTable来将父实体与子实体关联,如下所示:

@Entity
@LombokGettersAndSetters
public class ParentEntity {
    
    @Id
    private Long id;
    
    @JoinTable(name = "Parent_join_Child", joinColumns = {
        @JoinColumn(name = "parent_id", referencedColumnName = "id") }, inverseJoinColumns = {
            @JoinColumn(name = "child_id", referencedColumnName = "id") })
    private Set<ChildEntity> children;
}

请注意,我已将代码部分保持不变,只翻译了注释部分。

英文:

You can use @JoinTable to join your parent to child as shown below

@Entity
@(LombokGettersAndSetters)
public class ParentEntity {
    
    @Id
    private Long id;
    
    @JoinTable(name = &quot;Parent_join_Child&quot;, joinColumns = {
			@JoinColumn(name = &quot;parent_id&quot;, referencedColumnName = &quot;id&quot;) }, inverseJoinColumns = {
					@JoinColumn(name = &quot;child_id&quot;, referencedColumnName = &quot;id&quot;) })
    private Set&lt;ChildEntity&gt; children;
}

答案2

得分: 0

创建ParentChild实体:

@Entity
public class ParentChild {

  private long id;
  private long parentId;
  private long childId;

}

并对其进行操作:

parentChildRepository.findByWhatever() and group by parent and map to dto.
英文:

Can you do the following:

Create ParentChild entity:

@Entity
public class ParentChild {
  
  private long id;
  private long parentId;
  private long childId;

}

and work with it:

   parentChildRepository.findByWhatever() and group by parent and map to dto.

答案3

得分: 0

你需要编写一个自定义查询,仅选择你关心的部分,但这样你将无法使用Mapstruct。我建议你看看Blaze-Persistence Entity Views提供了什么。

我创建了这个库,以实现JPA模型与自定义接口或抽象类定义模型之间的轻松映射,有点类似于功能强化版的Spring Data Projections。思路是你可以根据喜好定义目标结构(领域模型),并通过JPQL表达式将属性(getter)映射到实体模型。

假设有如下的实体模型:

@Entity
public class ParentEntity {
    
    @Id
    private Long id;
    
    @OneToMany(mappedBy = "parent")
    private Set<ChildEntity> children;
}

@Entity
public class ChildEntity {
    
    @Id
    private Long id;

    @ManyToOne
    private ParentEntity parent;
}

对于你的用例,使用Blaze-Persistence Entity-Views的DTO模型可能如下所示:

@EntityView(ParentEntity.class)
public interface ParentDTO {
    @IdMapping
    Long getId();
    @Mapping("children.id")
    Set<Long> getChildIds();
}

查询只需要将实体视图应用于查询,最简单的情况就是按id查询。

ParentDTO a = entityViewManager.find(entityManager, ParentDTO.class, id);

在幕后,这将创建查询以仅获取所需内容,不多不少。

Spring Data集成使你可以几乎像使用Spring Data Projections一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

英文:

You'd have to write a custom query that selects only the parts that you care about, but then you won't be able to use Mapstruct. I recommend you take a look at what Blaze-Persistence Entity Views has to offer.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

Assuming an entity model like this:

@Entity
public class ParentEntity {
    
    @Id
    private Long id;
    
    @OneToMany(mappedBy = &quot;parent&quot;)
    private Set&lt;ChildEntity&gt; children;
}

@Entity
public class ChildEntity {
    
    @Id
    private Long id;

    @ManyToOne
    private ParentEntity parent;
}

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@EntityView(ParentEntity.class)
public interface ParentDTO {
    @IdMapping
    Long getId();
    @Mapping(&quot;children.id&quot;)
    Set&lt;Long&gt; getChildIds();
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

ParentDTO a = entityViewManager.find(entityManager, ParentDTO.class, id);

Behind the scenes this will create the query to fetch exactly what is needed, nothing more.

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

huangapple
  • 本文由 发表于 2020年10月13日 11:04:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/64327873.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定