简体   繁体   English

@ElementCollection不会脱离

[英]@ElementCollection does not getting detached

My goal is to clone entity 'Product' with all its filters. 我的目标是克隆带有所有过滤器的实体“产品”。

For example, I have an entity (getters and setters omitted for simplicity): 例如,我有一个实体(为简单起见,省略了getter和setter):

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ElementCollection()
    private List<Filter> filters = new ArrayList<Filter>();
}

And embeddable class: 和可嵌入的类:

@Embeddable
public class Filter {
    @Column(length = 255, nullable = false)
    private String name;
    @Column(nullable = false)
    private long variant = -1;
}

Now, if I do: 现在,如果我这样做:

entityManager.detach(product);
product.setId(null);
productService.save(product);

I will get a copy of product entity but with filters from original product. 我将获得产品实体的副本,但带有原始产品的过滤器。 In meanwhile original product will end up with no filters at all.. 同时,原始产品最终将根本没有任何过滤器。

Thats how filter's table rows looks like: 那就是过滤器的表格行的样子:

Before: 之前:

product_id; name; variant
217; "f2"; 86

After: 后:

product_id; name; variant
218; "f2"; 86

I tried detach each filter from the list but it gives me error. 我尝试从列表中分离每个过滤器,但它给了我错误。

How can I make it copy filters with an entity? 如何使其与实体复制筛选器?

Edit: Added full Product and Filter code: 编辑:添加了完整的产品和过滤器代码:

package com.serhiy1.model;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.*;

import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.SortableField;
import org.joda.time.DateTime;

import com.serhiy1.constraint.LocalePacker;

@Indexed
@Entity
@EntityListeners(ProductListener.class)
public class Product {
    public static final int PRICE_PER_ONE = 0;
    public static final int PRICE_PER_METER = 1;

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

    private Long code;

    private String name = "";

    private String grouping = "";

    @Field
    @Column(columnDefinition="text")
    private String title = "";

    @Field
    @Column(columnDefinition="text")
    private String intro = "";

    @Column(columnDefinition="text")
    private String content = "";

    @Field
    @Column(columnDefinition="text")
    private String contentHtml = "";

    private String locale = "en";

    private Long parentId = 0L;

    private DateTime time;

    private DateTime timeMod;

    private Long balanceRequired = 0L;

    private Integer index = 0;

    @Field(name = "price_sort")
    @SortableField(forField = "price_sort")
    private Double price = 0.0;

    private Integer pricePer;

    @Transient
    private long childrenCount = 0;
    @Transient
    private String image = "";
    @Transient
    private List<String> images = new ArrayList<String>();

    @ManyToOne(targetEntity = User.class)
    @JoinColumn(nullable = false, name = "user_id")
    @LazyCollection(LazyCollectionOption.FALSE)
    private User user;

    @ManyToOne(targetEntity = Product.class)
    @JoinColumn(nullable = true, name = "category_id")
    @LazyCollection(LazyCollectionOption.FALSE)
    private Product category;

    @ElementCollection()
    private List<Filter> filters = new ArrayList<Filter>();

    @ElementCollection()
    private List<Modifier> modifiers = new ArrayList<Modifier>();

    public Product() {
    }

    @Transient
    private String _title = "";
    @Transient
    private String _intro = "";
    @Transient
    private String _content = "";
    @Transient
    private String _contentHtml = "";

    public void pack(String locale, List<String> locales) {
        if(locale.contains("_")) return;
        title = LocalePacker.repack(locale, _title, title, locales);
        intro = LocalePacker.repack(locale, _intro, intro, locales);
        content = LocalePacker.repack(locale, _content, content, locales);
        contentHtml = LocalePacker.repack(locale, _contentHtml, contentHtml, locales);
    }
    public void unpack(String locale) {
        _title = LocalePacker.unpackStr(locale, title).getOrDefault(locale, "");
        _intro = LocalePacker.unpackStr(locale, intro).getOrDefault(locale, "");
        _content = LocalePacker.unpackStr(locale, content).getOrDefault(locale, "");
        _contentHtml = LocalePacker.unpackStr(locale, contentHtml).getOrDefault(locale, "");
    }
    public void copy(String landFrom, String landTo) {
        title = LocalePacker.copyLang(title, landFrom, landTo);
        intro = LocalePacker.copyLang(intro, landFrom, landTo);
        content = LocalePacker.copyLang(content, landFrom, landTo);
        contentHtml = LocalePacker.copyLang(contentHtml, landFrom, landTo);
    }

    public Modifier getModifier(String name) {
        for(Modifier m: modifiers) {
            if(m.getName().equals(name)) return m;
        }
        return null;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public long getCode() {
        return code == null ? id : code;
    }

    public void setCode(long code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGrouping() {
        return grouping;
    }

    public void setGrouping(String grouping) {
        this.grouping = grouping;
    }

    public String getTitle() {
        return _title;
    }

    public void setTitle(String title) {
        this._title = title;
    }

    public String getIntro() {
        return _intro;
    }

    public void setIntro(String intro) {
        this._intro = intro;
    }

    public String getContent() {
        return _content;
    }

    public void setContent(String content) {
        this._content = content;
    }

    public String getContentHtml() {
        return _contentHtml;
    }

    public void setContentHtml(String contentHtml) {
        this._contentHtml = contentHtml;
    }

    public String getLocale() {
        return locale;
    }

    public void setLocale(String locale) {
        this.locale = locale;
    }

    public long getParentId() {
        return parentId;
    }

    public void setParentId(long parentId) {
        this.parentId = parentId;
    }

    public DateTime getTime() {
        return time;
    }

    public void setTime(DateTime time) {
        this.time = time;
    }

    public DateTime getTimeMod() {
        return timeMod;
    }

    public void setTimeMod(DateTime timeMod) {
        this.timeMod = timeMod;
    }

    public long getBalanceRequired() {
        return balanceRequired == null ? 0L : balanceRequired;
    }
    public void setBalanceRequired(long balanceRequired) {
        this.balanceRequired = balanceRequired;
    }

    public Integer getIndex() {
        //return index == null ? 1000 : index;
        return index;
    }
    public void setIndex(Integer index) {
        this.index = index;
    }

    public double getPrice() {
        return price == null ? 0.0 : price;
    }
    public void setPrice(double price) {
        this.price = price;
    }

    public int getPricePer() {
        return pricePer == null ? PRICE_PER_METER : pricePer;
    }
    public void setPricePer(int pricePer) {
        this.pricePer = pricePer;
    }

    public long getChildrenCount() {
        return childrenCount;
    }
    public void setChildrenCount(long childrenCount) {
        this.childrenCount = childrenCount;
    }
    public String getImage() {
        return image;
    }
    public void setImage(String image) {
        this.image = image;
    }
    public List<String> getImages() {
        return images;
    }
    public void setImages(List<String> images) {
        this.images = images;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Product getCategory() {
        return category;
    }

    public void setCategory(Product category) {
        this.category = category;
    }

    public List<Filter> getFilters() {
        return filters;
    }

    public void setFilters(List<Filter> filters) {
        this.filters = filters;
    }

    public List<Modifier> getModifiers() {
        return modifiers;
    }

    public void setModifiers(List<Modifier> modifiers) {
        this.modifiers = modifiers;
    }

    public boolean isCategory() { return price < 0; }

    @Override
    public String toString() {
        return "Article{" +
                "id=" + id +
                '}';
    }
}

.. ..

package com.serhiy1.model;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Transient;

@Embeddable
public class Filter {
    @Column(length = 255, nullable = false)
    private String name;
    @Column(nullable = false)
    private long variant = -1;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public long getVariant() {
        return variant;
    }
    public void setVariant(long variant) {
        this.variant = variant;
    }
}

I made a mini project trying to replicate your issue. 我做了一个小型项目,试图复制您的问题。

It is a String Boot project with H2 database and JPA (Hibernate implementation). 这是一个带有H2数据库和JPA(Hibernate实现)的String Boot项目。

On startup, Hibernate creates 2 tables: 在启动时,Hibernate创建2个表:

create table product (
   id bigint not null,
    primary key (id)
)

and

create table product_filters (
   product_id bigint not null,
    name varchar(255) not null,
    variant bigint not null
) 

On product with filters creation, both tables get inserted: 在创建过滤器的产品上,将插入两个表:

insert 
into
    product
    (id) 
values
    (1)

and

insert 
into
    product_filters
    (product_id, name, variant) 
values
    (1, "f1", 1) 

After: 后:

entityManager.detach(product);
product.setId(null);
productService.save(product);

Hibernate issues: 休眠问题:

delete 
from
    product_filters 
where
    product_id=1

which is normal, since filters is an ElementCollection therefore it is totally owned by the entity Product . 这是正常的,因为filters是ElementCollection,所以它完全由实体Product拥有。 On productService.save(product) Hibernate detects that filters collection is bound to another Product therefore deletes the old bound (from product_filter table) before creating a new one. Hibernate在productService.save(product)检测到filters集合已绑定到另一Product因此在创建新product_filter之前删除了旧的绑定(从product_filter表中)。

The only way to overcome the deletion is to recreate the collection: 克服删除的唯一方法是重新创建集合:

    List<Filter> filters = new ArrayList<Filter>(); 
    filters.addAll(oldFilters);
    product.setFilters(filters);

To sum up, here is the solution: 总结一下,这是解决方案:

// To trigger the fetch
List<Filter> filters = new ArrayList<Filter>(product.getFilters());
entityManager.detach(product);
product.setId(null);
product.setFilters(filters);
productService.save(product);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM