Hello, I'm migrating from hibernate 5.6.15 to hibernate 6.2 and I have noticed some issues. it seems that I can’t use an enum as a string with `@Enumerated( STRING )` when my enum is part of a composite primary key which itself is reference another composite primary key. Entities Product
@Getter
@IdClass(ProductPK.class)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
@NoArgsConstructor(access = PROTECTED)
@Entity
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
@Cacheable
@Cache(usage = READ_WRITE)
@Table(name = "PRODUCTS")
public class Product {
public Product(String productId, Operator operator) {
this.productId = productId;
this.operator = operator;
}
public Product(String productId, Operator operator, Benefits benefits) {
this.productId = productId;
this.operator = operator;
this.benefits = benefits;
}
@EqualsAndHashCode.Include
@ToString.Include
@Id
@Column(name = "PRODUCT_ID", nullable = false)
private String productId;
@Id
@EqualsAndHashCode.Include
@ToString.Include
@Getter
@Setter
@Cache(usage = READ_WRITE)
@ManyToOne(fetch = LAZY, optional = false)
@JoinColumn(name = "OPERATOR_ID", nullable = false)
@JoinColumn(name = "COUNTRY", nullable = false, columnDefinition = "varchar")
private Operator operator;
@Column(name = "DESCRIPTION")
@Setter
private String description;
@Embedded
private Benefits benefits;
@EqualsAndHashCode
@ToString
@Embeddable
@NoArgsConstructor(access = PROTECTED)
public static class ProductPK implements Serializable {
private String productId;
@Embedded
private Operator.OperatorPK operator;
public ProductPK(String productId, Operator.OperatorPK operator) {
this.productId = productId;
this.operator = operator;
}
public ProductPK(String productId, String operatorID, Country country) {
this.productId = productId;
this.operator = new Operator.OperatorPK(operatorID, country);
}
}
@Embeddable
@Value
@AllArgsConstructor
@NoArgsConstructor(access = PROTECTED)
public static class Benefits {
@Embedded
@NonFinal
@Setter
TypeOneBenefit credit;
@Embedded
@NonFinal
@Setter
TypeTwoBenefit data;
}
@Embeddable
@Value
@AllArgsConstructor
@NoArgsConstructor(access = PROTECTED)
public static class TypeOneBenefit {
@NonFinal
@Column(name = "BENEFIT_ONE_BASE_AMOUNT")
BigDecimal baseAmount;
}
@Embeddable
@Value
@AllArgsConstructor
@NoArgsConstructor(access = PROTECTED)
public static class TypeTwoBenefit {
@NonFinal
@Column(name = "BENEFIT_TWO_BASE_AMOUNT")
String baseAmount;
}
}
Operator
@Getter
@Entity
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor(access = PROTECTED)
@Table(name = "OPERATORS")
@IdClass(Operator.OperatorPK.class)
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
@Cacheable
@Cache(usage = READ_WRITE)
public class Operator {
@EqualsAndHashCode.Include
@ToString.Include
@Id
@Enumerated(STRING)
@Column(name = "COUNTRY", nullable = false, columnDefinition = "varchar")
private Country country;
@EqualsAndHashCode.Include
@ToString.Include
@Id
@Column(name = "OPERATOR_ID", nullable = false)
private String operatorId;
@ManyToOne
@JoinColumn(name = "meta_operator_id", referencedColumnName = "ID")
private MetaOperator metaOperator;
@OneToMany(mappedBy = "operator", cascade = { CascadeType.ALL }, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Product> products = new ArrayList<>();
public Operator(String operatorId) {
this.operatorId = operatorId;
this.country = USA;
}
public void setMetaOperator(MetaOperator metaOperator) {
this.metaOperator = metaOperator;
}
public void setProducts(List<Product> products) {
this.products = products;
}
@Embeddable
@Value
@AllArgsConstructor
@NoArgsConstructor(access = PROTECTED)
public static class OperatorPK implements Serializable {
@NonFinal
String operatorId;
@NonFinal
@Enumerated(STRING)
Country country;
}
}
note : At first, I did not specify `columnDefinition = "varchar"` on Operator nor Product but I would get
even though I had @Enumerated(STRING) on Operator.country Country
public enum Country {
USA,
FRA;
}
Tests addProductTest
@Test
void addProductTest() {
String string = "ID";
String operatorID = "operatorID";
ProductPK id = new ProductPK(string, operatorID, USA);
String test = "test";
Operator operator = new Operator(operatorID);
operatorService.addOperator(operator);
Product product = new Product(string, operator);
product.setDescription(test);
productService.addProduct(product);
Optional<Product> byId = productService.getProduct(id);
assertThat(byId.orElseThrow().getDescription()).isEqualTo(test);
Optional<Product> byId2 = productService.readProduct(id);
assertThat(byId2.orElseThrow().getOperator().getOperatorId()).isEqualTo(operatorID);
}
an exception occurs when productService.addProduct(product); is committed :
shouldDeleteOperator
@Test
@Order(2)
void shouldDeleteOperator() {
String operatorID = "operatorID2";
Operator operator = new Operator(operatorID);
operatorDao.save(operator);
operatorService.deleteOperator(new Operator.OperatorPK(operatorID, USA));
Optional<Operator> byId2 = operatorService.getOperator(operatorID);
assertThat(byId2).isEmpty();
}
this one fails when committing the transaction for operatorService.deleteOperator
Sources source code can be found in branch cant_use_enum_as_string_in_join_columns : https://github.com/emouty/hibernate-issues/tree/cant_use_enum_as_string_in_join_columns for information all the tests in OperatorServiceTest, ProductServiceTest, ProductServiceWithCacheTest are failing. |