Сопоставление объекта Hibernate с помощью mapstruct - PullRequest
0 голосов
/ 02 июля 2018

Я новичок в этих технологиях, поэтому заранее прошу прощения.

Я использую в своем приложении springboot, Spring JPA, hibernate и mapstruct.

Я вставил родительскую запись с идентификатором '1'. Теперь, когда я пытаюсь вставить дочерний элемент для родителя, он выдает

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед сброс: вложенным исключением является java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед промывка:

Итак, как мне дать команду mapstruct сгенерировать код, чтобы он выбирал родительскую запись, вставку создания нового объекта, который, как мне кажется, вызывает вышеуказанную проблему.

    /**
     * Mapper class for the entity {@link Child} and its corresponding data transfer object {@link ParentTlDTO}.
     */
    @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN, uses = {ParentMapper.class})
    public interface ChildMapper extends EntityMapper<ChildDTO, Child> {

      @Mapping(source = "parent.id", target = "parentId")
      @Override
      ChildDTO toDto(Child child);

      @Mapping(source = "parentId", target = "parent.id")
      @Override
      Child toEntity(ChildDTO childDTO);

      default Child fromId(String id) {
        if (id == null) {
          return null;
        }
        Child Child = new Child();
        Child.setId(id);
        return Child;
      }
    }


    @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {})
    public interface ParentMapper extends EntityMapper<ParentDTO, Parent> {

      @Mapping(target = "childs", ignore = true)
      Parent toEntity(ParentDTO parentDTO);

      default Parent fromId(String id) {
        if (id == null) {
          return null;
        }
        Parent parent = new Parent();
        parent.setId(id);
        return parent;
      }
    }

----- Классы моделей ----

 @ApiModel(description = "Child holds the descriptive information of currencies translated into various languages supported by the application.")
 @Entity
 @Table(name = "child")
 @Cache(usage = CacheConparentStrategy.NONSTRICT_READ_WRITE)
 @Document(indexName = "child")
 public class Child extends AbstractEntity {

   private static final long serialVersionUID = 6419585975683709213L;

   /**
    * Identifier of the descriptive information.
    */
   @Id
   @GeneratedValue(generator = "system-uuid")
   @GenericGenerator(name = "system-uuid", strategy = "uuid2")
   @Column(name = "id", columnDefinition = "CHAR", length = 36, nullable = false)
   private String id;

   /**
    * Name of the parent.
    */
   @NotNull
   @Size(min = 3, max = 120)
   @Column(name = "entity_name", columnDefinition = "VARCHAR", length = 120, nullable = false)
   private String entityName;

   /**
    * Brief description about the parent.
    */
   @Size(max = 256)
   @Column(name = "entity_desc", columnDefinition = "VARCHAR", length = 256, nullable = true)
   private String entityDesc;

   @ManyToOne(fetch = FetchType.EAGER)
   @JsonIgnoreProperties("childs")
   private Parent parent;

   @ManyToOne(fetch = FetchType.EAGER)
   private Language language;

   /**
    * Callback method that is triggered before persisting the descriptive information of the parent where default
    * values are set for mandatory attributes and values are massaged without compromising on data integrity.
    */
   @PrePersist
   public void setDefaultValues() {
     this.entityName = StringUtils.capitalize(StringUtils.trim(this.entityName));
     this.entityDesc = StringUtils.trim(this.entityDesc);
   }

   /**
    * @return the id
    */
   public String getId() {
     return id;
   }

   /**
    * @param id the id to set
    */
   public void setId(String id) {
     this.id = id;
   }

   /**
    *
    * @return
    */
   public String getEntityName() {
     return entityName;
   }

   /**
    *
    * @param entityName
    * @return
    */
   public Child entityName(String entityName) {
     this.entityName = entityName;
     return this;
   }

   /**
    *
    * @param entityName
    */
   public void setEntityName(String entityName) {
     this.entityName = entityName;
   }

   /**
    *
    * @return
    */
   public String getEntityDesc() {
     return entityDesc;
   }

   /**
    *
    * @param entityDesc
    * @return
    */
   public Child entityDesc(String entityDesc) {
     this.entityDesc = entityDesc;
     return this;
   }

   /**
    *
    * @param entityDesc
    */
   public void setEntityDesc(String entityDesc) {
     this.entityDesc = entityDesc;
   }

   /**
    *
    * @return
    */
   public Parent getParent() {
     return parent;
   }

   /**
    *
    * @param parent
    * @return
    */
   public Child parent(Parent parent) {
     this.parent = parent;
     return this;
   }

   /**
    *
    * @param parent
    */
   public void setParent(Parent parent) {
     this.parent = parent;
   }

   public Language getLanguage() {
     return language;
   }

   public Child language(Language language) {
     this.language = language;
     return this;
   }

   public void setLanguage(Language language) {
     this.language = language;
   }

   @Override
   public boolean equals(Object o) {
     if (this == o) {
       return true;
     }
     if (o == null || getClass() != o.getClass()) {
       return false;
     }
     Child child = (Child) o;
     if (child.getId() == null || getId() == null) {
       return false;
     }
     return Objects.equals(getId(), child.getId());
   }

   @Override
   public int hashCode() {
     return Objects.hashCode(getId());
   }

   @Override
   public String toString() {
     return "Child{"
       + "id=" + getId()
       + ", entityName='" + getEntityName() + "'"
       + ", entityDesc='" + getEntityDesc() + "'"
       + "}";
   }
 }



@ApiModel(description = "Parent class holds the primary definition of a parent such as parent code, parent symbol etc.")
@Entity
@Table(name = "parent")
@Cache(usage = CacheConparentStrategy.NONSTRICT_READ_WRITE)
@Document(indexName = "parent")
public class Parent extends AbstractEffectiveEntity {

  private static final long serialVersionUID = 5671242791959882244L;

  /**
   * Identifier of the parent.
   */
  @Id
  @GeneratedValue(generator = "system-uuid")
  @GenericGenerator(name = "system-uuid", strategy = "uuid2")
  @Column(name = "id", columnDefinition = "CHAR", length = 36, nullable = false)
  private String id;

  /**
   * Code assigned to the parent.
   */
  @NotNull
  @Size(min = 2, max = 5)
  @Column(name = "parent_code", columnDefinition = "CHAR", length = 5, nullable = false, updatable = false)
  private String parentCode;

  /**
   * Symbol associated with the parent.
   */
  @NotNull
  @Size(min = 2, max = 10)
  @Column(name = "parent_symbol", columnDefinition = "VARCHAR", length = 10, nullable = false, updatable = false)
  private String parentSymbol;

  /**
   * Flag to denote whether parent is installed in the product suite to support converting it to other currencies.
   */
  @Column(name = "installed_flag", columnDefinition = "CHAR", length = 1, nullable = false)
  private String installedFlag;

  /**
   * Bi-directional OneToMany is not the best and most efficient way to model a one to many relationship. But in this
   * case we prefer to keep a collection of descriptive information (children) in Parent (parent) to retrieve the
   * descriptive information via Parent, since the count of descriptive information shall be less (depending on the
   * number of languages supported in the application for translation). Limitations of this are (1) inability to limit
   * the number of descriptive information (Child) loaded and thus no support for pagination, (2) inability to sort
   * descriptive information since @OrderColumn annotation can be expensive.
   */
  @OneToMany(mappedBy = "parent")

  @Cache(usage = CacheConparentStrategy.NONSTRICT_READ_WRITE)
  private Set<Child> childs = new HashSet<>();

  /**
   * Callback method that is triggered before persisting the primary information of the parent where default values
   * are set for mandatory attributes and values of key attributes are massaged without compromising on data integrity.
   */
  @PrePersist
  public void setDefaultValues() {
    this.parentCode = StringUtils.upperCase(StringUtils.trim(this.parentCode));
    this.parentSymbol = StringUtils.trim(this.parentSymbol);
    this.installedFlag = StringUtils.upperCase(StringUtils.trim(this.installedFlag));
    if (StringUtils.isBlank(this.installedFlag)) {
      this.installedFlag = "N";
    }
  }

  /**
   * Returns the identifier of the parent.
   *
   * @return the parent identifier.
   */
  public String getId() {
    return id;
  }

  /**
   * Sets the identifier of the parent.
   *
   * @param id the identifier to be set for the parent.
   */
  public void setId(String id) {
    this.id = id;
  }

  /**
   * Returns the code associated with the parent.
   *
   * @return the parentCode
   */
  public String getParentCode() {
    return parentCode;
  }

  /**
   * Sets the code associated with the parent.
   *
   * @param parentCode the parentCode to set
   */
  public void setParentCode(String parentCode) {
    this.parentCode = parentCode;
  }

