Nhibernate方言,精确铸造小数从5位到2位

时间:2015-05-18 18:03:40

标签: nhibernate

我们在decimal(20,5)类型的数据库中有一个货币字段。 如何将CAST或COnvert添加到传出的Nhibernate标准?

   SELECT
        TOP (1000)  this_.DepositAccountId as DepositA1_71_0_,
        this_.BranchId as BranchId71_0_,
        this_.ConfigurationStatusId as Configu14_71_0_,
        this_.ConfiguredBy as Configu41_71_0_,
        this_.ConfiguredDate as Configu15_71_0_,
        this_.DepositAccountBalance as DepositA9_71_0_
    FROM
        dbo.DepositAccount this_ 
    WHERE
        Convert(Decimal(20,2), this_.DepositAccountBalance) = 1.01

目前它像这样发送WHERE子句

WHERE
           this_.DepositAccountBalance = 1.01

我需要添加转换或转换或舍入。

数据库中有一条记录,其中DepositAccountBalance = 1.0107。 因此,如果没有Cast或Convert,则没有匹配项。

网站上有一些资源可供人们添加精度,缩放到生成的.hbm.xml。其中一些人建议使用Nhibernate Dialect和自定义SQL函数

有谁可以解释我需要哪一个?我什么时候使用Nhibernate Dialect。我何时将精度添加到.hbm.xml。我需要做的就是在where子句

中添加Convert或round

1 个答案:

答案 0 :(得分:2)

所以 应该工作(我已经大大简化了查询,但问题都是一样的):

IType decimalType = TypeFactory.Basic("decimal(20,2)");

IProjection castProjection = Projections.Cast(
    decimalType,
    Projections.Property<DepositAccount>(acct => acct.DepositAccountBalance));

var accounts = session.QueryOver<DepositAccount>()
    .Where(Restrictions.Eq(castProjection, 1.01))
    .List<DepositAccount>();

不幸的是,这会生成以下SQL:

SELECT         
    this_.*
FROM         
    DepositAccount this_     
WHERE         
    cast( this_.DepositAccountBalance as DECIMAL(19,5)) = 1.01

咦?我们刚刚指定我们想要一个类型decimal(20,2)!发生了什么事?

看起来CastProjection完全忽略了传递它的类型的精度和比例。这是CastProjection类的相关代码:

public override SqlString ToSqlString(ICriteria criteria, int position, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
    ISessionFactoryImplementor factory = criteriaQuery.Factory;
    SqlType[] sqlTypeCodes = type.SqlTypes(factory);
    if (sqlTypeCodes.Length != 1)
    {
        throw new QueryException("invalid Hibernate type for CastProjection");
    }

    // HERE: precision and scale are being ignored.
    string sqlType = factory.Dialect.GetCastTypeName(sqlTypeCodes[0]);
    int loc = position*GetHashCode();
    SqlString val = projection.ToSqlString(criteria, loc, criteriaQuery,enabledFilters);
    val = SqlStringHelper.RemoveAsAliasesFromSql(val);

    return new SqlString("cast( ", val, " as ", sqlType, ") as ", GetColumnAliases(position, criteria, criteriaQuery)[0]);
}

对于所有GetCastTypeName类型,decimal(19,5)只返回decimal,这似乎是一个错误。

有两种方法可以解决这个问题:

  1. 使用Projections.SqlFunction(推荐 - 不知道#2的后果是什么)

    要做到这一点,我们只需要使用Projections.SqlFunction明确为我们执行cast

    var decimalType = TypeFactory.Basic("decimal(20,2)");
    
    var castProjection = Projections.SqlFunction(
        new SQLFunctionTemplate(decimalType, "cast(?1 as decimal(20,2))"),
            decimalType,
            Projections.Property<DepositAccount>(acct=> acct.DepositAccountBalance));
    
    var q = session.QueryOver<DepositAccount>()
        .Where(Restrictions.Eq(castProjection, 1.01))
        .List<DepositAccount>();
    

    这会生成预期的SQL:

    SELECT        
        this_.* 
    FROM         
        DepositAccount this_     
    WHERE         
        cast(this_.DepositAccountBalance as decimal(20,2)) = 1.01
    
  2. 编写我们自己的类来正确进行强制转换。我们实际上只需要更改一行代码就可以使其工作。否则它与CastProjection类完全相同:

    public class PrecisionCast : SimpleProjection 
    {
        private readonly IType type;
        private readonly IProjection projection;
    
        public PrecisionCast(IType type, IProjection projection)
        {
            this.type = type;
            this.projection = projection;
        }
    
        public override bool IsAggregate
        {
            get { return false; }
        }
    
        public override SqlString ToSqlString(ICriteria criteria, int position, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
        {
            ISessionFactoryImplementor factory = criteriaQuery.Factory;
            SqlType[] sqlTypeCodes = type.SqlTypes(factory);
    
    
            if (sqlTypeCodes.Length != 1)
            {
                throw new QueryException("invalid Hibernate type for CastProjection");
            }       
    
            // Get the type name, preserving scale and precision
            string sqlType = factory.Dialect.GetTypeName(sqlTypeCodes[0]);
    
            int loc = position*GetHashCode();
            SqlString val = projection.ToSqlString(criteria, loc, criteriaQuery,enabledFilters);
            val = SqlStringHelper.RemoveAsAliasesFromSql(val);
    
            return new SqlString("cast( ", val, " as ", sqlType, ") as ", GetColumnAliases(position, criteria, criteriaQuery)[0]);
        }
    
        public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
        {
            return new IType[]{ type };
        }
    
        public override NHibernate.Engine.TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
        {
            return projection.GetTypedValues(criteria, criteriaQuery);
        }
    
        public override bool IsGrouped
        {
            get
            {
                return projection.IsGrouped;
            }
        }
    
        public override SqlString ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
        {
            return projection.ToGroupSqlString(criteria, criteriaQuery, enabledFilters);
        }
    }
    

    然后像这样使用它:

    var decimalType = TypeFactory.Basic("decimal(20,2)");
    
    var castProjection = new PrecisionCast(
        decimalType, Projections.Property<DepositAccount>(acct => acct.DepositAccountBalance));
    
    var accounts = session.QueryOver<DepositAccount>()
        .Where(Restrictions.Eq(castProjection, 1.01))
        .List<DepositAccount>();
    

    这似乎解决了decimal类型的问题,但我不知道其他类型的后果是什么,因此不保证此代码。

  3. 希望这会有所帮助。我和#1一起去。