# Hibernate OneToMany difference in produced queries when migrating from 5 to 6

**URL:** <https://discourse.hibernate.org/t/hibernate-onetomany-difference-in-produced-queries-when-migrating-from-5-to-6/7105>\
**Category:** Hibernate ORM\
**Created:** [January 5, 2023, 10:31am UTC](https://discourse.hibernate.org/t/hibernate-onetomany-difference-in-produced-queries-when-migrating-from-5-to-6/7105 "2023-01-05T10:31:07Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![zivkovicjan](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/zivkovicjan/32/2196_2.png) [@zivkovicjan](https://discourse.hibernate.org/u/zivkovicjan)\
**Post date:** [January 5, 2023, 10:31am UTC](https://discourse.hibernate.org/t/hibernate-onetomany-difference-in-produced-queries-when-migrating-from-5-to-6/7105/1 "2023-01-05T10:31:07Z")

</div>

I seem to have stumbled on a weird issue when migrating a project from hibernate 5 to 6.

The same code on both produces different queries and consequently different results. Hibernate 6 produces an extra “delete” query that deletes the record right after insertion, while hibernate 5 does not.

Hibernate 5 produced queries:

```auto
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into "user" (name, surname, id) values (?, ?, ?)
Hibernate: select count(*) as col_0_0_ from "user" user0_
// User count: 1
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into device (name, id) values (?, ?)
Hibernate: select count(*) as col_0_0_ from device device0_
// Device count: 1
Hibernate: select user0_.id as id1_2_, user0_.name as name2_2_, user0_.surname as surname3_2_ from "user" user0_ where user0_.name=?
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into provisioning_info (device_id, user_id, id) values (?, ?, ?)
Hibernate: select count(*) as col_0_0_ from provisioning_info provisioni0_
// ProvisioningInfo count: 1

```

Hibernate 6 produced queries:

```auto
Hibernate: select user_seq.nextval from dual
Hibernate: insert into "user" (name, surname, id) values (?, ?, ?)
Hibernate: select count(*) from "user" u1_0
// User count: 1
Hibernate: select device_seq.nextval from dual
Hibernate: insert into device (name, id) values (?, ?)
Hibernate: select count(*) from device d1_0
// Device count: 1
Hibernate: select u1_0.id,u1_0.name,u1_0.surname from "user" u1_0 where u1_0.name=?
Hibernate: select provisioning_info_seq.nextval from dual
Hibernate: insert into provisioning_info (device_id, user_id, id) values (?, ?, ?)
Hibernate: delete from provisioning_info where user_id=?
Hibernate: select count(*) from provisioning_info p1_0
// ProvisioningInfo count: 0

```

Now, why does hibernate 6 produce an extra query `delete from provisioning_info where user_id=?`?  
Are we using hibernate incorrectly? What can I do to make it work on hibernate 6?

Models:

```auto
@Entity
@Table(name = "\"user\"")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    private String surname;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "provisioning_info",
        joinColumns = @JoinColumn(name = "user_id", nullable = false, updatable = false),
        inverseJoinColumns = @JoinColumn(name = "device_id", nullable = false, updatable = false)
    )
    private Set<Device> devices;
	
	// Omitted getters and setters
}

@Entity
@Table(name = "device")
public class Device {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    // Omitted getters and setters
}

@Entity
@Table(name = "provisioning_info")
public class ProvisioningInfo {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne
    @JoinColumn(name = "device_id")
    private Device device;

    // Omitted getters and setters	
}

```

Repositories:

```auto
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByName(String name);
}

public interface DeviceRepository extends JpaRepository<Device, Long> {
    Optional<Device> findByName(String name);
}

public interface ProvisioningInfoRepository extends JpaRepository<ProvisioningInfo, Long> {
}

```

Test code:

```auto
@Transactional
public void test() {
	User user = new User();
	user.setName("name");
	user.setSurname("surname");

	userRepository.save(user);

	System.out.println("User count: " + userRepository.count());

	Device device = new Device();
	device.setName("device");

	deviceRepository.save(device);

	System.out.println("Device count: " + deviceRepository.count());

	user = userRepository.findByName(user.getName()).orElseThrow();
	device = deviceRepository.findById(device.getId()).orElseThrow();

	ProvisioningInfo pi = new ProvisioningInfo();
	pi.setUser(user);
	pi.setDevice(device);

	provisioningInfoRepository.save(pi);

	System.out.println("ProvisioningInfo count: " + provisioningInfoRepository.count());
}

```

Database creation sql (Oracle):

```auto
create sequence hibernate_sequence start with 1 increment by 1;
create table device (id number(19,0) not null, name varchar2(255), primary key (id));
create table provisioning_info (id number(19,0) not null, device_id number(19,0) not null, user_id number(19,0) not null, primary key (user_id, device_id));
create table "user" (id number(19,0) not null, name varchar2(255), surname varchar2(255), primary key (id));
alter table provisioning_info add constraint FKaa9goxi73eawrckas10a5u3r8 foreign key (device_id) references device;
alter table provisioning_info add constraint FKht6iwjjedykqjupratrdgpf6d foreign key (user_id) references "user";

```

Thank you in advance.

---

<div class="post-metadata">

**Author:** ![beikov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/beikov/32/258_2.png) [@beikov](https://discourse.hibernate.org/u/beikov)\
**Post date:** [January 5, 2023, 5:50pm UTC](https://discourse.hibernate.org/t/hibernate-onetomany-difference-in-produced-queries-when-migrating-from-5-to-6/7105/2 "2023-01-05T17:50:12Z")

</div>

Mapping a join table of an association as an entity and trying to manage both in one transaction is generally a recipe for disaster. If you have an entity for the join table, then use that. Use this mapping:

```java
@Entity
@Table(name = "\"user\"")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    private String surname;

    @OneToMany(mappedBy = "user")
    private Set<ProvisioningInfo> devices;
}

```

---

<div class="post-metadata">

**Author:** ![zivkovicjan](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/zivkovicjan/32/2196_2.png) [@zivkovicjan](https://discourse.hibernate.org/u/zivkovicjan)\
**Post date:** [January 6, 2023, 10:29am UTC](https://discourse.hibernate.org/t/hibernate-onetomany-difference-in-produced-queries-when-migrating-from-5-to-6/7105/3 "2023-01-06T10:29:39Z")

</div>

Thank you, that indeed does work.

Might I just ask why the “delete sql” disappears, if I simply replace  
`user = userRepository.findByName(user.getName()).orElseThrow();`  
with  
`user = userRepository.findById(user.getId()).orElseThrow();`

or if you could explain why this extra “delete sql” is even generated. I’m trying to understand it better, to never make this mistake again.

Best regards and thank you again.

---

<div class="post-metadata">

**Author:** ![beikov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/beikov/32/258_2.png) [@beikov](https://discourse.hibernate.org/u/beikov)\
**Post date:** [January 9, 2023, 9:22am UTC](https://discourse.hibernate.org/t/hibernate-onetomany-difference-in-produced-queries-when-migrating-from-5-to-6/7105/4 "2023-01-09T09:22:23Z")

</div>

Calling `userRepository.count()` triggers Hibernate to flush/persist the `User`, so after this point, it is managed, but with an empty `devices` collection.  
When you save a `ProvisioningInfo`, the collection would actually contain data, but the managed collection is not initialized yet, so at the end of the transaction, when Hibernate flushes data, it sees that the `devices` of the managed entity is an empty collection, so it issues a `delete` statement.

This kind of relates to [[Closed] ignoring collections on merge() · Discussion #4398 · hibernate/hibernate-orm · GitHub](https://github.com/hibernate/hibernate-orm/discussions/4398) in the sense that you should actually initialize a lazy collection instead of an empty one to avoid this case, but then you also fail to maintain “both sides” of the relationship, i.e. when you persist a `ProvisioningInfo` entity, you should also add that to the respective collection of the `User`.

By using the mapping I proposed to you, a flush of a `User` now doesn’t manage that join table `provisioning_info` anymore, so there are no surprises.  
TLDR, just don’t use a `@JoinTable` when you manage the contents of that table through an entity.
