Erwan Moutymbo (
https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=640210c...
) *updated* an issue
Hibernate ORM (
https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiY2EzNjIxMTM4...
) / Bug (
https://hibernate.atlassian.net/browse/HHH-16946?atlOrigin=eyJpIjoiY2EzNj...
) HHH-16946 (
https://hibernate.atlassian.net/browse/HHH-16946?atlOrigin=eyJpIjoiY2EzNj...
) Entity with a composite id using an @IdClass with a bidirectionnal @OneToOne not deleted
when L2 cache disabled (
https://hibernate.atlassian.net/browse/HHH-16946?atlOrigin=eyJpIjoiY2EzNj...
)
Change By: Erwan Moutymbo (
https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=640210c...
)
This issue might be a linked to
[
https://hibernate.atlassian.net/browse/HHH-16810|https://hibernate.atlass...
since this issue has been resolved, I have another problem.
h2. Entities
{code:java}@Getter
@Entity
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@IdClass(SpecialOperator.SpecialOperatorPK.class)
@Table(name = "SPECIAL_OPERATORS")
@Cacheable
@Cache(usage = READ_WRITE)
@NoArgsConstructor(access = PROTECTED)
public class SpecialOperator {
@EqualsAndHashCode.Include
@ToString.Include
@Id
private Provider provider;
@EqualsAndHashCode.Include
@ToString.Include
@Id
private String operatorId;
@OneToMany(mappedBy = "operator", cascade = { PERSIST, MERGE, REMOVE },
orphanRemoval = true)
private final Set<SpecialPricePoint> pricePoints = new HashSet<>();
@CreationTimestamp
@Column(name = "CREATION_DATE", nullable = false, updatable = false)
private Instant creationDate;
@UpdateTimestamp
@Column(name = "MODIFICATION_DATE", nullable = false)
private Instant modificationDate;
public void addPricePoint(SpecialPricePoint pricePoint) {
pricePoint.setOperator(this);
pricePoints.add(pricePoint);
}
public SpecialOperator(Provider provider, String operatorId) {
this.provider = provider;
this.operatorId = operatorId;
}
@EqualsAndHashCode
@ToString
@Getter
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor
@Embeddable
public static class SpecialOperatorPK implements Serializable {
@Enumerated(EnumType.STRING)
@Column(name = "PROVIDER_ID", nullable = false)
Provider provider;
@Column(name = "OPERATOR_ID", nullable = false)
String operatorId;
}
}{code}
{code:java}@Getter
@Entity
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
@Table(name = "SPECIAL_OPERATOR_PRICES_POINTS")
@IdClass(SpecialPricePoint.SpecialPricePointPK.class)
@NoArgsConstructor(access = PROTECTED)
public class SpecialPricePoint {
public SpecialPricePoint(SpecialOperator operator,
String wholesalePrice) {
this.operator = operator;
this.wholesalePrice = wholesalePrice;
}
@ManyToOne
@ToString.Include
@Setter(PACKAGE)
@EqualsAndHashCode.Include
@JoinColumn(name = "PROVIDER_ID", referencedColumnName =
"PROVIDER_ID")
@JoinColumn(name = "OPERATOR_ID", referencedColumnName =
"OPERATOR_ID")
@MapsId
private SpecialOperator operator;
@Id
@Column(name = "PRICE_POINT", nullable = false)
@ToString.Include
@EqualsAndHashCode.Include
String wholesalePrice;
@OneToOne(mappedBy = "wholesalePrice", cascade = { PERSIST, MERGE, REMOVE },
orphanRemoval = true)
@Cache(usage = READ_WRITE)
private SpecialProduct product;
public void setProduct(SpecialProduct product) {
product.setWholesalePrice(this);
this.product = product;
}
@Value
@Embeddable
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor
public static class SpecialPricePointPK implements Serializable {
@Embedded
@NonFinal
@AttributeOverride(name = "provider", column = @Column(name =
"PROVIDER_ID", nullable = false))
@AttributeOverride(name = "operatorId", column = @Column(name =
"OPERATOR_ID", nullable = false))
SpecialOperatorPK operator;
@NonFinal
String wholesalePrice;
}
}{code}
{code:java}
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
@NoArgsConstructor(access = PROTECTED)
@Table(name = "SPECIAL_PRODUCTS")
@Entity
@IdClass(SpecialProduct.SpecialProductPK.class)
@Cacheable
@Cache(usage = READ_WRITE)
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
public class SpecialProduct {
public SpecialProduct(String productId,
SpecialPricePoint wholesalePrice) {
this.productId = productId;
this.wholesalePrice = wholesalePrice;
}
@Id
@EqualsAndHashCode.Include
@ToString.Include
@Column(name = "PRODUCT_ID", nullable = false)
private String productId;
@Setter
@ToString.Include
@EqualsAndHashCode.Include
@OneToOne(optional = false, cascade = { PERSIST, MERGE })
@JoinColumn(name = "WHOLESALE_PRICE_AMOUNT",
referencedColumnName = "PRICE_POINT",
updatable = false,
insertable = false)
@JoinColumn(name = "OPERATOR_ID", referencedColumnName =
"OPERATOR_ID", updatable = false, insertable = false)
@JoinColumn(name = "PROVIDER_ID", referencedColumnName =
"PROVIDER_ID", updatable = false, insertable = false)
@MapsId
private SpecialPricePoint wholesalePrice;
@CreationTimestamp
@Column(name = "CREATION_DATE", nullable = false, updatable = false)
private Instant creationDate;
@OptimisticLock(excluded = true)
@UpdateTimestamp
@Column(name = "MODIFICATION_DATE", nullable = false)
private Instant modificationDate;
@Value
@Embeddable
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor
public static class SpecialProductPK implements Serializable {
@Embedded
@NonFinal
@AttributeOverride(name = "operator.provider", column = @Column(name =
"PROVIDER_ID", nullable = false))
@AttributeOverride(name = "operator.operatorId", column = @Column(name =
"OPERATOR_ID", nullable = false))
@AttributeOverride(name = "wholesalePrice", column = @Column(name =
"WHOLESALE_PRICE_AMOUNT", nullable = false))
SpecialPricePointPK wholesalePrice;
@NonFinal
String productId;
}
}{code}
h2. Tests
h3. Delete by Id
I run the following test :
{noformat} @Test
void shouldDeleteProduct() {
// given
String operatorId = "OPERATOR_1";
Provider provider = A;
SpecialOperator specialOperator = new SpecialOperator(provider, operatorId);
specialOperatorService.addOperator(specialOperator);
String wholesalePrice = "1 EUR";
SpecialPricePoint specialPricePoint = new SpecialPricePoint(specialOperator,
wholesalePrice);
specialPricePointService.addPricePoint(specialPricePoint);
String productId = "PRODUCT_1";
SpecialProduct specialProduct = new SpecialProduct(productId, specialPricePoint);
Optional<SpecialPricePoint> pricePoint =
specialPricePointService.getPricePoint(provider, operatorId, wholesalePrice);
assertThat(pricePoint).isNotEmpty();
specialProductService.addProduct(specialProduct);
SpecialProduct product = specialProductService.getProduct(provider, operatorId,
wholesalePrice, productId);
assertThat(product).isNotNull();
// when
specialProductService.deleteProduct(provider, operatorId, wholesalePrice, productId);
// then
SpecialProduct product1 = specialProductService.getProduct(provider, operatorId,
wholesalePrice, productId);
assertThat(product1).isNull();
}{noformat}
When I run it with L2 cache enabled ( see {{SpecialProductServiceTest}} in the sources)
the product is deleted but when I run this test without cache (see
{{SpecialProductServiceWithoutCacheTest}} in the sources) the product is not being
deleted.
h3. Delete All
{noformat} @Test
void shouldDeleteAllProducts() {
// given
String operatorId = "OPERATOR_1";
Provider provider = A;
SpecialOperator specialOperator = new SpecialOperator(provider, operatorId);
specialOperatorService.addOperator(specialOperator);
String wholesalePrice = "1 EUR";
SpecialPricePoint specialPricePoint = new SpecialPricePoint(specialOperator,
wholesalePrice);
specialPricePointService.addPricePoint(specialPricePoint);
String productId = "PRODUCT_1";
SpecialProduct specialProduct = new SpecialProduct(productId, specialPricePoint);
Optional<SpecialPricePoint> pricePoint =
specialPricePointService.getPricePoint(provider, operatorId, wholesalePrice);
assertThat(pricePoint).isNotEmpty();
specialProductService.addProduct(specialProduct);
SpecialProduct product = specialProductService.getProduct(provider, operatorId,
wholesalePrice, productId);
assertThat(product).isNotNull();
// when
specialProductService.deleteAllProducts();
// then
SpecialProduct product1 = specialProductService.getProduct(provider, operatorId,
wholesalePrice, productId);
assertThat(product1).isNull();
}{noformat}
This time whether cache is enabled or not (test is present in
{{SpecialProductServiceTest}} and {{SpecialProductServiceWithoutCacheTest}}) , the product
is not deleted.
h2. Sources
Sources can be found in branch {{reversdOneToOne}} on
[
https://github.com/emouty/hibernate-issues/tree/reversedOneToOne|https://...]
(
https://hibernate.atlassian.net/browse/HHH-16946#add-comment?atlOrigin=ey...
) Add Comment (
https://hibernate.atlassian.net/browse/HHH-16946#add-comment?atlOrigin=ey...
)
Get Jira notifications on your phone! Download the Jira Cloud app for Android (
https://play.google.com/store/apps/details?id=com.atlassian.android.jira....
) or iOS (
https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=Em...
) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100232- sha1:33a7c72 )