JPA 复合键和级联

JPA Composite Keys and Cascade

我想使用 JPA CASCADE 同时保留 parent 及其 children。 parent class (Filter) 有一个组合键 (PKFilter)。 Aldo,children class (FilterRule) 有它的组合键 (PKFilterRule)。主键 class 用 @Embeddable 注释,它们被引用为 @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;

}

过滤器

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

}

过滤规则

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

}

作为测试,我们正在尝试创建一个带有 FilterItem 的过滤器,然后保留该过滤器 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);

    }

}

而且,作为例外,我们得到了:

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

我们缺少什么? 非常感谢!

找到解决方案:

将 PkFilterRule 更改为使用 PkFilter,如下所示:

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

}

然后,将@MapsID 更改为如下所示:

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

}

最后但同样重要的是,更改了测试:

@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);

}

一切都按预期进行。 谢谢!