Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 4 posts ] 
Author Message
 Post subject: Unnecessary SQL updates triggered for all elements in map
PostPosted: Wed Sep 13, 2017 11:15 am 
Newbie

Joined: Wed Sep 13, 2017 10:14 am
Posts: 2
Dear Hibernate community

I've spent already a lot of time trying to figure out why hibernate triggers SQL statements for elements in a map which have not been touched.
I have the following scenario. I have a one-to-many bidrectional relationship between two objects. The child objects are stored in a map. Hibernate triggers update statements for all elements in a map if a new object has been added to this map.
I don't understand why this is necessary.

hibernate-core version is 5.2.7.Final

My database consists of two tables: person and task. These are the mappings:

Person.hbm.xml:
Code:
<hibernate-mapping>
    <class name="persistency.test.Person" table="person" lazy="true">
        <id name="dbKey" type="long" access="field" column="dbkey">
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String" length="20" access="field" />

        <map name="tasks" cascade="all-delete-orphan" inverse="true" access="field">
            <key column="person_dbkey" />
            <map-key type="string" column="name" />
            <one-to-many class="persistency.test.Task" />
        </map>
    </class>
</hibernate-mapping>


Person.java
Code:
public class Person {

    private String name;
    private long dbKey;

    private Map<String, Task> tasks = new HashMap<>();

    public void addTask(Task task) {
        task.setOwner(this);
        this.tasks.put(task.getName(), task);
    }
}


Task.hbm.xml:
Code:
<hibernate-mapping>
    <class name="persistency.test.Task" table="task" lazy="true">
        <id name="dbKey" type="long" access="field" column="dbkey">
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String" length="20" access="field" />
        <property name="description" type="java.lang.String" length="100" access="field" />

        <many-to-one name="owner" column="person_dbkey" class="persistency.test.Person" access="field" />

    </class>
</hibernate-mapping>


Task.java
Code:
public class Task {

    private String name;
    private long dbKey;
    private Person owner;
    private String description;

    public Task(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public Task() { }

    public String getName() {
        return name;
    }

    public void setOwner(Person owner) {
        this.owner = owner;
    }
}


Code:
session.beginTransaction();
Person person = session.load(Person.class, 139000l);
person.addTask(new Task("Cleaning", "Floor, Roof"));

session.getTransaction().commit();


Triggered SQL statements by hibernate:
Code:
select person0_.dbkey as dbkey1_0_0_, person0_.name as name2_0_0_ from XHM_2.person person0_ where person0_.dbkey=?
select tasks0_.person_dbkey as person_dbkey4_1_0_, tasks0_.dbkey as dbkey1_1_0_, tasks0_.name as name2_0_, tasks0_.dbkey as dbkey1_1_1_, tasks0_.name as name2_1_1_, tasks0_.description as description3_1_1_, tasks0_.person_dbkey as person_dbkey4_1_1_ from XHM_2.task tasks0_ where tasks0_.person_dbkey=?

select XHM_2.hibernate_sequence.nextval from dual
insert into XHM_2.task (name, description, person_dbkey, dbkey) values ('Cleaning', 'Floor, Roof', '139000', '224000')
update XHM_2.task set name='Eating' where dbkey='221000'
update XHM_2.task set name='Cooking' where dbkey='218000'
update XHM_2.task set name='Reading' where dbkey='225000'
update XHM_2.task set name='Cleaning' where dbkey='224000'


Person table
Code:
DBKEY  | NAME
139000 | John


Task table after insert
Code:
DBKEY  | NAME     | PERSON_DBKEY  | DESCRIPTION
224000 | Cleaning | 139000        | Floor, Roof
221000 | Eating   | 139000        | Food
218000 | Cooking  | 139000        | Food
225000 | Reading  | 139000        | Food


I don't understand why hibernate triggers the last four update statements. They update the names of the task elements to the same names they had before. There are four update statements because the tasks table currently contains four elements. The task name is used as key in person.tasks map.

We have this problem in production with a large map and it causes a performance issue if hibernate triggers thousands of update statements if only one element has been added to a map.

Any help is very much appreciated


Top
 Profile  
 
 Post subject: Re: Unnecessary sql updates are triggerd for all elements in map
PostPosted: Wed Sep 13, 2017 11:59 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1433
The HBM mappings are considered legacy by now. Try with annotations as depicted in the User Guide.

I don't remember seeing this behavior with annotations.

So, if I run this test on GitHub:

Code:
doInJPA( this::entityManagerFactory, entityManager -> {
   Person person = new Person( 1L );
   LocalDateTime now = LocalDateTime.now();
   person.addPhone( new Phone( PhoneType.LAND_LINE, "028-234-9876", Timestamp.valueOf( now ) ) );
   person.addPhone( new Phone( PhoneType.MOBILE, "072-122-9876", Timestamp.valueOf( now.minusDays( 1 ) ) ) );
   entityManager.persist( person );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
   Person person = entityManager.find( Person.class, 1L );
   Map<PhoneType, Phone> phones = person.getPhoneRegister();
   person.addPhone( new Phone( PhoneType.FAX, "072-122-2222", Timestamp.valueOf( LocalDateTime.now().minusDays( 1 ) ) ) );
   Assert.assertEquals( 3, phones.size() );
} );


There's just one SQL INSERT:

Code:
    insert
    into
        Phone
        ("number", person_id, since, type, id)
    values
        (?, ?, ?, ?, ?)

19:08:04,198 TRACE AbstractEntityPersister:2709 - Dehydrating entity: [org.hibernate.userguide.collections.BidirectionalMapTest$Phone#3]
19:08:04,199 TRACE BasicBinder:65 - binding parameter [1] as [VARCHAR] - [072-122-2222]
19:08:04,199 TRACE BasicBinder:65 - binding parameter [2] as [BIGINT] - [1]
19:08:04,199 TRACE BasicBinder:65 - binding parameter [3] as [TIMESTAMP] - [2017-09-12 19:08:04.167]


So, you can try it yourself. You just have to grab the Hibernate ORM repo and run the test.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: Unnecessary sql updates are triggerd for all elements in map
PostPosted: Thu Sep 14, 2017 8:25 am 
Newbie

Joined: Wed Sep 13, 2017 10:14 am
Posts: 2
Hello Vlad

Thank you very much for replying. Sorry, I guess I have not clearly explained my problem. I understand why Hibernate triggers the insert statement. The thing which confuses me are the update statements . In my example I added one new element to a map which already containes three elements. Hibernate then triggers db updates for the previous three elements as well.

In changed from hbm mappings to annotation and I don't have the unnecessary update statements anymore.
The example I provided here is just to show the problem. The application which I am maintaining isn't that small and changing from hbm mappings to annotations will take a long time.
The unnecessary update statements could be a bug in Hibernate because the problem only occures with hbm mappings. Are bug reports for hbm mappings still accepted?


Top
 Profile  
 
 Post subject: Re: Unnecessary sql updates are triggerd for all elements in map
PostPosted: Thu Sep 14, 2017 9:16 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1433
The bug is going to be accepted, but the priority is way lower than for the JPA-based mappings. Starting from 6.0, it might be that HBM mappings get deprecated since we want to offer an extension based on the JPA xml mapping files.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.