如何在避免复杂对象的情况下将 dto 映射到实体

Posted

技术标签:

【中文标题】如何在避免复杂对象的情况下将 dto 映射到实体【英文标题】:How to Map a dto to an entity with avoiding complexs objects 【发布时间】:2022-01-14 03:04:14 【问题描述】:

我尝试将 dto 映射到避免复杂对象的实体,但是当我尝试保存文章实体时出现 null 异常? 在我的 ArticleRequest 而不是使用 Department & ArticleCategory 作为一个完整的对象我只是把他们的 ids (uid) 放在了。

这是我的文章请求:

@Slf4j
@Builder
@Data
public class ArticleRequest 

    private Long id;

    @Size(min = 32, message = "uid should have at least 32 characters")
    private String uid;

    @Size(min = 2, message = "title should have at least 2 characters")
    private String title;

    @NotBlank(message = "content should not be empty value")
    private String content;

    @NotNull(message = "article category id should not be null value")
    private String articleCategoryUid;

    @NotNull(message = "department id should not be empty value")
    private String departmentUid;

    @JsonIgnore
    private List<ArticleVote> articleVoteList;

    public static ArticleRequest fromEntity(Article article) 
        if (article == null) 
            log.warn("Class: ArticleRequest || Method: fromEntity() || Error: article is null!!!");
            return null;
        

        return ArticleRequest.builder()
                .id(article.getId())
                .uid(article.getUid())
                .title(article.getTitle())
                .content(article.getContent())
                .articleCategoryUid(article.getArticleCategory().getUid())
                .departmentUid(article.getDepartment().getUid())
                .build();

    

    public static Article toEntity(ArticleRequest articleRequest) 
        if (articleRequest == null) 
            log.warn("Class: ArticleRequest || Method: toEntity() || Error: articleRequest is null!!!");
            return null;
        

        Article article = new Article();
        article.setId(articleRequest.getId());
        article.setUid(articleRequest.getUid());
        article.setTitle(articleRequest.getTitle());
        article.setContent(articleRequest.getContent());
        article.getArticleCategory().setUid(articleRequest.getArticleCategoryUid()); // i have null exeption here !! because ArticleCategory already null
         article.getDepartment().setUid(articleRequest.getDepartmentUid()); // i have null exeption here !! because Department already null
        return article;
    

这是我的基础实体

@Data
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Serializable 

    //region Simple Properties

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

    @NotNull
    @Size(min = 32, message = "uid should have at least 32 characters")
    @Column(name = "uid", nullable = true,updatable = false)
    private String uid;

    //endregion


这是我的文章分类类

@Data
@Entity
@Table(name = "article_categories", schema = "public")
public class ArticleCategory extends BaseEntity 

    //region Simple Properties

    @NotNull
    @Size(min = 2, message = "name should have at least 2 characters")
    @Column(name = "name")
    private String name;

    @NotNull
    @Size(min = 2, message = "slug should have at least 2 characters")
    @Column(name = "slug")
    private String slug;

    @NotNull
    @Size(min = 2, message = "description should have at least 2 characters")
    @Column(name = "description")
    private String description;

    //endregion

    //region Complex Properties
    @JsonIgnore
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "articleCategory", orphanRemoval = true)
    private List<Article> articleList;
    //endregion

这是我的系类

@Data
@Entity
@Table(name = "departments", schema = "public")
public class Department extends BaseEntity 

    //region Simple Properties
    @Size(min = 2,message = "name should have at least 2 characters")
    @Column(name = "name")
    private String name;

    @Enumerated(EnumType.STRING)
    @Column(name = "status")
    private DepartmentStatusEnum status;

    //endregion

    //region Complex Properties

    @JsonIgnore
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "department", orphanRemoval = true)
    private List<Article> articleList;

这是我的服务

@Override
    public ArticleRequest saveArticle(ArticleRequest articleRequest) 
        if (articleRequest == null) 
            throw new InvalidEntityException(CustomErrorMessage.ARTICLE_CAN_NOT_BE_NULL.getMessage(), CustomErrorCodes.ARTICLE_NOT_VALID);
        
        articleRequest.setUid(UUID.randomUUID().toString());
        Article saveArticle=ArticleRequest.toEntity(articleRequest);// null exception
        Article newArticle = articleRepository.save(saveArticle);
        ArticleRequest newArticleRequest = ArticleRequest.fromEntity(newArticle);
        return newArticleRequest;
    

那么我该如何保存我的文章实体并以正确的方式传递文章类别和部门的 uid!

提前致谢。

【问题讨论】:

您可以在设置它们的 id 之前创建一个 ArticleCategory 和 Departement 的新实例。试试看***.com/questions/218384/… 这能回答你的问题吗? What is a NullPointerException, and how do I fix it? 【参考方案1】:

给出的信息不是一个好主意,你应该在你的问题中跳过课程部门和文章

可能你的问题在这里:

article.getArticleCategory().setUid(articleRequest.getUid()); article.getDepartment().setUid(articleRequest.getUid());

你应该设置你的文章的ArticleCategory和部门,创建一个新的对象并设置它们。 我认为解决方案是将这些行替换为:

article.setArticleCategory(new ArticleCategory());
article.getArticleCategory().setUid(articleRequest.getArticleCategoryUid()); 
article.setDepartment(new Department());
article.getDepartment().setUid(articleRequest.getDepartmentUid());


TransientPropertyValueException:对象引用了一个未保存的瞬态实例 - 在刷新之前保存瞬态实例

您应该在 Article 对象的持久化之前将 Properties 值保存或持久化在数据库(实例)中,因此代码将是这样的:

ArticleCategory ac = new ArticleCategory();
ac.setUid(articleRequest.getArticleCategoryUid());// if you need you can set the other properties of the ac , get them from the articleRequest
articleCategoryRepository.save(ac);
Department dep = new Department();
dep.setUid(articleRequest.getDepartmentUid());
departmentRepository.save(dep);
article.setArticleCategory(ac);
article.setDepartment(dep);
articleRepository.save(article);

【讨论】:

我更新了我的帖子(添加文章类别和部门类)现在我有一个错误:TransientPropertyValueException:对象引用了一个未保存的瞬态实例 - 在刷新之前保存瞬态实例 ***.com/questions/2302802/…@NevergiveupNever 我以前看过这篇文章,但对我没有帮助!!!当我使用 id 时保存有效,但是当我选择使用 uid 归档时,如果我更改级联类型,它会显示此错误,我在数据库中找到重复项!任何建议我都会感激的 @NevergiveupNever answer updated ,这是你在找什么?【参考方案2】:
article.getArticleCategory() //this gives you the NPE

在调用 articleCategory 的 getter 方法之前初始化它。

例如:

article.setArticleCategory(new ArticleCategory());

部门也一样。在调用getDepartment()之前初始化部门对象

【讨论】:

我进行了初始化,但我有新的错误提示 TransientPropertyValueException:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例

以上是关于如何在避免复杂对象的情况下将 dto 映射到实体的主要内容,如果未能解决你的问题,请参考以下文章

数据传输对象 (dto):Hibernate 重复表映射

如何将休眠查询的结果映射到 DTO 对象?

如何在 Java 中将双向实体映射到 DTO

推荐一个 Java 实体映射工具 MapStruct

如何将DTO映射到多个实体?

Java实体映射工具MapStruct详解