diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3539/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3539/Fixture.cs
new file mode 100644
index 0000000000..342d688f1c
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3539/Fixture.cs
@@ -0,0 +1,85 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using NHibernate.Cfg;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3539
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class FixtureAsync : BugTestCase
+ {
+ protected override void Configure(Configuration configuration)
+ {
+ configuration.SetProperty(Cfg.Environment.GenerateStatistics, "true");
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = Sfi.OpenSession())
+ using (var t = s.BeginTransaction())
+ {
+ s.Delete("from Person");
+ t.Commit();
+ }
+
+ base.OnTearDown();
+ }
+
+ [Test]
+ public async Task TestComponentAsync()
+ {
+ int personId;
+ using (var s = Sfi.OpenSession())
+ using (var t = s.BeginTransaction())
+ {
+ var person = new Person(age: 20)
+ {
+ CardInfo = new("card1", "card2")
+ };
+ await (s.SaveAsync(person));
+ await (t.CommitAsync());
+ await (s.FlushAsync());
+ personId = person.Id;
+ }
+
+ using (var s = Sfi.OpenSession())
+ using (var t = s.BeginTransaction())
+ {
+ var restored = await (s.GetAsync(personId));
+
+ var oldCardInfo = restored.CardInfo;
+
+ Assert.That(oldCardInfo.GetCardsCopy().Contains("card1"));
+ Assert.That(oldCardInfo.GetCardsCopy().Contains("card2"));
+
+ var newCardInfo = new CardInfo("card1", "card2");
+
+ Assert.That(oldCardInfo.GetHashCode(), Is.EqualTo(newCardInfo.GetHashCode()));
+ Assert.That(oldCardInfo.Equals(newCardInfo));
+
+ restored.CardInfo = newCardInfo;
+
+ // Expected behaviour:
+ //
+ // At this point there should be no DML statements because newCardInfo
+ // is the same as the old one. But NHibernate will generate DELETE
+ // followed by 2 INSERT into OldCards table.
+
+ using (var x = new SqlLogSpy())
+ {
+ await (t.CommitAsync());
+ Assert.That(x.GetWholeLog(), Is.Empty);
+ }
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/CardInfo.cs b/src/NHibernate.Test/NHSpecificTest/GH3539/CardInfo.cs
new file mode 100644
index 0000000000..9596e2b61e
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3539/CardInfo.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace NHibernate.Test.NHSpecificTest.GH3539;
+
+public class CardInfo
+{
+ private readonly ISet _oldCards;
+
+ protected CardInfo() { }
+
+ public CardInfo(params string[] cards)
+ {
+ _oldCards = cards.ToHashSet();
+ }
+
+ public virtual ISet GetCardsCopy()
+ {
+ return _oldCards.ToHashSet();
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is null)
+ {
+ return false;
+ }
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+ if (obj.GetType() != GetType())
+ {
+ return false;
+ }
+ var other = (CardInfo) obj;
+ return _oldCards.SetEquals(other._oldCards);
+ }
+
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ foreach (var card in _oldCards) hashCode.Add(card);
+ return hashCode.ToHashCode();
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3539/Fixture.cs
new file mode 100644
index 0000000000..2311b2e50e
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3539/Fixture.cs
@@ -0,0 +1,74 @@
+using NHibernate.Cfg;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3539
+{
+ [TestFixture]
+ public class Fixture : BugTestCase
+ {
+ protected override void Configure(Configuration configuration)
+ {
+ configuration.SetProperty(Cfg.Environment.GenerateStatistics, "true");
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = Sfi.OpenSession())
+ using (var t = s.BeginTransaction())
+ {
+ s.Delete("from Person");
+ t.Commit();
+ }
+
+ base.OnTearDown();
+ }
+
+ [Test]
+ public void TestComponent()
+ {
+ int personId;
+ using (var s = Sfi.OpenSession())
+ using (var t = s.BeginTransaction())
+ {
+ var person = new Person(age: 20)
+ {
+ CardInfo = new("card1", "card2")
+ };
+ s.Save(person);
+ t.Commit();
+ s.Flush();
+ personId = person.Id;
+ }
+
+ using (var s = Sfi.OpenSession())
+ using (var t = s.BeginTransaction())
+ {
+ var restored = s.Get(personId);
+
+ var oldCardInfo = restored.CardInfo;
+
+ Assert.That(oldCardInfo.GetCardsCopy().Contains("card1"));
+ Assert.That(oldCardInfo.GetCardsCopy().Contains("card2"));
+
+ var newCardInfo = new CardInfo("card1", "card2");
+
+ Assert.That(oldCardInfo.GetHashCode(), Is.EqualTo(newCardInfo.GetHashCode()));
+ Assert.That(oldCardInfo.Equals(newCardInfo));
+
+ restored.CardInfo = newCardInfo;
+
+ // Expected behaviour:
+ //
+ // At this point there should be no DML statements because newCardInfo
+ // is the same as the old one. But NHibernate will generate DELETE
+ // followed by 2 INSERT into OldCards table.
+
+ using (var x = new SqlLogSpy())
+ {
+ t.Commit();
+ Assert.That(x.GetWholeLog(), Is.Empty);
+ }
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH3539/Mappings.hbm.xml
new file mode 100644
index 0000000000..a153459cc0
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3539/Mappings.hbm.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/Person.cs b/src/NHibernate.Test/NHSpecificTest/GH3539/Person.cs
new file mode 100644
index 0000000000..ce4fe44848
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3539/Person.cs
@@ -0,0 +1,35 @@
+namespace NHibernate.Test.NHSpecificTest.GH3539
+{
+ public class Person
+ {
+ private int _id;
+ private int _age;
+ private CardInfo _cardInfo;
+
+ protected Person() { }
+
+ public Person(int age)
+ {
+ _cardInfo = new();
+ _age = age;
+ }
+
+ public virtual int Id
+ {
+ get { return _id; }
+ set { _id = value; }
+ }
+
+ public virtual int Age
+ {
+ get { return _age; }
+ set { _age = value; }
+ }
+
+ public virtual CardInfo CardInfo
+ {
+ get { return _cardInfo; }
+ set { _cardInfo = value; }
+ }
+ }
+}