【问题标题】:NHibernate second level cache not working when using composite primary key?使用复合主键时 NHibernate 二级缓存不起作用?
【发布时间】:2013-06-15 09:26:35
【问题描述】:

在 NHibernate 中使用复合主键以使其适合缓存的正确方法是什么?

我隔离了类似于本文最后部分的复合主键:http://devlicio.us/blogs/anne_epstein/archive/2009/11/20/nhibernate-and-composite-keys.aspx

但是二级缓存没有缓存它。

如果是代理键,就是缓存,例如

 var q = from p in session.Query<Product>()
         select p;

 q.Cacheable().ToList(); // hit database


 // this doesn't hit the database, Get get its value from cache(via last query)
 var px = secondSession.Get<Product>(1); 

但是当使用复合主键时,Get 不会从缓存中获取它的值:

 var q = from pl in session.Query<ProductLanguage>()
         select pl;      

  q.Cacheable().ToList(); // hit the database

  // this hits the database, Get didn't get its value from cache(via last query)
  var plx = secondSession.Get<ProductLanguage>(
         new ProductLanguageCompositeKey { ProductId = 1, LanguageCode = "en" });

复合键(此处为 ProductLanguageCompositeKey),甚至将其隔离为自己的类(具有 Serializable 属性、Equals 和 GetHashCode)都没有被缓存?

我们如何使通过复合键访问的实体可缓存?

【问题讨论】:

    标签: nhibernate caching


    【解决方案1】:

    对于那些怀疑 NHibernate 的二级缓存在使用复合主键时是否不起作用(缓存有效)的人,请检查您的复合主键的值是否处于原始形式。我的缓存问题的解决方案:

    SQL Server conversion fidelity from nvarchar to varbinary, then from varbinary to nvarchar

    【讨论】:

    • 您介意详细说明您所说的“原始形式”是什么意思吗?
    【解决方案2】:

    为了缓存,为ProductLanguage 生成一个唯一的缓存键。这个缓存键是根据组合键构建的,它依赖于Product 实体的哈希码。如果您使用跨会话查询,NHibernate 可能会返回 Product 的代理或未代理版本,这将导致不同的哈希码并导致缓存查找错过缓存的 ProductLanguage 实体。

    解决方案是覆盖EqualsGetHashCode 方法以返回一致的值。最简单的方法是为所有具有代理 Id 键的实体继承流行的 EntityBase 类。

    public abstract class EntityBase<T>
        where T : EntityBase<T>
    {
        public virtual int Id { get; protected set; }
    
        protected bool IsTransient { get { return Id == 0; } }
    
        public override bool Equals(object obj)
        {
            return EntityEquals(obj as EntityBase<T>);
        }
    
        protected bool EntityEquals(EntityBase<T> other)
        {
            if (other == null)
            {
                return false;
            }
            // One entity is transient and the other is not.
            else if (IsTransient ^ other.IsTransient)
            {
                return false;
            }
            // Both entities are not saved.
            else if (IsTransient && other.IsTransient)
            {
                return ReferenceEquals(this, other);
            }
            else
            {
                // Compare transient instances.
                return Id == other.Id;
            }
        }
    
        // The hash code is cached because a requirement of a hash code is that
        // it does not change once calculated. For example, if this entity was
        // added to a hashed collection when transient and then saved, we need
        // the same hash code or else it could get lost because it would no 
        // longer live in the same bin.
        private int? cachedHashCode;
    
        public override int GetHashCode()
        {
            if (cachedHashCode.HasValue) return cachedHashCode.Value;
    
            cachedHashCode = IsTransient ? base.GetHashCode() : Id.GetHashCode();
            return cachedHashCode.Value;
        }
    
        // Maintain equality operator semantics for entities.
        public static bool operator ==(EntityBase<T> x, EntityBase<T> y)
        {
            // By default, == and Equals compares references. In order to 
            // maintain these semantics with entities, we need to compare by 
            // identity value. The Equals(x, y) override is used to guard 
            // against null values; it then calls EntityEquals().
            return Object.Equals(x, y);
        }
    
        // Maintain inequality operator semantics for entities. 
        public static bool operator !=(EntityBase<T> x, EntityBase<T> y)
        {
            return !(x == y);
        }
    }
    

    并实施:

    public class Blog : EntityBase<Blog>
    {
        public virtual string Name { get; set; }
    
        // This would be configured to lazy-load.
        public virtual IList<Post> Posts { get; protected set; }
    
        public Blog()
        {
            Posts = new List<Post>();
        }
    
        public virtual Post AddPost(string title, string body)
        {
            var post = new Post() { Title = title, Body = body, Blog = this };
            Posts.Add(post);
            return post;
        }
    }
    
    public class Post : EntityBase<Post>
    {
        public virtual string Title { get; set; }
        public virtual string Body { get; set; }
        public virtual Blog Blog { get; set; }
    
        public virtual bool Remove()
        {
            return Blog.Posts.Remove(this);
        }
    }
    
    void Main(string[] args)
    {
        var post = session.Load<Post>(postId);
    
        // If we didn't override Equals, the comparisons for
        // "Blog.Posts.Remove(this)" would all fail because of reference equality. 
        // We'd end up be comparing "this" typeof(Post) with a collection of
        // typeof(PostProxy)!
        post.Remove();
    
        // If we *didn't* override Equals and *just* did 
        // "post.Blog.Posts.Remove(post)", it'd work because we'd be comparing 
        // typeof(PostProxy) with a collection of typeof(PostProxy) (reference 
        // equality would pass!).
    }
    

    更多信息https://stackoverflow.com/a/20110265/179494

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-21
      • 2011-09-07
      • 2011-09-02
      • 2011-10-26
      • 2011-01-21
      • 2016-06-23
      • 1970-01-01
      相关资源
      最近更新 更多