API中的@OneToMany关系引发StackOverflow错误。

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

API throwing StackOverflow Error with a @OneToMany Relationship

问题

我对我的MYSQL数据库进行了一些更改,使得一个产品可以与多个图像关联(一对多关系)。然而,自从做出这些更改以来,我看到了一些奇怪的行为,程序抛出了StackOverflow Exception异常。似乎程序在崩溃之前陷入了一个连续循环中。

我的模型类结构如下:

@Entity
@Table(name="products")
public class Products {
    // 构造函数、属性等
    
    @OneToMany(mappedBy = "product")
    private List<ImageModel> imageModel;

    // Getters & Setters
}

@Entity
@Table(name = "image_table")
public class ImageModel {
    // 构造函数、属性等
    
    @ManyToOne
    @JoinColumn(name="product_id")
    private Products product;
    
    // Getters & Setters
}

当我添加产品时,以下代码执行并且似乎一切都按预期添加:

@PostMapping(value = "imageUploadMultiple")
public ResponseEntity<ImageResponse> addProductAndImages(@RequestParam("imageFiles") MultipartFile[] files, @RequestParam("productName") String productName, @RequestParam("productDescription") String productDescription, @RequestParam("productPrice") String productPrice, @RequestParam("categoryId") String categoryId) throws IOException {
    // 需要先保存产品,然后获取ID,然后将所有图像与产品ID一起保存
    
    Products products = productService.addProduct(productName,  productDescription,  productPrice,  categoryId);

    Arrays.asList(files).stream().forEach(file -> {
        ImageModel img = null;
        try {
            img = new ImageModel(file.getOriginalFilename(), file.getContentType(), compressBytes(file.getBytes()), products);
            imageRepository.save(img);
        } catch (IOException e) {
            e.printStackTrace();
        }
    });

    return ResponseEntity.ok().build();
}

然而,当我调用该端点以根据特定的categoryId获取所有产品时,问题就出现了:

@RequestMapping(value = "getProductsByCategory", produces = MediaType.APPLICATION_JSON_VALUE)
public List<Products> getProductsByCategory(@RequestBody HashMap<String, String> request) {
    String category_id = request.get("cat_id");
    List<Products> list = productService.getProductsByCategory(category_id);
    return list;
}

然后,这将调用服务类,然后调用存储库代码:

@Query("Select pro FROM Products pro WHERE pro.category_id=:cat_id")
List<Products> getByCategoryId(@Param("cat_id")String cat_id);

在调试模式下运行应用程序时,我获得了以下数据(此时该特定categoryID仅有一个产品):

"ImageModel"的类型为PersistentBag。当我深入挖掘时,我会得到映射到特定产品的图像。在这个实例中,一个产品有4个产品图像。当我深入挖掘时,我注意到出现了连续循环。

错误信息如下:

java.lang.StackOverflowError: null
	at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_251]
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756) ~[na:1.8.0_251]
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_251]
	...
	Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)

错误不断地重复,但我没有贴出完整的错误信息。

我真的很难理解出了什么问题!!

英文:

I have made some changes to my MYSQL database so that a Product can have multiple Images associated with it (One to Many), however since making this change I am seeing some weird behaviour and the program throws a StackOverflow Exception. It seems as if the program is stuck in a continuous loop before crashing and throwing the error.

My model classes are structured as follows:

@Entity
@Table(name=&quot;products&quot;)
public class Products {
	
	public Products(String name, String price, String added_on, String category_id, String image_name, String description, List&lt;ImageModel&gt; imageModel) {
		super();
		this.name = name;
		this.price = price;
		this.added_on = added_on;
		this.category_id = category_id;
		this.image_name = image_name;
		this.description = description;
		this.imageModel = imageModel;
	}
	
	public Products(String name, String price, String added_on, String category_id, String description) {
		super();
		this.name = name;
		this.price = price;
		this.added_on = added_on;
		this.category_id = category_id;
		this.description = description;
	}
	
	public Products() {}
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;
	private String name;
	private String price;
	private String added_on;
	private String category_id;
	private String image_name;
	private String description;
	private String image_id;
	
    @ManyToOne(optional=false)
    @JoinColumn(name = &quot;category_id&quot;, insertable=false, updatable=false)
	private Category category;
    
    @OneToMany(mappedBy = &quot;product&quot;)
    private List&lt;ImageModel&gt; imageModel;

    // Getters &amp; Setters

This class is then linked to ModelImage as follows:

@Entity
@Table(name = &quot;image_table&quot;)
public class ImageModel {

public ImageModel() {
	super();
}

public ImageModel(String name, String type, byte[] picByte, Products product) {
	this.name = name;
	this.type = type;
	this.picByte = picByte;
	this.product = product;
}

public ImageModel(String name, String type, byte[] picByte) {
	this.name = name;
	this.type = type;
	this.picByte = picByte;
}


@Id
@Column(name = &quot;id&quot;)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = &quot;name&quot;)
private String name;

@Column(name = &quot;type&quot;)
private String type;

// image bytes can have large lengths so we specify a value
// which is more than the default length for picByte column
@Column(name = &quot;picByte&quot;, length = 10000)
private byte[] picByte;

@ManyToOne
@JoinColumn(name=&quot;product_id&quot;)
private Products product;

// Getters Setters

When I add a product, the following code executes and seems to add everything as expected:

@PostMapping(value = &quot;imageUploadMultiple&quot;)
public ResponseEntity&lt;ImageResponse&gt; addProductAndImages(@RequestParam(&quot;imageFiles&quot;) MultipartFile[] files, @RequestParam(&quot;productName&quot;) String productName, @RequestParam(&quot;productDescription&quot;) String productDescription, @RequestParam(&quot;productPrice&quot;) String productPrice, @RequestParam(&quot;categoryId&quot;) String categoryId) throws IOException {		
	// Need to save product first then get the id and save all images with the productId
	
	Products products = productService.addProduct(productName,  productDescription,  productPrice,  categoryId);

	
	Arrays.asList(files).stream().forEach(file -&gt; {
		ImageModel img = null;
		try {
			img = new ImageModel(file.getOriginalFilename(), file.getContentType(), compressBytes(file.getBytes()), products);
			imageRepository.save(img);
		} catch (IOException e) {
			e.printStackTrace();
		}			
	});
	

	return ResponseEntity.ok().build();
}

This endpoint accepts multiple images and form data which corresponds to the image(Product Details etc)

However, the problem occurs when I call the endpoint to get all products based on a particular categoryId:

	@RequestMapping(value = &quot;getProductsByCategory&quot;, produces = MediaType.APPLICATION_JSON_VALUE)
public List&lt;Products&gt; getProductsByCategory(@RequestBody HashMap&lt;String, String&gt; request) {
	String category_id = request.get(&quot;cat_id&quot;);
	List&lt;Products&gt; list = productService.getProductsByCategory(category_id);
	return list;
}

This then calls into the service class which then calls the repository code:

	@Query(&quot;Select pro FROM Products pro WHERE pro.category_id=:cat_id&quot;)
List&lt;Products&gt; getByCategoryId(@Param(&quot;cat_id&quot;)String cat_id);

When I run the app in debug mode, I get the following data (At this time there is only one product for that particular categoryID):

API中的@OneToMany关系引发StackOverflow错误。

Notice how 'ImageModel' is of a PersistentBag type. When i dig deeper into this I get the mapped images to the particular product. In this instance there is 4 product images for the product. When i dig even deeper I notice there is just a continuous loop:

API中的@OneToMany关系引发StackOverflow错误。

API中的@OneToMany关系引发StackOverflow错误。

The error is as follows

java.lang.StackOverflowError: null
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_251]
at java.lang.ClassLoader.defineClass(ClassLoader.java:756) ~[na:1.8.0_251]
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_251]
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) ~[na:1.8.0_251]
at java.net.URLClassLoader.access$100(URLClassLoader.java:74) ~[na:1.8.0_251]
at java.net.URLClassLoader$1.run(URLClassLoader.java:369) ~[na:1.8.0_251]

Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.youtube.ecommerce.model.Products[&quot;imageModel&quot;]-&gt;org.hibernate.collection.internal.PersistentBag[0]-&gt;com.youtube.ecommerce.model.ImageModel[&quot;product&quot;]-&gt;com.youtube.ecommerce.model.Products[&quot;imageModel&quot;]-&gt;org.hibernate.collection.internal.PersistentBag[0]-&gt;com.youtube.ecommerce.model.ImageModel[&quot;product&quot;]-&gt;com.youtube.ecommerce.model.Products[&quot;imageModel&quot;]-&gt;org.hibernate.collection.internal.PersistentBag[0]-&gt;com.youtube.ecommerce.model.ImageModel[&quot;product&quot;]-&gt;com.youtube.ecommerce.model.Products[&quot;imageModel&quot;]-&gt;org.hibernate.collection.internal.PersistentBag[0]-&gt;com.youtube.ecommerce.model.ImageModel[&quot;product&quot;]-&gt;com.youtube.ecommerce.model.Products[&quot;imageModel&quot;]-&gt;org.hibernate.collection.internal.PersistentBag[0]

The error just keeps going and going so i didnt post the full thing but it continuously says the same thing over and over again.

Im really struggling to understand whats gone wrong here!!

答案1

得分: 0

因为当你将实体序列化时,你会得到类似于以下结构的图形:

Product->Image[]->Product->Image[]->Product-> 依此类推

所以你必须在某个地方中断递归,例如使用 @JsonIgnore,或者使用 @JsonManagedReference@JsonBackReference

这正好在你查看实体的其中一张图片中有清晰展示。

英文:

Its because when you will serialize your entity, you will have graph like

Product-&gt;Image[]-&gt;Product-&gt;Image[]-&gt;Product-&gt; and so on

so you have to cut the recursion somewhere, eg using @JsonIgnore or use @JsonManagedReference, @JsonBackReference`

This is exactly depicted on one of your images that you peek into the entity.

huangapple
  • 本文由 发表于 2020年9月15日 22:15:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63903838.html
匿名

发表评论

匿名网友

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

确定