bharathi
bharathi

Reputation: 6271

java.lang.StackOverflowError(Cyclic Reference) with JPA and Mapstruct

I am new to mapstruct. I am trying to map the Entity object(bidirectional) to DTO object using Mapstruct. I am able to retrieve the entity object using JPA repository and when I try to map the same to DTo object I am getting java.lang.StackOverflowError.

Code:

@Entity
@Table(name = "product_category")
@Getter
@Setter
@ToString(exclude = {"products"}) 

public class ProductCategory extends BaseModel<String> {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "FARM_PRODUCT_CATEGORY_GENERATOR")
    @SequenceGenerator(name = "FARM_PRODUCT_CATEGORY_GENERATOR", sequenceName = "FARM_PRODUCT_CATEGORY_GENERATOR_SEQ", allocationSize = 1)
    @Column(name = "farm_product_category_id")
    public Long productCategoryId;

    @Column(name="category_name")
    public String categoryName;

    @OneToMany(mappedBy = "productCategory", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    public List<FarmProducts> products;

}

@Entity
@Table(name="farm_products")
@Getter
@Setter
@ToString(exclude= "productCategory")
public class FarmProducts extends BaseModel<String> {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "FARM_PRODUCT_GENERATOR")
    @SequenceGenerator(name = "FARM_PRODUCT_GENERATOR", sequenceName = "FARM_PRODUCT_GENERATOR_SEQ", allocationSize = 1)
    @Column(name = "farm_product_id")
    public Long productId;

    @ManyToOne
    @JoinColumn(name = "farm_product_category_id")
    public ProductCategory productCategory;

    @Column(name = "product_name")
    public String product;

    @Column(name = "is_deleted")
    public String isDeleted;

}
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseModel<U> {

    @CreatedBy
    protected U createdBy;

    @CreatedDate
    @Temporal(TemporalType.TIMESTAMP)
    protected Date creationDate;

    @LastModifiedBy
    protected U lastModifiedBy;

    @LastModifiedDate
    @Temporal(TemporalType.TIMESTAMP)
    protected Date lastModifiedDate;

}

DTO class:

@Data
public class AuditDTO implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -775795624442449273L;

    public String createdBy;

    public Date creationDate;

    public Date lastModifiedBy;

    public Date lastModifiedDate;


}

@Data
public class FarmProductCategoryDTO extends AuditDTO implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 7016941232710915827L;


    public Long productCategoryId;

    public List<FarmProductDTO> products;


}

@Data
public class FarmProductDTO extends AuditDTO implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 6164359760527049606L;

    public Long productId;

    public FarmProductCategoryDTO farmProductCategoryVO;

    public String product;

    public String isDeleted;



}

Repository class:

@Repository
public interface FarmProductCategoryRepository  extends JpaRepository<ProductCategory, Long>{

    List<ProductCategory> findAll();

}

Service class:

@Service
public class FarmProductServiceImpl implements FarmProductService {

    @Autowired
    FarmProductCategoryRepository farmProductRepository;

    @Autowired
    ProductCategoryMapper productCategoryMapper;


    /**
     * 
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
    public List<FarmProductCategoryDTO> farmProductList() {
        List<FarmProductCategoryDTO>  farmCategories = null;
         List<ProductCategory> productCategory = farmProductRepository.findAll();
         System.out.println("farmProduct"+productCategory.size());

         farmCategories = productCategoryMapper.productCategoryToProductCategoryDTO(productCategory);

        return farmCategories;
    }
}

Mapper class:

@Mapper(componentModel = "spring",uses=ProductMapper.class)
public interface ProductCategoryMapper {
    @Mapping(source = "products", target = "products")
    FarmProductCategoryDTO productCategoryToProductCategoryDTO(ProductCategory productCategory);

    List<FarmProductCategoryDTO> productCategoryToProductCategoryDTO(List<ProductCategory> productCategory);

    ProductCategory FarmProductCategoryDTOToProductCategory(FarmProductCategoryDTO farmProductCategoryDTO);
}

@Mapper(componentModel="spring")

public interface ProductMapper  {

    /*
     * @Mappings({
     * 
     * @Mapping(source="productCategory",target = "farmProductCategoryVO",
     * ignore=true),
     * 
     * })
     */


     FarmProductDTO farmProductToProductDto(FarmProducts farmProducts);

     List<FarmProductDTO> farmProductsToFarmProductsDtos(List<FarmProducts> callRecords);


}

I have bidirectional mapping.

When I try to fetch the all the category from DB works fine. and when I try to map the same to DTO to object using mapstruct, I am getting below error.

java.lang.StackOverflowError
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductsToFarmProductsDtos(ProductMapperImpl.java:42)
at com.company.farmer.mapper.dto.ProductMapperImpl.productCategoryToFarmProductCategoryDTO(ProductMapperImpl.java:58)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductToProductDto(ProductMapperImpl.java:29)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductsToFarmProductsDtos(ProductMapperImpl.java:44)
at com.company.farmer.mapper.dto.ProductMapperImpl.productCategoryToFarmProductCategoryDTO(ProductMapperImpl.java:58)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductToProductDto(ProductMapperImpl.java:29)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductsToFarmProductsDtos(ProductMapperImpl.java:44)
at com.company.farmer.mapper.dto.ProductMapperImpl.productCategoryToFarmProductCategoryDTO(ProductMapperImpl.java:58)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductToProductDto(ProductMapperImpl.java:29)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductsToFarmProductsDtos(ProductMapperImpl.java:44)
at com.company.farmer.mapper.dto.ProductMapperImpl.productCategoryToFarmProductCategoryDTO(ProductMapperImpl.java:58)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductToProductDto(ProductMapperImpl.java:29)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductsToFarmProductsDtos(ProductMapperImpl.java:44)
at com.company.farmer.mapper.dto.ProductMapperImpl.productCategoryToFarmProductCategoryDTO(ProductMapperImpl.java:58)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductToProductDto(ProductMapperImpl.java:29)
at com.company.farmer.mapper.dto.ProductMapperImpl.farmProductsToFarmProductsDtos(ProductMapperImpl.java:44)

Upvotes: 0

Views: 1133

Answers (1)

Filip
Filip

Reputation: 21461

When mapping with cycles you would have to break that cycle somehow:

Using @Context

Have a look at the mapstruct-mapping-with-cycles example.

Ignore on some level

Like in your commented out code.

@Mapper
public interface ProductMapper  {

    @Mapping(source="productCategory",target = "farmProductCategoryVO", ignore=true)
    FarmProductDTO farmProductToProductDto(FarmProducts farmProducts);

    List<FarmProductDTO> farmProductsToFarmProductsDtos(List<FarmProducts> callRecords);
}

Upvotes: 2

Related Questions