DBMSとしてMySQLを使用して、サーバー側でSpring + Hibernateを使用してプロジェクトを実現する際に非常に厄介な問題が発生しました。Spring + Hibernate:サーバーが正しいJSONを受け取りますが、Hibernateがnull値を挿入します
この問題には、製品とフィードバックの2つの主体があり、1対多の関係で接続されています(1つの製品にはフィードバックがありますが、フィードバックは1つの製品にしか関係しません)。これは、ユーザーからのフィードバックを伴う古典的な製品構造のようです。
これは、2つのエンティティの春での実装です:
Product.java
package it.uniroma2.e01.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyEnumerated;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import it.uniroma2.e01.constant.Category;
import lombok.Data;
@Entity
@Data
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, unique = true)
private String name;
private String description;
@Column(nullable = false, length = 1)
@MapKeyEnumerated(EnumType.ORDINAL)
private Category category;
@ManyToOne(cascade = CascadeType.MERGE)
@JoinColumn(name = "product_user_id", nullable = false)
private User user;
@OneToMany(cascade = {CascadeType.DETACH, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.REMOVE})
@JoinTable(name = "product_feedback",
joinColumns = {@JoinColumn(name = "product_id")},
inverseJoinColumns = {@JoinColumn(name = "feedback_id")}
)
private List<Feedback> feedbacks;
public Product() {}
}
Feedback.java
package it.uniroma2.e01.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyEnumerated;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import it.uniroma2.e01.constant.Rating;
import lombok.Data;
@Entity
@Data
@Table(name = "feedback")
public class Feedback {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(cascade = CascadeType.MERGE)
@JoinColumn(name = "feedback_user_id", nullable = false)
private User user;
@Column(nullable = false, length = 1)
@MapKeyEnumerated(EnumType.ORDINAL)
private Rating rating;
private String text;
@OneToMany(cascade = {CascadeType.DETACH, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.REMOVE})
@JoinTable(name = "feedback_comment",
joinColumns = {@JoinColumn(name = "feedback_id")},
inverseJoinColumns = {@JoinColumn(name = "comment_id")}
)
private List<Comment> comments;
public Feedback() {}
}
私は新しいを挿入しようとすると、既存の製品を更新する製品に関連するフィードバックは、Hibernate corr実際に行う操作を理解していますが、すべてのnull値(idフィールドを除いて、自動生成)を含むフィードバックを挿入しようとします。
例えば、私はDBにすでに次の製品があります。
Product(
id=20,
name=Antipasto misto,
description=Misto di antipasti,
category=APPETIZER,
user=User(
id=1,
[email protected],
username=Luca,
password=ciao
),
feedbacks=[]
)
と、クライアントからのPUT要求に
は、私はそれを更新してみてください。Product(
id=20,
name=Antipasto misto,
description=Misto di antipasti,
category=APPETIZER,
user=User(
id=1,
[email protected],
username=Luca,
password=ciao
),
feedbacks=[
Feedback(
id=null,
user=User(
id=1,
[email protected],
username=Luca,
password=ciao
),
rating=GOOD,
text=Prova,
comments=[]
)
]
)
とHibernateは新しいフィードバックを挿入するために、正しくしようとしますが、代わりに右の値を結合し、それはすべてのNULLをバインドしよう:製品オブジェクトが正しくあり、サーバ側のコンソール上に印刷し、(JSONで)受け取りましたエラーを引き起こします。これはログです。
2017-08-23 15:49:24.720 DEBUG 1479 --- [nio-8080-exec-7] org.hibernate.SQL : insert into feedback (rating, text, feedback_user_id) values (?, ?, ?)
Hibernate: insert into feedback (rating, text, feedback_user_id) values (?, ?, ?)
2017-08-23 15:49:24.720 TRACE 1479 --- [nio-8080-exec-7] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [null]
2017-08-23 15:49:24.720 TRACE 1479 --- [nio-8080-exec-7] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [null]
2017-08-23 15:49:24.725 WARN 1479 --- [nio-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1048, SQLState: 23000
2017-08-23 15:49:24.725 ERROR 1479 --- [nio-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'rating' cannot be null
2017-08-23 15:49:24.728 DEBUG 1479 --- [nio-8080-exec-7] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<it.uniroma2.e01.dto.ProductDTO> it.uniroma2.e01.controller.ProductController.updateProduct(it.uniroma2.e01.dto.ProductDTO,java.lang.Long)]: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
2017-08-23 15:49:24.729 DEBUG 1479 --- [nio-8080-exec-7] .w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<it.uniroma2.e01.dto.ProductDTO> it.uniroma2.e01.controller.ProductController.updateProduct(it.uniroma2.e01.dto.ProductDTO,java.lang.Long)]: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
2017-08-23 15:49:24.730 DEBUG 1479 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<it.uniroma2.e01.dto.ProductDTO> it.uniroma2.e01.controller.ProductController.updateProduct(it.uniroma2.e01.dto.ProductDTO,java.lang.Long)]: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
2017-08-23 15:49:24.750 DEBUG 1479 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : Could not complete request
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
私は実際にそれを解決する方法はわかりませんが、明らかにすべてがうまくいきます(私にとっては少なくとも)。
お手数ですがお寄せいただきありがとうございます!
編集:
私はコントローラを追加:
ProductController.java
package it.uniroma2.e01.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import it.uniroma2.e01.constant.Category;
import it.uniroma2.e01.dto.ProductDTO;
import it.uniroma2.e01.service.ProductService;
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(path = "e01/product/findById/{id}", method = RequestMethod.GET)
public ResponseEntity<ProductDTO> findProductById(@PathVariable Long id) {
ProductDTO productDTO = productService.findById(id);
ResponseEntity<ProductDTO> resultProductDTO =
new ResponseEntity<ProductDTO>(productDTO, HttpStatus.OK);
return resultProductDTO;
}
@RequestMapping(path = "e01/product/findByName/{name}", method = RequestMethod.GET)
public ResponseEntity<ProductDTO> findProductByName(@PathVariable String name) {
ProductDTO productDTO = productService.findByName(name);
ResponseEntity<ProductDTO> resultProductDTO =
new ResponseEntity<ProductDTO>(productDTO, HttpStatus.OK);
return resultProductDTO;
}
@RequestMapping(path = "e01/product/findByCategory/{category}", method = RequestMethod.GET)
public ResponseEntity<ProductDTO> findProductById(@PathVariable Category category) {
ProductDTO productDTO = productService.findByCategory(category);
ResponseEntity<ProductDTO> resultProductDTO =
new ResponseEntity<ProductDTO>(productDTO, HttpStatus.OK);
return resultProductDTO;
}
@RequestMapping(path = "e01/product/findByUserId/{id}", method = RequestMethod.GET)
public ResponseEntity<List<ProductDTO>> findByUserId(@PathVariable Long id) {
List<ProductDTO> productsDTO = productService.findByUserId(id);
ResponseEntity<List<ProductDTO>> resultProductsDTO =
new ResponseEntity<List<ProductDTO>>(productsDTO, HttpStatus.OK);
return resultProductsDTO;
}
@RequestMapping(path = "e01/product/getAll", method = RequestMethod.GET)
public ResponseEntity<List<ProductDTO>> findAll() {
List<ProductDTO> productsDTO = productService.findAll();
ResponseEntity<List<ProductDTO>> resultProductsDTO =
new ResponseEntity<List<ProductDTO>>(productsDTO, HttpStatus.OK);
return resultProductsDTO;
}
@RequestMapping(path = "e01/product/create", method = RequestMethod.POST)
public ResponseEntity<ProductDTO> createProduct(@RequestBody ProductDTO productDTO) {
ProductDTO productCreated = productService.createProduct(productDTO);
ResponseEntity<ProductDTO> resultProductDTO =
new ResponseEntity<ProductDTO>(productCreated, HttpStatus.OK);
return resultProductDTO;
}
@RequestMapping(path = "e01/product/update/{id}", method = RequestMethod.PUT)
public ResponseEntity<ProductDTO> updateProduct(@RequestBody ProductDTO productDTO, @PathVariable Long id) {
ProductDTO productUpdated = productService.updateProduct(productDTO);
ResponseEntity<ProductDTO> resultEmployee;
if(productUpdated != null) {
resultEmployee = new ResponseEntity<ProductDTO>(productUpdated, HttpStatus.OK);
} else {
resultEmployee = new ResponseEntity<ProductDTO>(HttpStatus.NOT_MODIFIED);
}
return resultEmployee;
}
@RequestMapping(path = "e01/product/delete/{id}", method = RequestMethod.DELETE)
public ResponseEntity<ProductDTO> deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
ResponseEntity<ProductDTO> response = new ResponseEntity<ProductDTO>(HttpStatus.OK);
return response;
}
}
とサービス層:
ProductServiceI mpl.java
package it.uniroma2.e01.service.impl;
import java.lang.reflect.Type;
import java.util.List;
import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import it.uniroma2.e01.constant.Category;
import it.uniroma2.e01.dao.ProductDAO;
import it.uniroma2.e01.dto.ProductDTO;
import it.uniroma2.e01.model.Product;
import it.uniroma2.e01.service.ProductService;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDAO productDAO;
@Autowired
private ModelMapper mapper;
public ProductDTO findById(Long id) {
Product product = productDAO.findById(id);
return mapper.map(product, ProductDTO.class);
}
public ProductDTO findByName(String name) {
Product product = productDAO.findByName(name);
return mapper.map(product, ProductDTO.class);
}
public ProductDTO findByCategory(Category category) {
Product product = productDAO.findByCategory(category);
return mapper.map(product, ProductDTO.class);
}
public List<ProductDTO> findByUserId(Long id) {
List<Product> products = productDAO.findByUserId(id);
Type listProductsType = new TypeToken<List<ProductDTO>>() {}.getType();
return mapper.map(products, listProductsType);
}
public List<ProductDTO> findAll() {
List<Product> products = productDAO.findAll();
Type listProductsType = new TypeToken<List<ProductDTO>>() {}.getType();
return mapper.map(products, listProductsType);
}
public ProductDTO createProduct(ProductDTO productDTO) {
Product product = mapper.map(productDTO, Product.class);
Product productFromDB = productDAO.save(product);
return mapper.map(productFromDB, ProductDTO.class);
}
public ProductDTO updateProduct(ProductDTO productDTO) {
Product product = mapper.map(productDTO, Product.class);
Product productFromDB;
if(productDAO.exists(product.getId())) {
System.out.println(product.toString());
productFromDB = productDAO.save(product);
} else {
productFromDB = null;
}
return mapper.map(productFromDB, ProductDTO.class);
}
public void deleteProduct(Long id) {
Product product = productDAO.findById(id);
if(product != null) {
productDAO.delete(product.getId());
}
}
}
あなたの 'rating'フィールドが正しく提供されていますか?デバッグモードを試しましたか? – Pijotrek
はい、問題の種類なしで操作を実行する前に書いた例と同じ値のフィードバックのみを作成しようとするとします。あなたがコードのいくつかの部分が私に教えたい場合は、それらをコピーします – Luca
あなたのコントローラとビジネスレイヤを共有してください。オブジェクトフィールドを休止状態の部分に記録し、満たされたオブジェクトをビジネスレイヤに渡すようにしてください。 – Sarkhan