简体   繁体   中英

OneToMany returns null in response on JoinColumn from target Entity even though value was inserted in database - Spring boot JPA

I have two Models/Entities that are combined with @OneToMany, saving PK from one table to other as Foreign key. I want to get that newly updated foreign key in response aswell.

This is my code

CategoryModel

@Data
@AllArgsConstructor
@Getter
@Builder
public class CategoryModel {
    
    public String categoryName;
    public List<BeveragesModel> beverages;
    
}

BeveragesModel

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BeveragesModel {
    
    public Integer categoryID;
    public String name;
    public double price;
    
    
}

Entities of the models currently have the same fields as models

BeveragesEntity

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "BEVERAGES")
@ToString
@Builder
public class BeveragesEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    public Integer id;
    @Column(name = "CATEGORY_ID")
    public Integer categoryId;
    @Column(name = "NAME")
    public String name;
    @Column(name = "PRICE")
    public double price;
    
    @ManyToOne
    public void setCategoryId(Integer id) {
        this.categoryId=id;
    }
    
}

CategoryEntity

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "CATEGORY")
public class CategoryEntity {

    // TO DO kako dobiti na front zapis iz BEVERAGES tablice za CATEGORY_ID kojeg spremam...
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    public Integer id;
    @Column(name = "CATEGORY_NAME")
    public String categoryName;
    @Column(name = "BEVERAGES")
    @OneToMany(targetEntity = BeveragesEntity.class, cascade = CascadeType.ALL)
    @JoinColumn(name = "CATEGORY_ID", referencedColumnName = "id")
    List<BeveragesEntity> beverages;
}

Controller is only calling service

@RestController
@CrossOrigin(origins = "*")
public class CategoryController {

    
    @Autowired
    CategoryService categoryService;

    @RequestMapping(path = "/addCategory", method = RequestMethod.PUT)
    public CategoryEntity addCategory(@RequestBody CategoryRequest request) {
        return categoryService.addCategory(request);
    }
    
}

CategoryService is currently rebuilding objects from models to entities

@Service
public class CategoryServiceImpl implements CategoryService {
    
    @Autowired
    CategoryRepository categoryRepository;
    
    public CategoryEntity addCategory(CategoryRequest request) {
        
        List<BeveragesModel> beveragesModel = request.getCategory().getBeverages();
        List<BeveragesEntity> beveragesEnties = new ArrayList<>();
        if(!beveragesModel.isEmpty()) {
            for(BeveragesModel model : beveragesModel)  {
                BeveragesEntity beveragesEntity = BeveragesEntity.builder()
                        .name(model.getName())
                        .price(model.getPrice())
                        .build();
                beveragesEnties.add(beveragesEntity);
                
            }
        }
        
        CategoryEntity categoryEntity = CategoryEntity.builder()
                .categoryName(request.getCategory().getCategoryName())
                .beverages(beveragesEnties)
                .build();
        
        return categoryRepository.save(categoryEntity);
    }

Request is

    { 
    "category" : {
        "categoryName": "Alkoholna pića",
        "beverages": [
            {
                "name":"Ozujsko",
                "price":16.0
            },
            {
                "name":"Stella Artois",
                "price":18.0
            }
        ]
    }
}

And this is response

    {
    "id": 1,
    "categoryName": "Alkoholna pića",
    "beverages": [
        {
            "id": 1,
            "categoryId": null,
            "name": "Ozujsko",
            "price": 16.0
        },
        {
            "id": 2,
            "categoryId": null,
            "name": "Stella Artois",
            "price": 18.0
        }
    ]
}

I want to get categoryId from objeck beverages in response from backend, but in response I get null. Howerver in my H2 database in BEVERAGES table I can see that categoryId is correctly saved with the ID from CATEGORY table (in this case both of this records have CATEGORY_ID = 1)

Would be greatfull if some one would help me out...

Your issue stems from BeverageEntity having a categoryId rather than a CategoryEntity. Normal bidirectional relationship would be as follows:

    // CategoryEntity
    @OneToMany(cascade = CascadeType.ALL)
    List<BeveragesEntity> beverages;

    // BeverageEntity
    @ManyToOne
    @JoinColumn(name = "CATEGORY_ID")
    private CategoryEntity category;

Before saving, as well as setting beverages collection into category you should also ensure each beverage has a category set. Here's a tweak to your service method with comments highlighting this.

    public CategoryEntity addCategory(CategoryRequest request) {

        List<BeveragesModel> beveragesModel = request.getCategory().getBeverages();
        
        CategoryEntity categoryEntity = CategoryEntity.builder()
                .categoryName(request.getCategory().getCategoryName())
                .build();

        List<BeveragesEntity> beveragesEnties = new ArrayList<>();
        if (!beveragesModel.isEmpty()) {
            for (BeveragesModel model : beveragesModel) {
                BeveragesEntity beveragesEntity = BeveragesEntity.builder()
                        .name(model.getName())
                        .price(model.getPrice())
                        .category(categoryEntity) // set category in each beverage
                        .build();
                beveragesEnties.add(beveragesEntity);
            }
        }

        categoryEntity.setBeverages(beveragesEnties); // set beverages in category

        return categoryRepository.save(categoryEntity);
    }

As you are returning the identities themselves from the controller there is a circular dependency between CategoryEntity and BeveragesEntity so you need to prevent this. In your case you want to include the categoryId in your beverages json objects so you can do this by adding the following annotations to the category field in BeveragesEntity. I am not sure of how the client is handling the data returned, but including the categoryId is just duplicating the id that already in the category json object

    @JsonProperty("categoryId")
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = true)
    @ManyToOne
    @JoinColumn(name = "CATEGORY_ID")
    private CategoryEntity category;

I implemented your code and made the above changes above and the response back from the controller was as follows

{
    "id": 1,
    "categoryName": "Alkoholna pica",
    "beverages": [
        {
            "id": 2,
            "name": "Ozujsko",
            "price": 16.0,
            "categoryId": 1
        },
        {
            "id": 3,
            "name": "Stella Artois",
            "price": 18.0,
            "categoryId": 1
        }
    ]
}

Lastly I would avoid using Lombok with Entities, particularly as @Data will autogenerate equals and hashcode methods and you want to ensure they are implemented correctly as you are using database generated identifiers article here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM