jpa 有没有现成的解决方案可以按节点持久化整个对象图?

eqqqjvef  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(207)

有没有一些库可以粗略地实现这个接口?

public interface Persister {
    /** Persists the whole graph represented by graphNode */
    <T> void persistGraph(T graphNode);
}

字符串
我想以正确的顺序持久化任何给定图的所有JPA实体,而不需要手动执行。我希望它能在我的测试中取代手动@Sql脚本,如下面的脚本(为简洁起见进行了修剪)。它容易出错,而且不是OOP

INSERT INTO PUBLIC.operation_type
VALUES ('e3376ede-920b-4f48-abda-b565dd29a102', 'REPLENISHMENT', true);
INSERT INTO PUBLIC.operation_type
VALUES ('595cef39-29f3-4bf9-b9e1-30198a1d3df1', 'PAYMENT', false);
INSERT INTO PUBLIC.operation_type
VALUES ('f806a8b9-82dd-444f-9e93-429e1e42d480', 'TRANSFER', false);
INSERT INTO PUBLIC.operation_type
VALUES ('b02d27b1-3716-47c6-8160-d7e9ca4b6f6a', 'OTHER_EXPENDITURE', false);

INSERT INTO PUBLIC.PRODUCT
VALUES (1, 'product_name', 100, 100000, '810', 5, 10, false, true, true, false, 1, 5, true, 'some_details', 'ANNUITY',
        4, false, '2', '3.2', '5.0', false, 'CONSUMER'),
       (2, 'product_name', 200, 200000, '810', 2, 8, false, true, true, false, 1, 5, false, 'some_details', 'ANNUITY',
        4, false, '2', '3.2', '5.0', false, 'CONSUMER');

INSERT INTO PUBLIC.CREDIT_ORDER
VALUES ('00000000-0000-0001-0000-000000000001', '1', '00000000-0000-0001-0000-000000000001', 1, 'PENDING', 100000, 12,
        '2020-01-02', 20000, 20000, '123456');
INSERT INTO PUBLIC.CREDIT_ORDER
VALUES ('00000000-0000-0001-0000-000000000002', '2', '00000000-0000-0001-0000-000000000001', 1, 'PENDING', 200000, 12,
        '2020-04-23', 20000, 40000, '123456');
INSERT INTO PUBLIC.CREDIT_ORDER
VALUES ('00000000-0000-0001-0000-000000000003', '3', '00000000-0000-0001-0000-000000000001', 2, 'PENDING', 100000, 12,
        '2020-10-16', 20000, 20000, '123456');

-- and so on


相反,我会用ddl-auto=create-drop初始化我的数据库(去掉“schema.sql”),并用“persister”插入示例行(去掉“data.sql”)

@DataJpaTest
@Import(value = BasicPersister.class)
class CardRepositoryTest {
    @Autowired
    private CardRepository cardRepository;
    private DataGenerator dataGenerator; // this is another test utility that I wrote, it generates random entities, fully initialized
    @Autowired
    private Persister persister;
    private Card expectedCard;
    @BeforeEach
    void setUp() {
        dataGenerator = new BasicDataGenerator();
        expectedCard = dataGenerator.generateRandomCard();
        persister.persistGraph(expectedCard); // instead of all SQL inserts
    }


为了给予您对对象图的理解,我将粘贴服务实体的框架(我只保留了关联和id字段):

/* I'll include these class annotations only for 
the first entity to keep it short */

@Entity
@Table(name = "card")
@Getter
@Setter(AccessLevel.PUBLIC)
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Card {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @ManyToOne(cascade = CascadeType.MERGE)
    @JoinColumn(name = "account_id", referencedColumnName = "id")
    private Account account;
}
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @OneToOne(cascade = CascadeType.MERGE)
    @JoinColumn(name = "credit_id", referencedColumnName = "id")
    private Credit credit;

    @OneToMany(mappedBy = "account")
    private List<Card> cards;

    @OneToMany(mappedBy = "account")
    private List<PaymentSchedule> paymentSchedules;
}
public class PaymentSchedule {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @ManyToOne(cascade = CascadeType.MERGE)
    @JoinColumn(name = "account_id")
    private Account account;
}
public class Credit {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @OneToOne(mappedBy = "credit")
    private Account account;

    @OneToOne(mappedBy = "credit")
    private Agreement agreement;

    @OneToOne
    @JoinColumn(name = "order_id")
    private CreditOrder creditOrder;
}
public class Agreement {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @OneToOne(cascade = CascadeType.MERGE)
    @JoinColumn(name = "credit_id", referencedColumnName = "id")
    private Credit credit;
}
public class CreditOrder {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private UUID id;

    @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
    @JoinColumn(name = "product_id")
    private Product product;
}
public class Product implements Serializable { // I have no idea why it implements Serializable

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;
    
    // it doesn't have any association fields
}

我希望能够通过持久化上述类型中的 any 对象来持久化整个图(除了Product,它没有与任何东西链接)。以下所有操作都应该类似:

persister.persistGraph(dataGenerator.generateRandomAgreement());
     persister.persistGraph(dataGenerator.generateRandomCredit());
     persister.persistGraph(dataGenerator.generateRandomAccount());


标准的JPA不会有帮助。

cardRepository.save(dataGenerator.generateRandomCard());


导致异常,如

Caused by: javax.persistence.EntityNotFoundException: Unable to find by.afinny.credit.entity.CreditOrder with id edbdc129-31a0-46db-a32e-fdcf472579d3


也就是说,Hibernate(我使用的ORM提供程序)不能自己确定对象持久化的正确顺序
几天来,我一直在尝试实现自己的解决方案,我甚至开始相信我正在取得进展(我计算了外键),但后来我发现了一些限制,试图解决它们,我的代码很快就变成了一堆难闻的东西。显然,这对我来说有点太复杂了。但是如果我不需要写任何代码,并且已经有一些开源库可以处理这些问题呢?

lvjbypge

lvjbypge1#

这就是讨论的起源。大多数时候,Hibernate可以自己确定顺序:
1.它使用级联中的信息来确定首先存储哪些字段。所以如果你说ClassA.fieldBcascade="persist",那么ORM知道fieldB必须在拥有对象之前被INSERT艾德处理。
1.此外,它使用集合inverse/mappedBy来确定集合中的元素是否可以INSERT艾德化而不填充FK(这可能是一个非空列,这将导致错误);或者我们应该首先持久化父对象,然后插入已经填充了FK的集合元素。
我现在想不出其他重要的因素,但这些似乎是最关键的。现在,如果所有这些都设置正确,ORM应该能够找出正确的持久化顺序。
然而,在实践中,会有一些情况,对象的图不能被持久化,因为级联没有(也不能!)被设置。例如,如果你有一个住在某个国家的用户,那么你的用户不能负责持久化国家,反之亦然。
我认为这也有可能造成一种冲突的情况,比如循环依赖,ORM将不按照你希望的顺序持久化实体。但我不记得在我的实践中是否是这种情况,或者我只是太懒而没有找到原因:)
总而言之,我不认为这个问题可以在一般情况下解决(不管有没有ORM)。如果你的图如此复杂,我会退回到显式地以正确的顺序持久化对象,而不是动态地持久化图。

相关问题