JPA 复合键和级联

问题描述

我想同时使用 JPA CASCADE 来持久化父级及其子级。父类 (Filter) 有一个复合键 (PKFilter)。奥尔多,子类 (FilterRule) 有其复合键 (PKFilterRule)。主键类用 @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 创建过滤器,然后持久化过滤器对象:

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

}

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