Search code examples
javaspring-bootspring-data-jpadtomapstruct

adding new entity in database through PostMapping


I have two entities

@Entity
@Table(name = "categories")
public class Category {
    @Getter
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "category_id", unique = true, nullable = false)
    private long categoryId;

    @Getter @Setter
    @ManyToMany(mappedBy = "categories", cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    })
    List<Product> products;

    @Getter @Setter
    @Column(name = "category_name", nullable = false, unique = true)
    private String categoryName;

And

@Entity
@Table(name = "products")
public class Product {
    @Getter
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "product_id", unique = true, nullable = false)
    private long productId;

    @Getter @Setter
    @Column(name = "price")
    private float price;

    @Getter @Setter
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "categories_product")
    private List<Category> categories;

    @Getter @Setter
    @Column(name = "product_code", unique = true, nullable = false)
    private String productCode;

    @Getter @Setter
    @Column(name = "product_name", nullable = false)
    private String productName;

    @Getter @Setter
    @Column(name = "description", nullable = false)
    private String description;

    @Getter @Setter
    @Column(name = "short_description", nullable = false)
    private String shortDescription;
}

I`m using MapStruct for DTO. When I want to add new product through controller I get the following error: org.hibernate.PropertyValueException: not-null property references a null or transient value : com.project.shop.models.Category.categoryName

As I understand, hibernate tries to create a new Category, when I want it to use an already existing one in database.

CategoryDTO:

@Getter
@Setter
public class CategoryDto {
    private long categoryId;
    private String categoryName;
    private boolean categoryActive;
}

ProductDTO:

@Getter
@Setter
public class ProductDto {
    private String productName;
    private String productCode;
    private float price;
    private String shortDescription;
    private String description;

    private List<CategoryDto> categories;
}

CategoryMapper:

@Mapper(componentModel = "spring")
public interface CategoryMapper {
    CategoryDto toDto(Category category);
    List<CategoryDto> toDtos(List<Category> categories);
    List<Category> toModels(List<CategoryDto> categoryDtos);
    Category toModel(CategoryDto categoryDto);
}

ProductMapper:

@Mapper(uses = {CategoryMapper.class},
        componentModel = "spring")
public interface ProductMapper {
    ProductDto toDto(Product product);
    List<ProductDto> toDtos(List<Product> products);
    List<Product> toModels(List<ProductDto> productDtos);
    Product toModel(ProductDto productDto);
}

Controller:

@PostMapping("/product")
public ResponseEntity<ProductDto> create(@RequestBody ProductDto productDto) {
    productService.save(productMapper.toModel(productDto));
    return ResponseEntity.status(HttpStatus.CREATED).body(productDto);
}

productService.save:

public void save(Product product) {
    productRepository.save(product);
}

Solution

  • If you look at the identity, nullable = false option appears to be defined option is defined.

    @Entity
    @Table(name = "categories")
    public class Category {
        
        ....
    
        @Getter @Setter
        @Column(name = "category_name", nullable = false, unique = true)
        private String categoryName;
    

    I think it would be good to look for the categoryName column value of CategoryDto first.