【问题标题】:Error map @OneToMany relationship with JPA and Hibernate错误映射@OneToMany 与 JPA 和 Hibernate 的关系
【发布时间】:2020-04-15 05:54:59
【问题描述】:

我的数据库中的 OneToMany/ManyToOne 映射目前存在问题。我希望我的 Sale Item 表具有到 Sale 表的 @ManyToOne 映射。

@Entity
@Table(name = "sale")
public class Sale implements Serializable {

    private static final long serialVersionUID = -3857608619547245492L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Column(name = "company", nullable = false)
    private Long company;


    @OneToMany(mappedBy = "sale", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<SaleItem> saleItems = new ArrayList<>();

    public Sale() {
    }

    public void addSaleItem(SaleItem saleItem) {
        saleItems.add(saleItem);
        saleItem.setSale(this);
    }

    //****************************Getters and Setters****************************

}



@Entity
@Table(name = "sale_item")
public class SaleItem implements Serializable {

    private static final long serialVersionUID = 1016354254590870341L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Column(name = "company", nullable = false)
    private Long company;    

    @Column(name = "description", nullable = false)
    private String description;

    @Column(name = "amount", nullable = false)
    private Double amount;

    @Column(name = "price", nullable = false)
    private Double price;

    @ManyToOne(fetch = FetchType.LAZY)
    private Sale sale;

    public SaleItem() {

    }

    //****************************Getters and Setters****************************


    public void setSale(Sale sale) {
        this.sale = sale;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (!(o instanceof SaleItem))
            return false;
        return id != null && id.equals(((SaleItem) o).getId());
    }

    @Override
    public int hashCode() {
        return 31;
    }   

    @Override
    public String toString() {
        return "SaleItem [id=" + id + ", company=" + company + ", product=" + product + ", created=" + created
                + ", updated=" + updated + ", description=" + description + ", amount=" + amount + ", price=" + price
                + ", discount=" + discount + ", user=" + user + ", unit=" + unit + ", sale=" + sale + "]";
    }


}

    //*********************** My Controller ***********************

    /**
     * Create Sale
     * 
     * @param sale
     * @param result
     * @return ResponseEntity<Response<SaleDto>>
     * @throws ParseException
     */
    @PostMapping
    public ResponseEntity<Response<SaleDto>> create(@Valid @RequestBody SaleDto saleDto, BindingResult result)
            throws ParseException {

        log.info("Creating sale: {}", saleDto.toString());
        Response<SaleDto> response = new Response<SaleDto>();

        validateSale(saleDto, result);

        if (result.hasErrors()) {
            log.error("Error - invalid information for Sale: {}", result.getAllErrors());
            result.getAllErrors().forEach(error -> response.getErrors().add(error.getDefaultMessage()));
            return ResponseEntity.badRequest().body(response);
        }

        response.setData(this.convertSaleDto(this.nova(saleDto, result)));
        return ResponseEntity.ok(response);
    }

    private Sale nova(SaleDto saleDto, BindingResult result) throws ParseException {
        SaleItem saleItem = new SaleItem();
        saleItem.setCompany(saleDto.getCompany());
        saleItem.setPrice(saleDto.getItems().get(0).getPrice());
        saleItem.setDescription(saleDto.getItems().get(0).getDescription());
        saleItem.setUnit(saleDto.getItems().get(0).getUnit());

        Sale sale = new Sale();
        sale.setCompany(saleDto.getCompany());
        sale.setUser(saleDto.getUser());
        sale.addSaleItem(saleItem);

        return this.saleService.persist(sale);
    }



    //**************************My Service**************

    /**
     * Persist a sale
     * 
     * @param Sale
     * @return Sale
     */
    Sale persist(Sale sale);



//******************My impl***********************
@Transactional
public Sale persist(Sale sale) {
    log.info("Persisting sale {}", sale);
    return this.saleRepository.save(sale);
}

但我认为我做错了什么,因为我收到了关于 id null 的错误:

SLF4J: Failed toString() invocation on an object of type [com.pissolato.api.entitys.Sale]
Reported exception:
java.lang.StackOverflowError
  at java.lang.AbstractStringBuilder.append(Unknown Source)
  at java.lang.StringBuilder.append(Unknown Source)
  at java.lang.StringBuilder.<init>(Unknown Source)
  at com.pissolato.api.entitys.SaleItem.toString(SaleItem.java:189)
  at java.lang.String.valueOf(Unknown Source)
  at java.lang.StringBuilder.append(Unknown Source)
  at java.util.AbstractCollection.toString(Unknown Source)
  at java.lang.String.valueOf(Unknown Source)
2019-12-27 02:46:40.041  INFO 2684 --- [nio-8080-exec-2] c.p.a.services.implementation.SaleImpl   : Persisting sale [FAILED

toString()] 休眠: /* 插入 com.pissolato.api.entitys.Sale */ 插入 进入 销售 (公司、创建、描述、折扣、子值、类型、更新、用户、值) 价值观 (?, ?, ?, ?, ?, ?, ?, ?, ?)

【问题讨论】:

