当前位置: 首页 > 知识库问答 >
问题:

Hibernate使用@Conversion类保存实体时的奇怪行为

陶裕
2023-03-14

所以我有一个推广域,类如实体、服务、控制器等。

但是我在Entity类中有一个接口属性,并且根据POST时传递的参数,该实例将是保存在DB中的子类。但是我遇到了Hibernate在这方面的奇怪行为。如果我将该属性设为促销季节="easter促销"-首先,在控制台中,将应用程序创建为Christmas促销季节,然后更新为Easter促销季节,我不知道为什么,如果我再举一个例子,让我们说:促销季节="no促销",同样的问题...

我在查询中得到了错误的结果,以查看我们基于PromotionSeason的促销活动,如果我在数据库中保存了EasterPromotion,它将在查询结果中重新链接ChristmasPromotion。为什么会出现此问题?您可以从下面的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);
}

接口的Impl:

@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个答案

充普松
2023-03-14

PromotionConverter中的方法执行的操作不一致:

convertToDatabaseColumn将转换为促销季节类的小写名称,因此它将返回:promotioneasterseasonorpromotionchristmasseasonNoPromotionForYouthistimumuHaha。这是将存储在数据库中的字符串

但是方法<code>convertToEntityAttribute</code>应该做相反的事情,它还做了其他事情:如果数据库中的字符串是<code>easterPromotion</code>那么它将返回<code>PromotionEasterSeason</code>类型的对象,否则将返回<code>Promotion ChristmasSeason>/code>类型的对象。

那么,当您在数据库中保存复活节促销时会发生什么:Hibernate将存储字符串促销EasterSeason。当您从数据中读取该记录时,转换器注意到该记录与easterPromotion不匹配,因此它将返回促销Christmasseason

解决方案:确保方法convertToDatabaseColumnconvertToEntityAttribute

 类似资料:
  • 当我保存以下实体时,它在另一个对象中复制相同的值: 例如,在我的struts项目(屏幕)中,如果我更改shipmentShipper字段的值,那么Shipment收货人也将使用相同的值进行更新。 实体: 仓库类: JSP中的字段映射(工作正常): 请检查屏幕截图,我将发货人更改为C4并保存。收货人也被选为C4。请注意,两个选择都使用相同的数据列表填充。(客户名单)

  • 问题内容: 如何保存hibernate实体并忽略(不正确)瞬态模式。 例如: 我想保存它: 弹簧产生错误: 问题答案: 参见http://docs.jboss.org/hibernate/validator/4.2/reference/en- US/html_single/#validator-checkconstraints- orm 。默认情况下,Hibernate(ORM)检查默认验证组的每

  • 问题内容: 我在父方使用批注具有一对一关系。现在,我想自己保存子实体。 例如,我有和作为孩子的实体,我需要保存(父的id属性设置为之后的课程)。但是,当使用这种安排时,我在下面列出了一个例外… 为什么hibernate不允许这样做的任何想法?更清楚地说,我的代码如下… ParentEntity: ChildEntity: 我尝试保存的方式是… 关于如何尝试保存子实体,任何指针将不胜感激。 问题答案

  • 我正在使用Mapstruct映射将一个POJO转换为另一个POJO模型 以下是mapstruct自动生成的方法 该方法基本上获取源POJO的映射,并将其转换为目标模型的映射。生成正在通过。 当我运行代码时,我在这个方法中得到了ClassCast异常:HeaderAttributeGenericDataTypeMaptoStringEnergiectAttributeDataMap 堆栈跟踪: 我还

  • 我是Hibernate的新手,并要求使用具有这些列的表的数据库 表:TBL _ product//库存项目列表< br >列:< br > key _ product < br > key _ category < br > fld _ product _ name < br > fld _ Inventory _ qty < br > fld _ unit _ price < br > fld

  • 为了方便起见,我想创建datetime的子类。时间三角洲。这样做的目的是定义一个类: 所以我可以快速创建这样的时间增量: 然而,上面的代码产生了n天而不是n小时的时间增量。例如,请看以下ipython会话: 我无法解释这一点。有人吗?