Spring + JPA @OneToMany与orphanRemoval [英] Spring + JPA @OneToMany with orphanRemoval

查看:167
本文介绍了Spring + JPA @OneToMany与orphanRemoval的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Spring列表表单绑定和orphanRemoval方面存在一些(大)问题。只有当更新某个项目时才会发生此异常 - 插入和删除确实有效。

cascade =all-delete-orphan的集合不再被引用由拥有实体实例: me.gerenciar.model.entity.PedidoItem.filhos



那么,我有一个表单,并且在这种形式下,有一些项目(子项)是在javascript前端动态插入/删除/更新的。



我制作了另外两个这样的表格,它们完美地工作,唯一的区别在于,在这个表格中,我们有3个等级,其他的只有1个层级。



我知道所有这些我们无法像这样设置新字典的东西:this.children = children;但这是由Spring在绑定表单实体时反射而成的。正如我所说,它确实在其他两种情况下工作。



这是我的实体(没有getter和setter)。

  // BaseEntity只是覆盖equals,toString和hashCode的通用方法

@Entity
@Table(name =PEDIDO )
公共类Pedido扩展BaseEntity
{
private static final long serialVersionUID = 1586104653460442257L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name =ID_PEDIDO)
私人整数pedidoId;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =ID_PESSOA_ESTABELECIMENTO)
private Estabelecimento estabelecimento;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =ID_PESSOA_CLIENTE)
私人客户客户;
$ b @ManyToOne(fetch = FetchType.EAGER)
@JoinColumns({@JoinColumn(name =ID_MESA,referencedColumnName =ID_MESA,insertable = false,updatable = false),@ JoinColumn(name =ID_PESSOA_ESTABELECIMENTO,referencedColumnName =ID_PESSOA,insertable = false,updatable = false)})
私人Mesa mesa;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =ID_TURNO)
私人Turno电话;

@DateTimeFormat(iso = ISO.DATE_TIME)
@Column(name =DATA)
私有日期数据;

@Column(name =DATA,updatable = false,insertable = false)
private String rawData;

@Column(name =PRECO)
私人BigDecimal preco;

@Column(name =FINALIZADO)
私人布尔finalizado;

@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy =pedido,orphanRemoval = true)
@OrderBy(ID_PEDIDO_ITEM_GRUPO DESC)
private列表与LT; PedidoItemGrupo> pedidoItemGrupos;

@Column(name =DATA_ANO)
private Integer dataAno;

@Column(name =DATA_MES)
private Integer dataMes;

@Column(name =DATA_DIA)
private Integer dataDia;

@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinColumn(name =ID_CHEQUE)
private check Check;

@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,orphanRemoval = true)
@JoinTable(name =PEDIDO_CARTAO,joinColumns = {@JoinColumn(name =ID_PEDIDO ,referenceColumnName =ID_PEDIDO)},inverseJoinColumns = {@JoinColumn(name =ID_CARTAO,referencedColumnName =ID_CARTAO)})
private List< Cartao> cartoes;

@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinColumn(name =ID_DINHEIRO)
private Dinheiro dinheiro;

@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinColumn(name =ID_BOLETO)
private Boleto boleto;
}

@Entity
@Table(name =PEDIDO_ITEM_GRUPO)
public class PedidoItemGrupo extends BaseEntity
{
private static final long serialVersionUID = 7785627059444833691L;

public static enum Tipo
{
DIVIDIDO,SOMADO
}

@Id
@GeneratedValue(strategy = GenerationType。 AUTO)
@Column(name =ID_PEDIDO_ITEM_GRUPO)
private Integer pedidoItemGrupoId;

@JsonIgnore
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =ID_PEDIDO)
私人Pedido pedido;

@Column(name =QUANTIDADE)
private BigDecimal quantidade;

@Column(name =PRECO_UNITARIO)
private BigDecimal precoUnitario;

@Column(name =PRECO)
私人BigDecimal preco;

@Column(name =DESCONTO)
private BigDecimal desconto;

@Column(name =PRECO_FINAL)
private BigDecimal precoFinal;

@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy =pedidoItemGrupo,orphanRemoval = true)
@Where(clause =EXISTS(SELECT * FROM PEDIDO_ITEM WHERE ID_PEDIDO_ITEM_PAI IS NULL))
private List< PedidoItem> pedidoItens;

$ b $实体
@Table(name =PEDIDO_ITEM)
public class PedidoItem extends BaseEntity
{
private static final long serialVersionUID = 5296905009119022656L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name =ID_PEDIDO_ITEM)
private Integer pedidoItemId;
$ b $ @ @JsonIgnore
@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinColumn(name =ID_PEDIDO_ITEM_PAI)
私人PedidoItem pai;

@OneToMany(cascade = CascadeType.ALL,mappedBy =pai,fetch = FetchType.EAGER,orphanRemoval = true)
private List< PedidoItem> FILHOS;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =ID_PRODUTO,insertable = false,updatable = false)
private Produto produto;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =ID_PRODUTO_CATEGORIA)
private ProdutoCategoria produtoCategoria;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumns({@JoinColumn(name =ID_PRODUTO,referencedColumnName =ID_PRODUTO),@JoinColumn(name =TAMANHO, referencedColumnName =TAMANHO)})
private ProdutoTamanho produtoTamanho;

@JsonIgnore
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =ID_PEDIDO_ITEM_GRUPO)
私人PedidoItemGrupo pedidoItemGrupo;

@Column(name =QUANTIDADE)
private BigDecimal quantidade;

@Column(name =PRECO_UNITARIO)
private BigDecimal precoUnitario;

@Column(name =PRECO)
私人BigDecimal preco;

@Column(name =DESCONTO)
private BigDecimal desconto;

@Column(name =PRECO_TOTAL_UNITARIO)
private BigDecimal precoTotalUnitario;

@Column(name =PRECO_TOTAL)
private BigDecimal precoTotal;

@Column(name =PRECO_TOTAL_FINAL)
private BigDecimal precoTotalFinal;





$ b如果你们需要更多的细节,我会马上发布它。
非常感谢你!





我也试过这个: http://mcls.github.io/blog/2012/08 / 07 / pojo-binding-and-jpas-orphanremoval-in-play /

不成功=(,当spring尝试绑定时得到空指针异常列表filhos

解决方案

所以,我会回答我自己的问题。

对于你们来说,面临POJO绑定到hibernate实体的同样的问题。



解决方法是预先创建所有集合,然后替换您的普通设置方法一个

  class Child {
private List< Child> children = new ArrayList<>();

public setChildren(List< Child> children)
{
this.children.clear();

if(children!= null){
this.children.addAll(children);
}
}
}

这样,你会杀死所有其他的孩子...
当然,这是一个特定的setter方法,你可以用Reflection来做一个通用的方法,只需更新需要的子元素,然后移除其他子元素。



所以,我的最终版本是这样的:

  class Child extends BaseEntity {
private列表与LT;儿童> children = new ArrayList<>();

public setChildren(List< Child> children)
{
//这是神奇的方法,使用BaseEntity上的Reflection $ b $ setList(this.children,children );
}
}


I'm having a little (big) problem with Spring list form binding, and orphanRemoval. This exception occurs only when updating some item - insert and delete does work.

"A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: me.gerenciar.model.entity.PedidoItem.filhos"

Well, I have a form, and in this form, there are some items (children) that is dynamically inserted/deleted/updated on front-end with javascript.

I've made two other forms like this one, and they works perfectly, the only difference is that in this one, we have 3 hierarchy level, and the others were just 1 hierarchy level.

I know all that stuff that we can not set new dictionary like this: "this.children = children;" but this is made by Spring by reflection when binding the form entity. And as I had said, it did work on 2 other cases.

Here are my entities (without getters and setters).

//BaseEntity is just a generic way to override equals, toString and hashCode

@Entity
@Table(name = "PEDIDO")
public class Pedido extends BaseEntity
{
    private static final long serialVersionUID = 1586104653460442257L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID_PEDIDO")
    private Integer pedidoId;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_PESSOA_ESTABELECIMENTO")
    private Estabelecimento estabelecimento;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_PESSOA_CLIENTE")
    private Cliente cliente;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumns({ @JoinColumn(name = "ID_MESA", referencedColumnName = "ID_MESA", insertable = false, updatable = false), @JoinColumn(name = "ID_PESSOA_ESTABELECIMENTO", referencedColumnName = "ID_PESSOA", insertable = false, updatable = false) })
    private Mesa mesa;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_TURNO")
    private Turno turno;

    @DateTimeFormat(iso = ISO.DATE_TIME)
    @Column(name = "DATA")
    private Date data;

    @Column(name = "DATA", updatable = false, insertable = false)
    private String rawData;

    @Column(name = "PRECO")
    private BigDecimal preco;

    @Column(name = "FINALIZADO")
    private Boolean finalizado;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "pedido", orphanRemoval = true)
    @OrderBy("ID_PEDIDO_ITEM_GRUPO DESC")
    private List<PedidoItemGrupo> pedidoItemGrupos;

    @Column(name = "DATA_ANO")
    private Integer dataAno;

    @Column(name = "DATA_MES")
    private Integer dataMes;

    @Column(name = "DATA_DIA")
    private Integer dataDia;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_CHEQUE")
    private Cheque cheque;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinTable(name = "PEDIDO_CARTAO", joinColumns = { @JoinColumn(name = "ID_PEDIDO", referencedColumnName = "ID_PEDIDO") }, inverseJoinColumns = { @JoinColumn(name = "ID_CARTAO", referencedColumnName = "ID_CARTAO") })
    private List<Cartao> cartoes;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_DINHEIRO")
    private Dinheiro dinheiro;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_BOLETO")
    private Boleto boleto;
}