  • 1 - 事务性不适用于私有方法,因为它是代理 2 - 您在哪里使用存储库接口调用保存? 3 - 为什么添加insertable = false
  • OneToMany and ManyToOne 单向和双向的好书。 vladmihalcea.com/…

标签: java hibernate jpa spring-data-jpa


【解决方案1】:

删除 nullable = false 它将不允许具有 null 值的字段在执行 sql 查询时更新它

@Column(name = "sale_id", nullable = false)
private Long saleId;

【讨论】:

  • 此字段在我的数据库中为空,因此如果我在我的实体中删除可为空的,此错误继续存在。
【解决方案2】:

尝试将 targetEntity 添加到 @ManyToOne 注释属性。

@ManyToOne(targetEntity = Sale.class, fetch = FetchType.LAZY)
@JoinColumn(name = "sale_id", insertable = false, updatable = false)
private Sale sale;

【讨论】:

  • 我做了你的建议,但不幸的是它没有奏效!
【解决方案3】:

您在 Inverse 端使用 mappedby,因此您不需要在实体 (saleItem) 的拥有端加入列。您的“saleItem”实体应如下所示

@Entity
@Table(name = "sale_item")
public class SaleItem implements Serializable {

    private static final long serialVersionUID = 1016354254590870341L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Column(name = "company", nullable = false)
    private Long company;



    @Column(name = "description")
    private String description;

    @Column(name = "amount", nullable = false)
    private Double amount;

    @Column(name = "price", nullable = false)
    private Double price;

    @ManyToOne(fetch = FetchType.LAZY)
    private Sale sale;

    public SaleItem() {

    }



    public void setSale(Sale sale) {
        this.sale = sale;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (!(o instanceof SaleItem))
            return false;
        return id != null && id.equals(((SaleItem) o).getId());
    }

    @Override
    public int hashCode() {
        return 31;
    }
}  

这是我为测试编写的示例服务类。

  @Service
public class SaleService {

    @Autowired
    SaleRepository saleRepo;

    @Transactional
    public void saveSales() {

        SaleItem saleItem = new SaleItem();
        saleItem.setCompany(12L);
        saleItem.setAmount(232.0);
        saleItem.setPrice(23232.0);

        Sale sale = new Sale();
        sale.setCompany(12L);
        \\ Utility class will update on either side of the entities
        sale.addSaleItem(saleItem);
        saleRepo.save(sale);

    } 

}

TRACE 记录输出。

2019-12-26 21:33:32.980 DEBUG 12024 --- [nio-8080-exec-1] org.hibernate.SQL                        : 
    insert 
    into
        sale
        (company, id) 
    values
        (?, ?)
2019-12-26 21:33:32.985 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [12]
2019-12-26 21:33:32.985 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [BIGINT] - [1]
2019-12-26 21:33:33.069 DEBUG 12024 --- [nio-8080-exec-1] org.hibernate.SQL                        : 
    insert 
    into
        sale_item
        (amount, company, description, price, sale_id, id) 
    values
        (?, ?, ?, ?, ?, ?)
2019-12-26 21:33:33.070 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [DOUBLE] - [232.0]
2019-12-26 21:33:33.070 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [BIGINT] - [12]
2019-12-26 21:33:33.071 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [null]
2019-12-26 21:33:33.071 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [4] as [DOUBLE] - [23232.0]
2019-12-26 21:33:33.071 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [5] as [BIGINT] - [1]
2019-12-26 21:33:33.071 TRACE 12024 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [6] as [BIGINT] - [2]

【讨论】:

  • 我做了你的建议,但是当我坚持我的实体时,只有销售类保存在数据库中。我想当一个实体(销售)被保存时,SaleItem 会自动一起保存。
  • 我能够一次性保存 Sale 和 sale_item 实体。我通过添加 Service 类和 TRACE 日志更新了我的答案
  • 我曾尝试使用您的建议,但现在收到错误消息(我已更新我的帖子),如果我删除 toString(),则只需保存销售。
  • 您是否在 Sale 类上调用 addSaveItem 方法?您可以发布有关如何将值设置为 Sale 对象的代码吗?
  • 我已经用所有代码和 tks 更新了我的帖子以寻求您的帮助!
猜你喜欢
  • 2012-02-08
  • 1970-01-01
  • 2019-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 1970-01-01
相关资源
最近更新 更多