如何激活与自己的用户类型的延迟加载属性的二级缓存?自己的、二级缓存、属性、加载

2023-09-04 01:53:52 作者:爷丶有爷的范°

preface : 在我的应用程序,我存储为字节[] 在数据库中的原始WAV数据。在我的领域模型还有一类 PcmAudioStream 的再presents的原始WAV数据。我创建了一个实现NHibernate的 IUserType 来我的阶级和字节[] 之间的转换。 有迹象表明,使用 PcmAudioStream 类几类,它们都映射到数据库表。为了避免总是检索从这样一个表中的一行时加载所有WAV数据,我创建了一个实现流利的NHibernate的 IUserTypeConvention ,指定这些属性应该永远是懒惰的加载。 所有这一切就像一个魅力。

问: 由于这些内容 PcmAudioStream 取值很少会发生变化,我希望把获取的实例中的二级缓存。现在,我知道如何激活一个完整的类二级缓存,但是我怎么做到这一点只在延迟加载属性?

我的域模型的相关部分看起来是这样的:

 公共类用户:实体
{
    公共虚拟字符串名字{获得;组; }
    公共虚拟字符串名字{获得;组; }
    公共虚拟PcmAudioStream FullNameRecording {获得;组; }
    // ...
}
 

映射为简单的(注意:这不是我的映射,我使用的是惯例,但它是相当值):

 公共类用户映射:ClassMap<使用者>
{
    公众用户映射()
    {
        ID(X => x.Id);
        地图(X => x.FirstName);
        地图(X => x.LastName);
        地图(X => x.FullNameRecording).CustomType< PcmAudioStreamAsByteArray>();
    }
}
 

解决方案 MyBatis 延迟加载 一级缓存 二级缓存 详解

您可以使用一个专用静态缓存来实现这一目标。这是一个有点更多的工作来建立,但不要求额外的类或公共更改您的域模型。一个大缺点是项不从缓存中删除,但你可以使用自定义的集合或全球高速缓存限制条目的数量。

 公共类实体
{
    公共虚拟INT标识{获取;保护套; }
}

公共类PcmAudioStream
{}

公共类用户:实体
{
    私人静态只读的IDictionary< INT,PcmAudioStream> _fullNameRecordingCache;

    私人PcmAudioStream _fullNameRecording;

    静态用户()
    {
        _fullNameRecordingCache =新字典< INT,PcmAudioStream>();
    }

    公共虚拟字符串名字{获得;组; }
    公共虚拟字符串名字{获得;组; }
    公共虚拟PcmAudioStream FullNameRecording
    {
        得到
        {
            如果(_fullNameRecordingCache.ContainsKey(同上))
            {
                返回_fullNameRecordingCache [ID]
            }
            //可能需要看这里的代理
            _fullNameRecordingCache.Add(同上,_fullNameRecording);
            返回_fullNameRecording;
        }
        组
        {
            如果(_fullNameRecordingCache.ContainsKey(同上))
            {
                _fullNameRecordingCache [ID] =价值;
            }
            _fullNameRecording =价值;
        }
    }
    // ...
}
 

映射:

 公共类用户映射:ClassMap<使用者>
{
    公众用户映射()
    {
        ID(X => x.Id);
        地图(X => x.FirstName);
        地图(X => x.LastName);
        地图(X => x.FullNameRecording).CustomType< PcmAudioStreamAsByteArray>()
            .Access.CamelCaseField(prefix.Underscore);
    }
}
 

编辑回应评论:

我不认为这有可能实现这一目标的用户类型,因为IDataReader的是已经在NullSafeGet打开。我想你可以做一个倾听者实施我preLoadEventListener但是,这并不让你的缓存失效。我不认为这两个选项是可行的。

想着它经过一些我仍然觉得我原来的解决方案(或变体)是最好的选择。我的理解(和共享),你希望有一个干净的领域模型,但有时候妥协是必要的,我的解决方案并不改变模型的公共成员或需要任何额外的参考。另一个理由是,对象是第一个知道该记录已发生变化,需要更换或添加到高速缓存。

Preface: In my application, I store raw WAV data in the database as byte[]. In my domain model there is a class PcmAudioStream that represents that raw WAV data. I created an implementation of NHibernate's IUserType to convert between my class and byte[]. There are several classes that use the PcmAudioStream class, all of which are mapped to database tables. To avoid always loading all WAV data when retrieving a row from such a table, I created an implementation of Fluent NHibernate's IUserTypeConvention that specifies that those properties should always be lazy loaded. All of this works like a charm.

Question: Because the content of these PcmAudioStreams rarely ever changes, I want to put retrieved instances in the second level cache. Now, I know how to activate the second level cache for a complete class, but how do I achieve this only for a lazy loaded property?

The relevant part of my domain model looks like this:

public class User : Entity
{
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual PcmAudioStream FullNameRecording { get; set; }
    // ...
}

The mapping is simple (note: that is not my mapping, I am using a convention, but it is equivalent):

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id);
        Map(x => x.FirstName);
        Map(x => x.LastName);
        Map(x => x.FullNameRecording).CustomType<PcmAudioStreamAsByteArray>();
    }
}

解决方案

You could use a private static cache to accomplish this. It's a little more work to set up but doesn't require an additional class or public changes to your domain model. A big drawback is that entries are not removed from the cache, but you could use a custom collection or a "global" cache that limits the number of entries.

public class Entity
{
    public virtual int Id { get; protected set; }
}

public class PcmAudioStream
{}

public class User : Entity
{
    private static readonly IDictionary<int, PcmAudioStream> _fullNameRecordingCache;

    private PcmAudioStream _fullNameRecording;

    static User()
    {
        _fullNameRecordingCache = new Dictionary<int, PcmAudioStream>();
    }

    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual PcmAudioStream FullNameRecording
    {
        get
        {
            if (_fullNameRecordingCache.ContainsKey(Id))
            {
                return _fullNameRecordingCache[Id];
            }
            // May need to watch for proxies here
            _fullNameRecordingCache.Add(Id, _fullNameRecording);
            return _fullNameRecording;
        }
        set
        {
            if (_fullNameRecordingCache.ContainsKey(Id))
            {
                _fullNameRecordingCache[Id] = value;
            }
            _fullNameRecording = value;
        }
    }
    // ...
}

Mapping:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id);
        Map(x => x.FirstName);
        Map(x => x.LastName);
        Map(x => x.FullNameRecording).CustomType<PcmAudioStreamAsByteArray>()
            .Access.CamelCaseField(Prefix.Underscore);
    }
}

Edited in response to comments:

I don't see that it's possible to achieve this in a user type because the IDataReader is already open in NullSafeGet. I think you could do it in a listener implementing IPreLoadEventListener but that doesn't allow you to invalidate the cache. I don't think either option is viable.

After thinking about it some more I still think my original solution (or a variant) is the best option. I understand (and share) your desire for a clean domain model but sometimes compromises are necessary and my solution does not change the public members of the model or require any additional references. Another justification is that the object is the first to know that the recording has changed and needs to be replaced in or added to the cache.

 
精彩推荐
图片推荐