Column ‘brand_id’ cannot be null

huangapple go评论70阅读模式
英文:

Column 'brand_id' cannot be null

问题

我使用 Spring Boot / Hibernate / JPA 创建新的产品并且出现了'brand_id' 不能为空的错误我不知道为什么会出现这个错误有人可以解释一下我错在哪里吗

Product:

    @Entity
    public class Product {

	    @javax.persistence.Id
	    @GeneratedValue(strategy = GenerationType.IDENTITY)
	    private Long Id;
	    @NotBlank(message = "产品名称是必需的")
	    private String name;
	    private String image;
	    private String description;
	    private double price;
	    private int countInStock;
	    private double rating;
	    private int numReviews;

	    @ManyToOne(fetch = FetchType.EAGER)
	    @JoinColumn(name = "brand_id", updatable = false, nullable = false)
	    @JsonIgnore
	    private Brand brand;
	    @ManyToMany
	    @JoinTable(name = "product_category", joinColumns = @JoinColumn(name = "product_id"), inverseJoinColumns = @JoinColumn(name = "category_id"))
	    @JsonIgnore
	    private List<Category> categories;
}

Brand:

    @Entity
    public class Brand {

	    @javax.persistence.Id
	    @GeneratedValue(strategy = GenerationType.IDENTITY)
	    private Long Id;
	    @NotBlank(message = "品牌名称是必需的")
	    private String name;
	    @OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, mappedBy = "brand", orphanRemoval = true)
	    private List<Product> products;
}

Category:

    @Entity
    public class Category {

	    @javax.persistence.Id
	    @GeneratedValue(strategy = GenerationType.IDENTITY)
	    private Long Id;
	    @NotBlank(message = "类别名称是必需的")
	    private String name;
	    @ManyToMany(mappedBy = "categories")
	    private List<Product> products;
}

以下是我用于输入创建新产品的 Product 对象

    {
        "name": "test",
        "image": "/images/test.jpg",
        "description": "test",
        "brand_id": 4,
        "category_id": 1,
        "price": 99.99,
        "countInStock": 100,
        "rating": 4.5,
        "numReviews": 120
    }
英文:

I create new Product using Spring Boot / Hibernate / JPA and get Column 'brand_id' cannot be null error. I don't know why this error happen. Does anyone can explain where I was wrong?

Product:

@Entity
public class Product {
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
@NotBlank(message = &quot;Product name is required&quot;)
private String name;
private String image;
private String description;
private double price;
private int countInStock;
private double rating;
private int numReviews;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = &quot;brand_id&quot;, updatable = false, nullable = false)
@JsonIgnore
private Brand brand;
@ManyToMany
@JoinTable(name = &quot;product_category&quot;, joinColumns = @JoinColumn(name = &quot;product_id&quot;), inverseJoinColumns = @JoinColumn(name = &quot;category_id&quot;))
@JsonIgnore
private List&lt;Category&gt; categories;

Brand:

@Entity
public class Brand {
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
@NotBlank(message = &quot;Brand name is required&quot;)
private String name;
@OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, mappedBy = &quot;brand&quot;, orphanRemoval = true)
private List&lt;Product&gt; products;

Category:

@Entity
public class Category {
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
@NotBlank(message = &quot;Category name is required&quot;)
private String name;
@ManyToMany(mappedBy = &quot;categories&quot;)
private List&lt;Product&gt; products;

And my Product Object that used to input create new Product:

{
&quot;name&quot;: &quot;test&quot;,
&quot;image&quot;: &quot;/images/test.jpg&quot;,
&quot;description&quot;: &quot;test&quot;,
&quot;brand_id&quot;: 4,
&quot;category_id&quot;: 1,
&quot;price&quot;: 99.99,
&quot;countInStock&quot;: 100,
&quot;rating&quot;: 4.5,
&quot;numReviews&quot;: 120
}

答案1

得分: 2

在创建这个产品对象的任何地方,您必须为其提供一个品牌对象。
目前,您只提供了一个整数作为品牌。

您需要做的是从您的数据库中获取您正在引用的品牌对象,并在实例化新产品时将其包含在内。
可能最好和最简单的方法是使用EntityManager来获取对该品牌的引用。

在使用Spring时,获取EntityManager非常简单。

@PersistenceContext
private EntityManager entityManager;

现在,只需使用它来获取对目标品牌的引用。

Brand brand = entityManager.getReference(Brand.class, brand_id);

使用此品牌来实例化您的新Product,并将其插入数据库,而不会出现任何异常。


如何将此逻辑自动化到解组过程中

如果您在创建产品时总是想要使用此逻辑,您可以在构造函数中使用此逻辑。如果您只想在解组时使用此方法,以下是一个基于我最近编写的内容的示例。我正在使用XmlAdapter,但您还可以研究JSONAdapter类,它们应该以类似的方式工作。

创建您的适配器类。此类将用于将JSON解析为Java对象。

//再次说明,我在使用XmlAdapter,但使用JSONAdapter的思想应该类似
@Service
public class BrandIdXmlAdapter extends XmlAdapter<String, Brand> {

