Skip to content

NH-3848 - Tests and fix for HQL #463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Feb 6, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/NH3848/HqlTestFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using NHibernate.Transform;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.NH3848
{
public class HqlTestFixture : TestFixture
{
[Ignore("We can't write such query using hql, because we can't use 'with' clause when we're fetching collection.")]
public override void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache()
{
throw new NotImplementedException();
}

protected override IList<Customer> GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session)
{
var query = session.CreateQuery("from Customer as c left join fetch c.Orders as o");
query.SetResultTransformer(new DistinctRootEntityResultTransformer());
return query.List<Customer>();
}

protected override IList<Customer> GetCustomersWithOrdersEagerLoaded(ISession session)
{
var query = session.CreateQuery("from Customer as c left join fetch c.Orders as o");
query.SetResultTransformer(new DistinctRootEntityResultTransformer());
return query.List<Customer>();
}

//We can't write such query using hql, because we can't use 'with' clause when we're fetching collection.
protected override IList<Customer> GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber)
{
throw new NotImplementedException();
}

protected override IList<Customer> GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber)
{
var numberParameterName = "number";
var query = session.CreateQuery(string.Format("from Customer as c " +
"left join fetch c.Orders as o " +
"where o.Number = :{0}", numberParameterName));
query.SetParameter(numberParameterName, orderNumber);
return query.List<Customer>();
}

protected override IList<Customer> GetCustomersByNameUsingWhereClause(ISession session, string customerName)
{
var nameParameterName = "name";
var query = session.CreateQuery(string.Format("from Customer as c " +
"left join fetch c.Orders as o " +
"where c.Name = :{0}", nameParameterName));
query.SetParameter(nameParameterName, customerName);
query.SetResultTransformer(new DistinctRootEntityResultTransformer());
return query.List<Customer>();
}

protected override IList<Customer> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName)
{
var nameParameterName = "name";
var numberParameterName = "number";
var query = session.CreateQuery(string.Format("from Customer as c " +
"left join fetch c.Orders as o " +
"where c.Name = :{0} and " +
"c.Id in (select c1.Id from Customer as c1 left join c1.Orders as o2 where o2.Number = :{1})", nameParameterName, numberParameterName));
query.SetParameter(nameParameterName, customerName);
query.SetParameter(numberParameterName, orderNumber);
query.SetResultTransformer(new DistinctRootEntityResultTransformer());
return query.List<Customer>();
}

protected override IList<Customer> GetAllCustomers(ISession session)
{
return session.CreateQuery("from Customer as c").List<Customer>();
}
}
}
266 changes: 266 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/NH3848/TestFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Cache;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.NH3848
{
public abstract class TestFixture : TestCaseMappingByCode
{
protected Customer Customer1;
protected Customer Customer2;
protected Customer Customer3;
protected const int OrderNumber = 2;

protected override HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.Class<Customer>(rc =>
{
rc.Table("Customers");
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Property(x => x.Name);
rc.Set(x => x.Orders, m =>
{
m.Inverse(true);
m.Key(k =>
{
k.Column("CustomerId");
k.NotNullable(true);
});
m.Cascade(Mapping.ByCode.Cascade.All.Include(Mapping.ByCode.Cascade.DeleteOrphans));
m.Cache(c => c.Usage(CacheUsage.ReadWrite));
}, m => m.OneToMany());
});

mapper.Class<Order>(rc =>
{
rc.Table("Orders");
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
rc.Property(x => x.Number);
rc.ManyToOne(x => x.Customer, m => m.Column("CustomerId"));
rc.Cache(c => c.Usage(CacheUsage.ReadWrite));
});

return mapper.CompileMappingForAllExplicitlyAddedEntities();
}

protected override void Configure(Configuration configuration)
{
base.Configure(configuration);
configuration.Cache(c =>
{
c.UseQueryCache = true;
c.Provider<HashtableCacheProvider>();
});
}

protected override void OnSetUp()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
Customer1 = new Customer { Name = "First Customer" };

Customer2 = new Customer { Name = "Second Customer" };

Customer3 = new Customer { Name = "Third Customer" };

var customer1Order1 = new Order { Number = 1 };
Customer1.AddOrder(customer1Order1);

var customer1Order2 = new Order { Number = 2 };
Customer1.AddOrder(customer1Order2);

var customer2Order1 = new Order { Number = 1 };
Customer2.AddOrder(customer2Order1);

var customer2Order2 = new Order { Number = 2 };
Customer2.AddOrder(customer2Order2);

var customer3Order1 = new Order { Number = 1 };
Customer3.AddOrder(customer3Order1);

session.Save(Customer1);
session.Save(Customer2);
session.Save(Customer3);

transaction.Commit();
session.Flush();
}
}

protected override void OnTearDown()
{
ClearSecondLevelCacheFor(typeof(Customer));
ClearCollectionCache<Customer>(n => n.Orders);
ClearSecondLevelCacheFor(typeof(Order));

using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
session.Delete("from System.Object");

session.Flush();
transaction.Commit();
}
}

[Test]
public virtual void ChildCollectionsFromLeftOuterJoinWithOnClauseRestrictionOnCollectionShouldNotBeInSecondLevelCache()
{
var firstSession = OpenSession();
var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingOnClause(firstSession, OrderNumber);

var secondSession = OpenSession();
var customers = GetAllCustomers(secondSession);

Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber)));
Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber)));
Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count(n => n.Number == OrderNumber)));

Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count));
Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count));
Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count));

