I'd like to use JPA CASCADE to Persist parent and its children at the same time. The parent class (Filter
) has a composite key (PKFilter
). Aldo, the children class (FilterRule
) has its composite key (PKFilterRule
). The primary key classes are annotated with @Embeddable
and they are referenced as @EmbeddedId
.
PKFilter:
@Getter
@Setter
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@SuppressWarnings("JpaDataSourceORMInspection")
@EqualsAndHashCode
public class PkFilter implements Serializable {
@Column(name = "CUSTOMER_ID")
private int customerId;
@Column(name = "COMPANY_ID")
private int companyId;
@Column(name = "FILTER_ID")
private String filterId;
}
PKFilterRule:
@Getter
@Setter
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@SuppressWarnings("JpaDataSourceORMInspection")
@EqualsAndHashCode
public class PkFilterRule implements Serializable {
@Column(name = "CUSTOMER_ID")
private int customerId;
@Column(name = "COMPANY_ID")
private int companyId;
@Column(name = "FILTER_ID")
private String filterId;
@Column(name = "FILTER_RULE")
private String filterRule;
@Column(name = "FILTER_KEY")
private String filterKey;
}
Filter
@Getter
@Setter
@Entity
@EqualsAndHashCode
@Table( name = "IOND_FILTERS")
public class Filter {
@EmbeddedId
private PkFilter pkFilter;
@Column(name = "FILTER_TARGET")
private String filterTarget;
@Column(name = "FILTER_STATUS")
private String filterStatus;
@OneToMany(mappedBy = "filter", cascade = CascadeType.ALL)
private List<FilterRule> rules;
}
FilterRule
@Getter
@Setter
@Entity
@EqualsAndHashCode
@Table( name = "IOND_FILTERS_RULES")
public class FilterRule {
@EmbeddedId
private PkFilterRule pkFilterRule;
@MapsId
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
@JoinColumn(name = "COMPANY_ID", referencedColumnName = "COMPANY_ID")
@JoinColumn(name = "FILTER_ID", referencedColumnName = "FILTER_ID")
private Filter filter;
}
As a test, we are trying to create a Filter with a FilterItem and then, persist the Filter object:
@RunWith(SpringRunner.class)
@SpringBootTest()
@TestPropertySource(locations = {"classpath:application-test.properties"})
public class ProductsApplicationTests {
@Autowired
private FilterRepository filterRepository;
@Test
@Transactional
@Commit
public void test() {
var uuid = UUID.randomUUID().toString();
//Cria um novo Filtro
var filter = new Filter();
filter.setPkFilter(
new PkFilter(208, 210, uuid)
);
filter.setFilterTarget("ET");
filter.setFilterStatus("EN");
//Adiciona alguns itens
var filterRule = new FilterRule();
filterRule.setPkFilterRule(
new PkFilterRule(208, 210, uuid, "RULE", "KEY")
);
filterRule.setFilter(filter);
filter.setRules(Collections.singletonList(filterRule));
filterRepository.saveAndFlush(filter);
}
}
And, as exception, we got:
Caused by: java.lang.IllegalArgumentException: Can not set int field api.products.domain.model.keys.PkFilterRule.companyId to api.products.domain.model.keys.PkFilter
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at java.base/jdk.internal.reflect.UnsafeIntegerFieldAccessorImpl.getInt(UnsafeIntegerFieldAccessorImpl.java:56)
at java.base/java.lang.reflect.Field.getInt(Field.java:594)
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:62)
... 97 more
What are we missing? Thanks a lot!
Found the solution:
Changed PkFilterRule to use PkFilter as below:
@Getter
@Setter
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@SuppressWarnings("JpaDataSourceORMInspection")
@EqualsAndHashCode
public class PkFilterRule implements Serializable {
private PkFilter pkFilter; //<-------HERE
@Column(name = "FILTER_RULE")
private String filterRule;
@Column(name = "FILTER_KEY")
private String filterKey;
}
and then, changed the @MapsID as bellow:
@Getter
@Setter
@Entity
@EqualsAndHashCode
@Table(name = "IOND_FILTERS_RULES")
public class FilterRule {
@EmbeddedId
private PkFilterRule pkFilterRule;
@MapsId("pkFilter") //<------------------------------------ HERE
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
@JoinColumn(name = "COMPANY_ID", referencedColumnName = "COMPANY_ID")
@JoinColumn(name = "FILTER_ID", referencedColumnName = "FILTER_ID")
private Filter filter;
}
Last but not least, changed the tests:
@Test
@Transactional
@Commit
public void test() {
var uuid = UUID.randomUUID().toString();
//Cria um novo Filtro
var filter = new Filter();
filter.setPkFilter(
new PkFilter(208, 210, uuid)
);
filter.setFilterTarget("ET");
filter.setFilterStatus("EN");
//Adiciona alguns itens
var filterRule = new FilterRule();
filterRule.setPkFilterRule(
new PkFilterRule(filter.getPkFilter(), //<--------HERE
"RULE", "KEY")
);
filterRule.setFilter(filter);
filter.setRules(Collections.singletonList(filterRule));
filterRepository.saveAndFlush(filter);
}
And everything works as expected. Thanks!