  /**
   * Sets the code of the parent in the Parent object.
   *
   * @param parentCode
   * @return parent the Parent object with parentCode set.
   */
  public Parent parentCode(String parentCode) {
    this.parentCode = parentCode;
    return this;
  }

  /**
   * Returns the symbol associated with the parent.
   *
   * @return the parentSymbol
   */
  public String getParentSymbol() {
    return parentSymbol;
  }

  /**
   * Sets the symbol associated with the parent.
   *
   * @param parentSymbol the parentSymbol to set
   */
  public void setParentSymbol(String parentSymbol) {
    this.parentSymbol = parentSymbol;
  }

  /**
   * Sets the symbol of the parent in the Parent object.
   *
   * @param parentSymbol
   * @return parent the Parent object with parentSymbol set.
   */
  public Parent parentSymbol(String parentSymbol) {
    this.parentSymbol = parentSymbol;
    return this;
  }

  /**
   * Return true if the parent is marked as installed in the product suite, otherwise false.
   *
   * @return the installedFlag
   */
  public String getInstalledFlag() {
    return installedFlag;
  }

  /**
   * Setter method to specify whether the parent is marked as installed in the product suite or not.
   *
   * @param installedFlag the installedFlag to set
   */
  public void setInstalledFlag(String installedFlag) {
    this.installedFlag = installedFlag;
  }

  /**
   * Sets the flag to denote whether the parent is installed in the product suite or note in the Parent object.
   *
   * @param installedFlag
   * @return the Parent object with installedFlag set.
   */
  public Parent installedFlag(String installedFlag) {
    this.installedFlag = installedFlag;
    return this;
  }

  /**
   *
   * @return
   */
  public Set<Child> getChilds() {
    return childs;
  }

  /**
   *
   * @param childs
   * @return
   */
  public Parent childs(Set<Child> childs) {
    this.childs = childs;
    return this;
  }

  /**
   *
   * @param child
   * @return
   */
  public Parent addChild(Child child) {
    this.childs.add(child);
    child.setParent(this);
    return this;
  }

  /**
   *
   * @param child
   * @return
   */
  public Parent removeChild(Child child) {
    this.childs.remove(child);
    child.setParent(null);
    return this;
  }

  /**
   *
   * @param childs
   */
  public void setChilds(Set<Child> childs) {
    this.childs = childs;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Parent parent = (Parent) o;
    if (parent.getId() == null || getId() == null) {
      return false;
    }
    return Objects.equals(getId(), parent.getId());
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(getId());
  }

  @Override
  public String toString() {
    return "Parent{" + "id=" + getId() + ", parentCode='" + getParentCode() + "'" + ", parentSymbol='"
      + getParentSymbol() + "'" + ", installedFlag='" + getInstalledFlag() + "'" + "}";
  }

}

1 Ответ

0 голосов
/ 05 июля 2018

То, что вы ищете, это, вероятно, что-то вроде Передача целевого типа сопоставления в пользовательские сопоставители или lookup-entity-by-id пример.

У вас может быть абстрактный маппер, куда вы можете добавить свой репозиторий и выполнить поиск вашей сущности. Или вы можете даже передать Репозиторий как параметр @Context.

Давайте предположим, что у вас есть ParentRepository и в нем у вас есть findById(String id)

Таким образом, вместо использования ParentMapper в вашем ChildMapper вы будете использовать ParentRepository. Затем MapStruct сгенерирует код, который выглядит следующим образом:

@Component
public class ChildMapperImpl implements ChildMapper {

    @Autowired
    private ParentRepository parentRepository;

    @Autowired
    private ChildFactory childFactory;


    @Override
    public Child toEntity(ChildDTO childDTO) {
        Child child = childFactory.create(childDTO);

        child.setParent(parentRepository.findById(childDTO.getParentId());
    }
}

Вам, конечно, нужно будет адаптировать ваше отображение к @Mapping(source = "parentId", target = "parent") по методу ChildMapper#toEntity.

Если вы хотите получить обновления для создания сущностей, вам нужно будет использовать Object Factory . Вам нужно будет поместить ChildFactory в дочерний маппер, также используется аннотация.

Например, чтобы вызвать ребенка для выполнения обновления, вы можете получить что-то вроде:

public class ChildFactory {

    protected final ChildRepository childRepository;

    public ChildFactory(ChildRepository childRepository) {
        this.childRepository = childRepository;
    }

    @ObjectFactory
    public Child create(ChildDTO childDTO) {
        Child child = childRepository.findById(childDTO.getId());
        return child == null ? new Child() : child;
    }
}
...