У меня проблема с сохраняющимися объектами в Spring JPA/hibernate. Я также использую Lombok Plugin, чтобы уменьшить коды шаблонов. В принципе у меня есть объект под названием продукт, который имеет oneToMany отношения с подпроизведением:Как сохранить объект JPA, который имеет два уровня отношений oneToMany?
@Entity
@Table(name="product")
@EqualsAndHashCode(exclude = {"productId"}, callSuper=false)
@ToString(includeFieldNames=true, callSuper=true)
@Data
public class Product implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="product_id")
private Integer productId;
@OneToMany(fetch = FetchType.EAGER, mappedBy="product"
, cascade ={CascadeType.ALL}, orphanRemoval=true)
private Set<SubProduct> subProducts;
}
и это мое подпроизведение, который имеет другой oneToMany отношение с joinTable называется ProductVariantMap:
@Entity
@Table(name="sub_product")
@EqualsAndHashCode(exclude = {"subId","product"})
@ToString(includeFieldNames=true, exclude = {"product"})
@Data
public class SubProduct implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="sub_id")
private Integer subId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
private Product product;
@OneToMany(fetch = FetchType.EAGER,
mappedBy="productVariantMapId.subProduct"
, cascade = {CascadeType.ALL})
private List<ProductVariantMap> productVariantMaps;
}
Это мой ProductVariantMap с встроен идентификатор, который содержит PRODUCTID, SubID и variantId
@Entity
@Table(name="product_variant_map")
@EqualsAndHashCode
@ToString(includeFieldNames=true)
@Data
public class ProductVariantMap implements Serializable {
@EmbeddedId
private ProductVariantMapId productVariantMapId;
@Column(name="variant_value", nullable=false)
private String variantValue;
}
productVariantMapId:
@Embeddable
@EqualsAndHashCode(exclude = {"subProduct","product"})
@ToString(includeFieldNames=true,exclude = {"subProduct","product"})
@Data
public class ProductVariantMapId implements Serializable {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "sub_id")
private SubProduct subProduct;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "product_id")
private Product product;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "variant_id")
private Variant variant;
}
и последнее, но не в последнюю очередь это мой вариант:
@Entity
@Table(name="variant")
@EqualsAndHashCode(exclude = {"variantId"})
@ToString(includeFieldNames=true)
@Data
public class Variant implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="variant_id")
private Integer variantId;
@Column(name="name", nullable=false)
private String name;
}
Моя цель состоит в том, чтобы быть в состоянии сохраняются все отношения на одном дыхании, когда я productRepository.saveAndFlush (продукт). Обратите внимание, что все первичные ключи генерируются генератором последовательности базы данных и, следовательно, его значение null перед saveAndFlush. У меня ниже исключения, когда я пытался сделать выше:
java.lang.NullPointerException
at org.hibernate.type.descriptor.java.AbstractTypeDescriptor.extractHashCode(AbstractTypeDescriptor.java:65)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:201)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:206)
at org.hibernate.type.EntityType.getHashCode(EntityType.java:355)
at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:241)
at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:61)
at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:54)
at org.hibernate.internal.AbstractSharedSessionContract.generateEntityKey(AbstractSharedSessionContract.java:459)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:162)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:788)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:391)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:316)
Я проследил выполнение и казалось, что исключение было вызвано классом Варианта, имеющими нулевую Hashcode, который не должен был быть случаем, так как я заселен вариантом с различным имя для спящего режима для различения (хотя идентификатор еще не известен) для каждого субпроизведения (я пытался сохранить 2 подпрограммы под одним продуктом). Затем каждый подпродукт будет иметь два productVariantMap.
Я ожидаю, что останется 1 строка продукта, 2 подпрограммы, 4 строки карты варианта продукта и 2 варианта строк. Можно ли сохранить все отношения за один раз в JPA? Большое спасибо
Причина, по которой мой метод хэша и равенства основан на других полях рядом с идентификатором, заключается в том, что я полагаюсь на генератор идентификаторов базы данных. Если я использую ID как свой уникальный идентификатор, Java будет обрабатывать новые объекты в наборе как единое целое, поскольку все они имеют нулевой идентификатор, см. Это http://www.onjava.com/pub/a/onjava/2006/09/13 /dont-let-hibernate-steal-your-identity.html – user3391672
См. эту строку в методе equals: if (id == null) return false; Новые объекты без Id никогда не будут такими же. –
Если я удалю класс manyToOne в классе Embeddable, как hibernate знает, какой sub_id или product_id имеет один конкретный продуктVariantMap ?. Помните, что все объекты Id имеют значение null до saveAndFlush – user3391672