Sunday, September 7, 2014

Use entity cache when fetching an entity

The following code contrast the difference on cache re-use between getting an entity via cached query, and via cached entity


Here's the common code:

namespace TestTheSecondLevelCache
{
    public class Common
    {
        public static ISessionFactory BuildSessionFactory()
        {
            var sf = DomainMapping.Mapper.BuildSessionFactory();

            using (var session = sf.OpenStatelessSession())
            using (var tx = session.BeginTransaction())
            {
                Console.WriteLine("Stateless update");
                var p = session.Get<Person>(1);
                p.FirstName = "John";
                session.Update(p);
                tx.Commit();
            }


            return sf;
        }
    }
}

Query caching:
[TestMethod]
public void Test_Query_Caching_Compare_This_To_Entity_Caching()
{

    var sf = Common.BuildSessionFactory();
    
    Action query = delegate
    {

        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            Console.WriteLine("Query 1");
            var person = session.Query<Person>().Where(x => x.PersonId == 1).Cacheable().Single();
        }

        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {

            Console.WriteLine("Query 2");
            var person = session.Query<Person>().Where(x => x.PersonId == 2).Cacheable().Single();
        }


        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            Console.WriteLine("Query 3");
            var person = session.Query<Person>().Where(x => x.PersonId == 1).Cacheable().Single();
        }

        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            Console.WriteLine("Query 4");
            var person = session.Query<Person>().Where(x => x.PersonId == 2).Cacheable().Single();
        }

    };


    query();

    using (var session = sf.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        Console.WriteLine("Update");
        var p = session.Get<Person>(1);
        p.FirstName = "ZX-" + p.FirstName;
        session.Save(p);
        tx.Commit();
    }


    query();


    using (var session = sf.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        Console.WriteLine("Assert");
        var p = session.Get<Person>(1);
        Assert.AreEqual("ZX-John", p.FirstName);
    }


}


Output:
Test Name:    Test_Query_Caching_Compare_This_To_Entity_Caching
Test Outcome:    Passed
Result StandardOutput:    
Stateless update
NHibernate: 
    SELECT
        person0_.PersonId as PersonId4_0_,
        person0_.FirstName as FirstName4_0_,
        person0_.LastName as LastName4_0_ 
    FROM
        Person person0_ 
    WHERE
        person0_.PersonId=@p0;
    @p0 = 1 [Type: Int32 (0)]
Query 1
NHibernate: 
    select
        person0_.PersonId as PersonId4_,
        person0_.FirstName as FirstName4_,
        person0_.LastName as LastName4_ 
    from
        Person person0_ 
    where
        person0_.PersonId=@p0;
    @p0 = 1 [Type: Int32 (0)]
Query 2
NHibernate: 
    select
        person0_.PersonId as PersonId4_,
        person0_.FirstName as FirstName4_,
        person0_.LastName as LastName4_ 
    from
        Person person0_ 
    where
        person0_.PersonId=@p0;
    @p0 = 2 [Type: Int32 (0)]
Query 3
Query 4
Update
Query 1
NHibernate: 
    select
        person0_.PersonId as PersonId4_,
        person0_.FirstName as FirstName4_,
        person0_.LastName as LastName4_ 
    from
        Person person0_ 
    where
        person0_.PersonId=@p0;
    @p0 = 1 [Type: Int32 (0)]
Query 2
NHibernate: 
    select
        person0_.PersonId as PersonId4_,
        person0_.FirstName as FirstName4_,
        person0_.LastName as LastName4_ 
    from
        Person person0_ 
    where
        person0_.PersonId=@p0;
    @p0 = 2 [Type: Int32 (0)]
Query 3
Query 4
Assert

As you can see, the query cache always gets evicted when there's a change on any of the row. Hence, even we didn't update person #2, all cached query on person gets evicted, resulting to re-fetching them from database. Not efficient


Contrast that query caching with entity caching, notice the use of Get:
[TestMethod]
public void Test_Entity_Caching_Compare_This_To_Query_Caching()
{

    var sf = Common.BuildSessionFactory();

    Action query = delegate
    {

        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            Console.WriteLine("Query 1");
            var person = session.Get<Person>(1);
        }

        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {

            Console.WriteLine("Query 2");
            var person = session.Get<Person>(2);
        }


        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            Console.WriteLine("Query 3");
            var person = session.Get<Person>(1);
        }

        using (var session = sf.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            Console.WriteLine("Query 4");
            var person = session.Get<Person>(2);
        }

    };


    query();

    using (var session = sf.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        Console.WriteLine("Update");
        var p = session.Get<Person>(1);
        p.FirstName = "ZX-" + p.FirstName;
        session.Save(p);
        tx.Commit();
    }


    query();


    using (var session = sf.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        Console.WriteLine("Assert");
        var p = session.Get<Person>(1);
        Assert.AreEqual("ZX-John", p.FirstName);
    }





}

Output:
Test Name:    Test_Entity_Caching_Compare_This_To_Query_Caching
Test Outcome:    Passed
Result StandardOutput:    
Stateless update
NHibernate: 
    SELECT
        person0_.PersonId as PersonId4_0_,
        person0_.FirstName as FirstName4_0_,
        person0_.LastName as LastName4_0_ 
    FROM
        Person person0_ 
    WHERE
        person0_.PersonId=@p0;
    @p0 = 1 [Type: Int32 (0)]
Query 1
NHibernate: 
    SELECT
        person0_.PersonId as PersonId4_0_,
        person0_.FirstName as FirstName4_0_,
        person0_.LastName as LastName4_0_ 
    FROM
        Person person0_ 
    WHERE
        person0_.PersonId=@p0;
    @p0 = 1 [Type: Int32 (0)]
Query 2
NHibernate: 
    SELECT
        person0_.PersonId as PersonId4_0_,
        person0_.FirstName as FirstName4_0_,
        person0_.LastName as LastName4_0_ 
    FROM
        Person person0_ 
    WHERE
        person0_.PersonId=@p0;
    @p0 = 2 [Type: Int32 (0)]
Query 3
Query 4
Update
Query 1
Query 2
Query 3
Query 4
Assert

It's more efficient, both entity 1 and 2 are not re-fetched from DB even there's a change on entity



Happy Coding!

No comments:

Post a Comment