firstSession.Dispose();
secondSession.Dispose();
}

[Test]
public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnCollectionShouldBeInSecondLevelCache()
{
var firstSession = OpenSession();
var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingWhereClause(firstSession, OrderNumber);

var secondSession = OpenSession();
var customers = GetAllCustomers(secondSession);

Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count(n => n.Number == OrderNumber)));
Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count(n => n.Number == OrderNumber)));

Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count));
Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count));
Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count));

firstSession.Dispose();
secondSession.Dispose();
}

[Test]
public void ChildCollectionsEagerFetchedShouldBeInSecondLevelCache()
{
var firstSession = OpenSession();
var customersWithOrderNumberEqualsTo2 = GetCustomersWithOrdersEagerLoaded(firstSession);

using (IDbCommand cmd = OpenSession().Connection.CreateCommand())
{
cmd.CommandText = "DELETE FROM Orders;";
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}

var secondSession = OpenSession();
var customers = GetAllCustomers(secondSession);


Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count));
Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count));

Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(Customer3.Orders.Count));
Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count));
Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(Customer2.Orders.Count));

firstSession.Dispose();
secondSession.Dispose();
}

[Test]
public void ChildCollectionsFromLeftOuterJoinWithWhereClauseRestrictionOnRootShouldBeInSecondLevelCache()
{
var firstSession = OpenSession();
var customersWithOrderNumberEqualsTo2 = GetCustomersByNameUsingWhereClause(firstSession, "First Customer");


using (IDbCommand cmd = OpenSession().Connection.CreateCommand())
{
cmd.CommandText = "DELETE FROM Orders;";
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}

var secondSession = OpenSession();
var customers = secondSession.Get<Customer>(Customer1.Id);

Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count));
Assert.That(customers.Orders, Has.Count.EqualTo(Customer1.Orders.Count));

firstSession.Dispose();
secondSession.Dispose();
}

[Test]
public void ChildCollectionsFromLeftOuterJoinShouldBeInSecondLevelCacheIfQueryContainsSubqueryWithRestrictionOnLeftOuterJoin()
{
var firstSession = OpenSession();
var customersWithOrderNumberEqualsTo2 = GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(firstSession, OrderNumber, Customer1.Name);

var secondSession = OpenSession();
var customers = GetAllCustomers(secondSession);

Assert.That(customersWithOrderNumberEqualsTo2.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count));

using (IDbCommand cmd = OpenSession().Connection.CreateCommand())
{
cmd.CommandText = "DELETE FROM Orders;";
cmd.ExecuteNonQuery();
cmd.Connection.Close();
}

Assert.That(customers.Single(n => n.Id == Customer1.Id).Orders, Has.Count.EqualTo(Customer1.Orders.Count));
Assert.That(customers.Single(n => n.Id == Customer2.Id).Orders, Has.Count.EqualTo(0));
Assert.That(customers.Single(n => n.Id == Customer3.Id).Orders, Has.Count.EqualTo(0));

firstSession.Dispose();
secondSession.Dispose();
}

protected void ClearSecondLevelCacheFor(System.Type entity)
{
var entityName = entity.FullName;
Sfi.EvictEntity(entityName);
var entityPersister = Sfi.GetEntityPersister(entityName);
if (!entityPersister.HasCache)
return;

IReadOnlyCollection<string> querySpaces = entityPersister.QuerySpaces;
Sfi.UpdateTimestampsCache.Invalidate(querySpaces);
}

protected void ClearCollectionCache<T>(Expression<Func<T, IEnumerable>> pathToCollection)
{
var rootEntityTypeFullPath = typeof(T).FullName;
var memberExpression = pathToCollection.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentException("pathToCollection should be member expression");

var role = string.Format("{0}.{1}", rootEntityTypeFullPath, memberExpression.Member.Name);
Sfi.EvictCollection(role);
}

protected abstract IList<Customer> GetCustomersWithFetchedOrdersWithoutRestrictions(ISession session);
protected abstract IList<Customer> GetCustomersWithOrdersEagerLoaded(ISession session);
protected abstract IList<Customer> GetCustomersByOrderNumberUsingOnClause(ISession session, int orderNumber);
protected abstract IList<Customer> GetCustomersByOrderNumberUsingWhereClause(ISession session, int orderNumber);
protected abstract IList<Customer> GetCustomersByNameUsingWhereClause(ISession session, string customerName);
protected abstract IList<Customer> GetCustomersByOrderNumberUsingSubqueriesAndByNameUsingWhereClause(ISession session, int orderNumber, string customerName);
protected abstract IList<Customer> GetAllCustomers(ISession session);
}
}
2 changes: 1 addition & 1 deletion src/NHibernate/Cache/UpdateTimestampsCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public virtual void PreInvalidate(IReadOnlyCollection<string> spaces)
}

//Since v5.1
[Obsolete("Please use PreInvalidate(IReadOnlyCollection<string>) instead.")]
[Obsolete("Please use Invalidate(IReadOnlyCollection<string>) instead.")]
public void Invalidate(object[] spaces)
{
//Only for backwards compatibility.
Expand Down
Loading