    @PersistenceContext
    private EntityManager entityManager;

    //v是要解组的字符串。
    //在我们的情况下,它将是brand_id字符串。
    @Override
    public Brand unmarshal(String v) {
        Brand brand = entityManager.getReference(Brand.class, v);
        return brand;
    }
}

还有可能覆盖marshal()方法,用于从POJO解析到XML/JSON。

唯一的问题是,为了能够使用PersistenceContext注解,此类必须是一个EJB。
我们将通过告诉Spring这是一个必要的服务来解决这个问题。

第一步是给适配器类添加Service注解(在上面的示例中完成)。

下一步是转到您想要将输入解组为POJO的位置(如果您将其作为请求接收,则为控制器;如果您将其从另一个服务请求,则为服务)并进行自动装配适配器。

@Autowired
private BrandIdXmlAdapter xmlAdapter;

接下来的步骤是创建将使用此适配器的解组器。

JAXBContext context = JAXBContext.newInstance(Product.class);
brandIdUnmarshaller = context.createUnmarshaller();
brandIdUnmarshaller.setAdapter(xmlAdapter);

现在在接收数据时,使用brandIdUnmarshaller.unmarshal()方法。

最后一步是在Product中注释您的Brand变量,告诉它在解析此特定变量时使用适配器。

public class Product {
    .
    .
    .
    //再次根据您的JSONAdapter找到正确的注解
    @XmlJavaTypeAdapter(BrandIdXmlAdapter.class)
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "brand_id", updatable = false, nullable = false)
    @JsonIgnore
    private Brand brand;
}

现在每次从JSON解析到Product时,您都会自动获得一个包含有效Brand对象的Product。

英文:

Wherever you are creating this Product object, you must provide it with a Brand object.
Currently, you are only providing it with an integer as a Brand.

What you need to do is fetch the brand object you are referring to from your DB and include it in the instantiation of your new Product.
Probably the best and simplest approach to this would be using the EntityManager to get a reference to that Brand.

Getting the EntityManager is very simple when using spring.

@PersistenceContext
private EntityManager entityManager;

Now, simply use it to get the reference to the target Brand.

Brand brand = entityManager.getReference(Brand.class , brand_id);

Use this brand to instantiate your new Product and insert it to the DB without any exceptions.


How to automate this logic into the unmarshalling process

If you are always going to want to use this logic when creating a Product, you can use this logic in the constructor. If you only want to use this method when unmarshalling, here is an example that is based of something I wrote recently. I am using an XmlAdapter, but there are also JSONAdapater classes you can look into and should work about the same way.

Create you adapter class. This class is going to be used to parse JSON to java object.

//Once again, I am using an XmlAdapter, but the idea should be similar with JSONAdapters
@Service
public class BrandIdXmlAdapter extends XmlAdapter&lt;String, Brand&gt; {
@PersistenceContext
private EntityManager entityManager;
//v is the String that is going to be unmarshalled.
//In our case, its going to be the brand_id String.
@Override
public Brand unmarshal(String v) {
Brand brand = entityManager.getReference(Brand.class, v);
return brand;
}

There is also a possibility to override the marshal() method for parsing from a POJO to XML/JSON.

The only problem here is that to be able to use use the PersistenceContext annotation, this class has to be an EJB.
We are going to workaround that by telling Spring this is a necessary service.

First step is to give the adapter class the Service annotation(Done in example above).

The next step is to go to where you would want to unmarshall the input into a POJO (either the controller if you receive it as a request or the service if you are going to request it from another service) and autowire the adapter

@Autowired
private BrandIdXmlAdapter xmlAdapter;

Next step is to create the unmarshaller that will use this adapter.

JAXBContext context = JAXBContext.newInstance(Product.class);
brandIdUnmarshaller = context.createUnmarshaller();
brandIdUnmarshaller.setAdapter(xmlAdapter);

Now when receiving the data, use the brandIdUnmarshaller.unmarhsall() method.

Last step is to annotate your Brand variable in Product to tell it to use the adapter when parsing this specific variable.

public class Product {
.
.
.
//again, find the right annotation according to your JSONAdapter
@XmlJavaTypeAdapter(BrandIdXmlAdapter.class)
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = &quot;brand_id&quot;, updatable = false, nullable = false)
@JsonIgnore
private Brand brand;
}

Now everytime you parse from JSON to Product, you will automatically get a Product that contains a valid Brand object.

huangapple
  • 本文由 发表于 2020年10月12日 17:45:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/64315401.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定