@Entity
@Table(name = "PEDIDO_ITEM_GRUPO")
public class PedidoItemGrupo extends BaseEntity
{
    private static final long serialVersionUID = 7785627059444833691L;

    public static enum Tipo
    {
        DIVIDIDO, SOMADO
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID_PEDIDO_ITEM_GRUPO")
    private Integer pedidoItemGrupoId;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_PEDIDO")
    private Pedido pedido;

    @Column(name = "QUANTIDADE")
    private BigDecimal quantidade;

    @Column(name = "PRECO_UNITARIO")
    private BigDecimal precoUnitario;

    @Column(name = "PRECO")
    private BigDecimal preco;

    @Column(name = "DESCONTO")
    private BigDecimal desconto;

    @Column(name = "PRECO_FINAL")
    private BigDecimal precoFinal;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "pedidoItemGrupo", orphanRemoval = true)
    @Where(clause = "EXISTS (SELECT * FROM PEDIDO_ITEM WHERE ID_PEDIDO_ITEM_PAI IS NULL)")
    private List<PedidoItem> pedidoItens;
}

@Entity
@Table(name = "PEDIDO_ITEM")
public class PedidoItem extends BaseEntity
{
    private static final long serialVersionUID = 5296905009119022656L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID_PEDIDO_ITEM")
    private Integer pedidoItemId;

    @JsonIgnore
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_PEDIDO_ITEM_PAI")
    private PedidoItem pai;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "pai", fetch = FetchType.EAGER, orphanRemoval = true)
    private List<PedidoItem> filhos;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_PRODUTO", insertable = false, updatable = false)
    private Produto produto;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_PRODUTO_CATEGORIA")
    private ProdutoCategoria produtoCategoria;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumns({ @JoinColumn(name = "ID_PRODUTO", referencedColumnName = "ID_PRODUTO"), @JoinColumn(name = "TAMANHO", referencedColumnName = "TAMANHO") })
    private ProdutoTamanho produtoTamanho;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ID_PEDIDO_ITEM_GRUPO")
    private PedidoItemGrupo pedidoItemGrupo;

    @Column(name = "QUANTIDADE")
    private BigDecimal quantidade;

    @Column(name = "PRECO_UNITARIO")
    private BigDecimal precoUnitario;

    @Column(name = "PRECO")
    private BigDecimal preco;

    @Column(name = "DESCONTO")
    private BigDecimal desconto;

    @Column(name = "PRECO_TOTAL_UNITARIO")
    private BigDecimal precoTotalUnitario;

    @Column(name = "PRECO_TOTAL")
    private BigDecimal precoTotal;

    @Column(name = "PRECO_TOTAL_FINAL")
    private BigDecimal precoTotalFinal;
}

If you guys need more details I'll immediately post it. thank you so much!


I have tried this also: http://mcls.github.io/blog/2012/08/07/pojo-binding-and-jpas-orphanremoval-in-play/

no succeed =(, got null pointer exception when spring try to bind List filhos

解决方案

So, im gonna answer my own question.

For you guys, facing the same problem with POJO binding to hibernate entity.

The solution is to pre-instanciate all your collections, and replace your normal set method for this one

class Child {
    private List<Child> children = new ArrayList<>();

    public setChildren(List<Child> children)
    {
        this.children.clear();

        if (children != null) {
          this.children.addAll(children);
        }
    }
}

This way, you gonna kill all the other children... Of course this is a specific setter method, and you could do a generic way with Reflection, and just "update" the needed children, and "remove" the others.

So, my final version is like this:

class Child extends BaseEntity {
    private List<Child> children = new ArrayList<>();

    public setChildren(List<Child> children)
    {
        //this is the magic method, doing with Reflection on BaseEntity
        setList(this.children, children);
    }
}

这篇关于Spring + JPA @OneToMany与orphanRemoval的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