Eugene
Eugene

Reputation: 77

Hibernate duplicates record on INSERT with children

The database table structure is the following:

id        INT
extId     VARCHAR
name      VARCHAR
parent    INT (references self.id)

Here is the entity

@Entity
@Table(name = "categories")
public class Category
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @ManyToOne
    @JoinColumn(name = "parent", referencedColumnName = "id")
    private Category parent;

    @org.hibernate.annotations.OrderBy(clause = "name ASC")
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", cascade = CascadeType.ALL)
    private Set<Category> children = new HashSet<>();

    @Column(name = "extId")
    private String extId;

    @Column(name = "name")
    private String name;

    public void addChild(Category child)
    {
        child.setParent(this);
        this.children.add(child);
    }

    //getters and setters ...
}

In the very beginning there is only one entity:

{
    id: 0
    extId: ''
    name: 'Category'
    parent: null
    children: Set<Category>{/*empty*/}
}

This entity is fetched in the beginning of program and assigned to another class

Later this class performs addition of new Category as a child to existing root (the one that was fetched in the beginning) property

Addition of child is done like this:

Session session = HibernateSessionFactory.getInstance().getFactory().openSession();
//gather data
Category child = new Category();
//set name
//set extId

this.rootCategory.addChild(child);

Transaction tx = session.beginTransaction();
session.save(this.rootCategory);
tx.commit();
session.close();

After this instead of expected result in database:

Root(id(0), extId(''), parent(null), name('root'))
\-— Child(id(10), extId('art-0'), parent(0), name('child'))

I get the following result

Root(id(0), extId(''), parent(null), name('root'));
Root(id(10), extId(''), parent(null), name('root'))
\-— Child(id(11), extId('art-0'), parent(10), name('child'))

Notes:

What could be the reason of this behavior (assuming two different sessions)? And how can it be fixed?

Upvotes: 0

Views: 474

Answers (2)

Rocherlee
Rocherlee

Reputation: 2801

After fetching from db at the beginning of your program, rootCategory becomes a detached object.

Later when you want to use it in another session, you need to reattach it to this session. Instead of session.save(), you can use session.update()

this.rootCategory.addChild(child);
Transaction tx = session.beginTransaction();
session.update(this.rootCategory);
tx.commit();
session.close();

Upvotes: 1

Abhinav Singh
Abhinav Singh

Reputation: 76

If you are closing the session after fetching rootCategory then the rootCategory object becomes a detached object according to the hibernate lifecycle and using Session.save() on it will create a new row for it. Therefore you either need to fetch, add the child and save the rootCategory object in the same session or use refresh to tell hibernate that it is not a new object but one that is already saved.

Upvotes: 1

Related Questions