因此,我有一个促进领域的类,如实体,服务,控制器等。
但是我在Entity类中有一个接口属性,根据POST时传递的参数,该示例将是保存在DB中的一个子类。但是我遇到了Hibernate在这方面的一个奇怪行为。如果我将该属性设置为promotionSeason = "easterPromotion"
-首先,在控制台中,似乎被创建为ChristmasPromotionSeason,然后更新为EasterPromotionSeason,我不知道为什么,如果我再举一个例子,比如说:promotionSeason = "noPromotion"
,同样的问题...
我在查询时得到了错误的结果,以查看我们有什么促销基于促销季节,如果我有复活节促销保存在数据库中,将返回圣诞节促销在查询结果中,为什么会发生这个问题?你可以看到下面的Hibernate控制台日志...
促销实体:
@Entity
@org.hibernate.annotations.DynamicInsert
@org.hibernate.annotations.DynamicUpdate
@Access(AccessType.FIELD)
public class Promotion {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE) //pre Insert values
private Long promotionId;
//Strategy Pattern, maybe State pattern was more suitable?
//check this -> https://stackoverflow.com/questions/51138344/hibernate-persisting-a-composition-interface-of-strategy-pattern
@Convert(converter = PromotionConverter.class)
@Column(name = "PROMOTION_SEASON", nullable = false)
private PromotionSeason promotionSeason;
public Promotion() {}
public Promotion(PromotionSeason promotionSeason)
{
this.promotionSeason = promotionSeason;
}
public Long getPromotionId() {
return promotionId;
}
public PromotionSeason getPromotionSeason() {
return promotionSeason;
}
public void setPromotionSeason(PromotionSeason promotionSeason) {
this.promotionSeason = promotionSeason;
}
}
推广服务
@Service
public class PromotionService {
private final PromotionRepository promotionRepository;
private final PromotionCallingOthers promotionCallingOthers;
@PersistenceContext
private EntityManager entityManager;
@Autowired
public PromotionService(PromotionRepository promotionRepository, PromotionCallingOthers promotionCallingOthers) {
this.promotionRepository = promotionRepository;
this.promotionCallingOthers = promotionCallingOthers;
}
public void createPromotion(Promotion promotion)
{
System.out.println(promotion.getPromotionSeason().isSeason());
this.promotionRepository.save(promotion);
}
public void addProducts(Promotion promotion, String productName) {
Promotion createdPromotion = new Promotion(promotion.getPromotionSeason());
ResponseEntity<Product> productResponseEntity = promotionCallingOthers.callProduct(productName);
Session session = entityManager.unwrap(Session.class);
session.update(productResponseEntity.getBody()); // for detached entity error
createdPromotion.addProduct(productResponseEntity.getBody());
double price = createdPromotion.getProductList().get(0).getProductPrice();
double discountedPrice = createdPromotion.getPromotionSeason().applySeasonPromotionDiscount(price);
double priceTo = getDigitsFormat(price - discountedPrice);
Objects.requireNonNull(productResponseEntity.getBody()).setProductPrice(priceTo);
createdPromotion.setNumberOfProductsAtPromotion(productResponseEntity.getBody().getProductQuantity());
this.promotionRepository.save(createdPromotion);
}
private double getDigitsFormat(double numberToFormat)
{
DecimalFormat formatDecimal = new DecimalFormat("#.##");
return Double.parseDouble(formatDecimal.format(numberToFormat));
}
public Promotion createPromotionWithType(String promotionType) {
Promotion promotion = new Promotion();
promotion.setPromotionSeason(setPromotionSeasonImplBasedOnType(promotionType));
promotionRepository.save(promotion);
return promotion;
}
public Promotion getPromotionSeasonBasedOnSomething(String promotionType)
{
PromotionSeason promotionSeason = setPromotionSeasonImplBasedOnType(promotionType);
Promotion promotion = promotionRepository.findPromotionByPromotionSeason(promotionSeason);
System.out.println(promotion.getPromotionSeason());
return promotion;
}
private PromotionSeason setPromotionSeasonImplBasedOnType(String promotionType)
{
// eh, state pattern would be better i guess
switch (promotionType.toLowerCase()) {
case "christmas":
return new PromotionChristmasSeason();
case "easter":
return new PromotionEasterSeason();
default:
return new NoPromotionForYouThisTimeMUHAHA();
}
}
public Promotion test(String testam) {
PromotionSeason promotionSeason = checkPromotionSeason(testam);
System.out.println(promotionSeason.isSeason());
Promotion promotion = promotionRepository.findWhatPromotionSeasonWeHave(promotionSeason);
if (promotion == null) {
System.out.println("promotion season ii in if: " + promotionSeason.isSeason());
Promotion promotion1 = new Promotion(promotionSeason);
System.out.println(promotion1.getPromotionSeason().isSeason());
//?
promotion = promotion1;
System.out.println(promotion.getPromotionSeason().isSeason());
promotion.setPromotionStore(promotion1.getPromotionStore());
promotionRepository.save(promotion);
System.out.println(promotion.getPromotionSeason().isSeason());
return promotion;
}
promotion.setPromotionSeason(promotionSeason);
promotionRepository.save(promotion);
System.out.println("promotion is" + promotion.getPromotionSeason().isSeason());
return promotion;
}
private PromotionSeason checkPromotionSeason(String promotionSeason)
{
System.out.println("is \n" + promotionSeason.toLowerCase());
switch (promotionSeason.toLowerCase().trim())
{
case "easter" :
return new PromotionEasterSeason();
case "christmas" :
return new PromotionChristmasSeason();
default:
return new NoPromotionForYouThisTimeMUHAHA();
}
}
促销存储库:
@Repository
public interface PromotionRepository extends JpaRepository<Promotion, Long> {
@Query("SELECT s FROM Promotion s WHERE s.promotionSeason = :promotionSeason")
Promotion findWhatPromotionSeasonWeHave(@Param("promotionSeason") PromotionSeason promotionSeason);
Promotion findPromotionByPromotionSeason(PromotionSeason promotionSeason);
}
促销控制器:
@RestController
@RequestMapping(value = "/promotions")
public class PromotionController {
private final PromotionService promotionService;
@Autowired
public PromotionController(PromotionService promotionService) {
this.promotionService = promotionService;
}
@PostMapping(value = "/createPromotion")
public ResponseEntity<String> createPromotion(@RequestBody Promotion promotion)
{
promotionService.createPromotion(promotion);
return ResponseEntity.status(HttpStatus.CREATED)
.body("Done");
}
@GetMapping(value = "/createPromotion/{promotionType}")
public ResponseEntity<Promotion> createPromotionType(@PathVariable String promotionType)
{
return ResponseEntity.status(HttpStatus.CREATED).body(promotionService.createPromotionWithType(promotionType));
}
//WRONG RESULT
@GetMapping(value = "/getPromotion/{promotionType}")
public ResponseEntity<Promotion> getPromotionType(@PathVariable String promotionType)
{
return ResponseEntity.status(HttpStatus.FOUND).body(promotionService.getPromotionSeasonBasedOnSomething(promotionType));
}
//WRONG RESULT
@GetMapping(value = "/test/{promotion}")
public Promotion check(@PathVariable String promotion)
{
return promotionService.test(promotion);
}
}
促销季界面:
//see: -> https://www.youtube.com/watch?v=IlLC3Yetil0
//see: -> https://stackoverflow.com/questions/72155637/a-way-of-polymorphic-http-requests-using-postman/72158992#72158992
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "promotion")
@JsonSubTypes(
{ @JsonSubTypes.Type(value = PromotionEasterSeason.class, name = "easterPromotion"),
@JsonSubTypes.Type(value = PromotionChristmasSeason.class, name = "christmasPromotion"),
@JsonSubTypes.Type(value = NoPromotionForYouThisTimeMUHAHA.class, name = "noPromotion")
})
public interface PromotionSeason {
String isSeason();
double applySeasonPromotionDiscount(double initialPrice);
}
PromotionEasterSeason类-接口的实现:
@JsonTypeName(value = "easterPromotion")
public class PromotionEasterSeason implements PromotionSeason{
private double promotionProcentToDiscount = 10.99f;
@Override
public String isSeason() {
return "Is Easter Season Discount Time of the Year again!";
}
@Override
public double applySeasonPromotionDiscount(double initialPrice) {
System.out.println("Now you have to pay less with: " + calculateDiscount(initialPrice) + ", instead of: " + initialPrice);
return calculateDiscount(initialPrice);
}
private double calculateDiscount(double initialPriceToDiscount)
{
return this.promotionProcentToDiscount / initialPriceToDiscount;
}
}
促销转换器类:
public class PromotionConverter implements AttributeConverter<PromotionSeason, String> {
@Override
public String convertToDatabaseColumn(PromotionSeason attribute) {
return attribute.getClass().getSimpleName().trim().toLowerCase(Locale.ROOT);
}
@Override
public PromotionSeason convertToEntityAttribute(@NotBlank String dbData) {
return stateOfPromotion(dbData);
}
private PromotionSeason stateOfPromotion(String state)
{
return state.equals("easterPromotion") ? new PromotionEasterSeason() : new PromotionChristmasSeason();
}
}
Hibernate SQL控制台:
2022-08-07 12:16:37.123 DEBUG 7432 --- [nio-8080-exec-2] org.hibernate.SQL : call next value for hibernate_sequence
2022-08-07 12:16:37.249 DEBUG 7432 --- [nio-8080-exec-2] org.hibernate.SQL : insert into PROJECT_HIBERNATE_Promotion (NUMBER_PRODUCTS_AT_PROMOTION, PROMOTION_SEASON, promotionId) values (?, ?, ?)
2022-08-07 12:16:37.258 TRACE 7432 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [0]
2022-08-07 12:16:37.260 DEBUG 7432 --- [nio-8080-exec-2] tributeConverterSqlTypeDescriptorAdapter : Converted value on binding : com.shoppingprojectwithhibernate.PromotionsModule.Domain.PromotionChristmasSeason@54de6f66 -> promotionchristmasseason
2022-08-07 12:16:37.260 TRACE 7432 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [VARCHAR] - [promotionchristmasseason]
2022-08-07 12:16:37.261 TRACE 7432 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [BIGINT] - [1]
2022-08-07 12:16:37.271 DEBUG 7432 --- [nio-8080-exec-2] org.hibernate.SQL : update PROJECT_HIBERNATE_Promotion set PROMOTION_SEASON=? where promotionId=?
2022-08-07 12:16:37.273 DEBUG 7432 --- [nio-8080-exec-2] tributeConverterSqlTypeDescriptorAdapter : Converted value on binding : com.shoppingprojectwithhibernate.PromotionsModule.Domain.PromotionEasterSeason@18c401ef -> promotioneasterseason
2022-08-07 12:16:37.274 TRACE 7432 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [promotioneasterseason]
2022-08-07 12:16:37.275 TRACE 7432 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
1条答案
按热度按时间6rvt4ljy1#
类
PromotionConverter
中的方法执行的操作不一致:convertToDatabaseColumn
将转换为促销季节类的小写名称,因此它将返回:promotioneasterseason
或promotionchristmasseason
或nopromotionforyouthistimemuhaha
。这是将存储在数据库中的字符串。但是,
convertToEntityAttribute
方法(应该执行相反的操作)执行了其他操作:如果来自数据库的字符串是easterPromotion
,则它将返回类型为PromotionEasterSeason
的对象,否则返回类型为PromotionChristmasSeason
的对象。因此,当您在数据库中保存复活节促销时,会发生什麽情况:Hibernate将存储字符串
promotioneasterseason
。当您从数据库中读取该记录时,转换器注意到该记录与easterPromotion
不匹配,因此它将返回PromotionChristmasSeason
。解决方案:确保方法
convertToDatabaseColumn
和convertToEntityAttribute
执行相反的操作。