@ElementCollection不会脱离

时间:2018-12-20 11:21:12

标签: java jpa

我的目标是使用所有过滤器克隆实体“产品”。

例如,我有一个实体(为简单起见,省略了getter和setter):

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

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

和可嵌入的类:

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

现在,如果我这样做了:

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

我将获得产品实体的副本,但带有原始产品的过滤器。同时,原始产品最终将根本没有任何过滤器。

那是过滤器的表格行的样子:

之前:

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

之后:

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

我尝试从列表中分离出每个过滤器,但这给了我错误。

如何使其与实体复制过滤器?

编辑:添加了完整的产品和过滤器代码:

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;
    }
}

1 个答案:

答案 0 :(得分:1)

我做了一个小型项目,试图复制您的问题。

这是一个具有H2数据库和JPA(Hibernate实现)的String Boot项目。

在启动时,Hibernate创建2个表:

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

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

在创建过滤器的产品上,两个表都将插入:

insert 
into
    product
    (id) 
values
    (1)

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

之后:

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

休眠问题:

delete 
from
    product_filters 
where
    product_id=1

这是正常的,因为filters是ElementCollection,因此它完全由实体Product拥有。在productService.save(product)上,Hibernate检测到filters集合已绑定到另一个Product,因此在创建新集合之前将其删除(从product_filter表中删除)。

克服删除的唯一方法是重新创建集合:

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

总而言之,这是解决方案:

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