Nhibernate方言,将小数从5位转换为2位的精度
Nhibernate dialect, precision for Casting decimal from 5 places to 2 places
我们在数据库中有一个 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
我需要添加一个 Convert 或 cast 或 round。
数据库中有一条 DepositAccountBalance = 1.0107 的记录。
因此,如果没有 Cast 或 Convert,就没有匹配项。
网站上有一些资源,人们在其中为生成的 .hbm.xml 添加了精度和比例。其中一些推荐使用 Nhibernate 方言和自定义 SQL 函数
谁能解释一下我需要其中的哪一个?我什么时候使用 Nhibernate Dialect。什么时候将 Precision 添加到 .hbm.xml。我需要做的就是在 where 子句中添加一个 Convert 或 round
所以这就是应该的工作(我大大简化了查询,但问题都是一样的):
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
class:
中的相关代码
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]);
}
对于所有 decimal
类型,GetCastTypeName
只是 returns decimal(19,5)
,这似乎是一个错误。
有两种方法可以解决这个问题:
使用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
编写我们自己的 class 以正确进行转换。我们真的只需要更改一行代码就可以让它工作。否则它与 CastProjection
class:
完全相同
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
类型的问题,但我不知道对其他类型会有什么影响,因此无法保证此代码。
希望对您有所帮助。我会选择#1。
我们在数据库中有一个 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
我需要添加一个 Convert 或 cast 或 round。
数据库中有一条 DepositAccountBalance = 1.0107 的记录。 因此,如果没有 Cast 或 Convert,就没有匹配项。
网站上有一些资源,人们在其中为生成的 .hbm.xml 添加了精度和比例。其中一些推荐使用 Nhibernate 方言和自定义 SQL 函数
谁能解释一下我需要其中的哪一个?我什么时候使用 Nhibernate Dialect。什么时候将 Precision 添加到 .hbm.xml。我需要做的就是在 where 子句中添加一个 Convert 或 round
所以这就是应该的工作(我大大简化了查询,但问题都是一样的):
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
class:
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]);
}
对于所有 decimal
类型,GetCastTypeName
只是 returns decimal(19,5)
,这似乎是一个错误。
有两种方法可以解决这个问题:
使用
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
编写我们自己的 class 以正确进行转换。我们真的只需要更改一行代码就可以让它工作。否则它与
完全相同CastProjection
class: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
类型的问题,但我不知道对其他类型会有什么影响,因此无法保证此代码。
希望对您有所帮助。我会选择#1。