英文:
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 = "Product name is required")
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 = "Brand name is required")
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 = "Category name is required")
private String name;
@ManyToMany(mappedBy = "categories")
private List<Product> products;
And my Product Object that used to input create new 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
}
答案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<String, Brand> {
@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 = "brand_id", 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论