简体   繁体   中英

Hibernate: merge two related entities

I have this entity:

@Entity
public class Node
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    protected Long id;

    @Column
    private String name;

    @ManyToOne
    @JoinColumn(name = "NODE_ID")
    private Node parent;

    @OneToMany(mappedBy = "parent")
    private List<Node> children = new ArrayList();
}

Suppose there is an external source that generates a tree, and that I want to import (read make persistent) this whole tree:

public void importTree()
{
    Node root = someExternalSource.receiveTree();

    // service.preOrderSave(root);  ERROR - Not-null property references a transient value
    // service.postOrderSave(root); ERROR - Not-null property references a transient value

    // no way out :(
}

Note that I absolutely want to avoid cascade because this is an exceptional case, and it would interfere with the normal application logic.

What I don't understand is why TransientPropertyValueException is thrown when calling em.persist / em.merge instead on transaction commit.

However, is there any way out?

Thanks


this is the rest of the code:

public Node receiveTree()
{
    // dummy code

    Node root = new Node();
    root.setName("root");

    Node child = new Node()
    child.setName("child");

    root.getChildren().add(child);
    child.setParent(root);

    return root;
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void preOrderSave(Node node)
{
    em.persist(node);

    for(Node child : node.getChildren())
    {
        preOrderSave(child);
    }
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void postOrderSave(Node node)
{
    for(Node child : node.getChildren())
    {
        postOrderSave(child);
    }

    em.persist(node);
}

It seems this problem is caused by @GeneratedValue(strategy = GenerationType.IDENTITY) , which forces hibernate to flush immediately the insert statement (or something like this, I haven't done enough code-digging).

Switching to another strategy, solves it:

/** The id. */
@XmlTransient
@Id
// NOK, forces immediate flush
// @GeneratedValue(strategy = GenerationType.IDENTITY)
// NOK, in my case defaults to IDENTITY
// @GeneratedValue(strategy = GenerationType.AUTO, generator = "sequence_generator")
// NOK, MySQL doesn't support sequences...
// @SequenceGenerator(name = "sequence_generator", sequenceName = "GLOBAL_SEQUENCE", allocationSize = 100, initialValue = 1000) 
// OK, but ACID is lost
@GeneratedValue(strategy = GenerationType.TABLE, generator = "table_generator")
@TableGenerator(name = "table_generator", table = "SEQUENCE_GENERATOR", pkColumnName = "NAME", valueColumnName = "NEXT_VAL", allocationSize = 100)
@Column(name = "ID")
protected Long id;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM