這篇文章將為大家詳細講解有關如何解決因BigDecimal類型數據引出的問題,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

問題描述:
程序中需要判斷一個字段是否為0(字段類型為BigDecimal),想都沒想,對象的判斷用equals?結果卻與預期有一定的差距,看下面代碼及運行結果。
public static void main(String[] args) {
BigDecimal decimal1 = BigDecimal.valueOf(0);
BigDecimal decimal2 = new BigDecimal("0.00");
System.out.println("the result is " +decimal1.equals(decimal2));
}運行結果:
the result is false
結論: BigDecimal類型比較相等不能簡單的通過equals方法實現。
BigDecimal類的equals方法源碼如下:
public boolean equals(Object x) {
if (!(x instanceof BigDecimal))
return false;
BigDecimal xDec = (BigDecimal) x;
if (x == this)
return true;
if (scale != xDec.scale)//這里會比較數字的精度
return false;
long s = this.intCompact;
long xs = xDec.intCompact;
if (s != INFLATED) {
if (xs == INFLATED)
xs = compactValFor(xDec.intVal);
return xs == s;
} else if (xs != INFLATED)
return xs == compactValFor(this.intVal);
return this.inflate().equals(xDec.inflate());
}看上面的注釋可以知道,BigDecimal類的equals方法會判斷數字的精度,看下面的代碼及運行結果:
public static void main(String[] args) {
BigDecimal decimal1 = BigDecimal.valueOf(0).setScale(2);
BigDecimal decimal2 = new BigDecimal("0.00").setScale(2);
System.out.println("the result is " +decimal1.equals(decimal2));
}運行結果:
the result is true
結論: 使用BigDecimal類equals方法判斷兩個BigDecimal類型的數據時,需要設置精度,否則結果可能不正確。
思考:每次都設置精度比較麻煩,有其他方式進行相等的比較嗎?
看了下BigDecimal的方法列表,有一個名為compareTo的方法,通過注釋可知,貌似可以進行不同精度的比較,看下面的代碼。
public static void main(String[] args) {
BigDecimal decimal1 = BigDecimal.valueOf(1.1);
BigDecimal decimal2 = new BigDecimal("1.10");
System.out.println("the result is " +decimal1.compareTo(decimal2));
}運行結果:
the result is 0
0表示兩個數相等,所有可以通過compareTo實現不同精度的兩個BigDecimal類型的數字是否相等的比較
引出的問題:公司的項目中,為了避免由于精度丟失引起問題,凡是有精度要求的字段用的都是BigDecimal類型。數據持久層用的是Mybatis框架,Mybatis的mapper文件中有些條件判斷用的是BigDecimal對應的字段,如下:
<select id="selectByCondition" resultType="com.scove.demo.domain.Score">
select * from tb_score where 1=1
<if test="score!=null and score!=0">
and score>#{score}
</if>
...score是一個BigDecimal類型的字段,score!=0 Mybatis是如何進行判斷的,會不會用的是上面的equals方法?如果是那么項目上線會不會捅大簍子,想到這兒,有點怕了。寫了個程序測了下,這樣寫完全沒問題,能夠達到我想要的目的。但是還是有點擔心,看看Mybatis底層是如何實現的吧,以免以后犯類似的錯誤嘛。
經過分析調試,很快就找到了關鍵代碼的位置,如下:
public class ExpressionEvaluator {
public boolean evaluateBoolean(String expression, Object parameterObject) {
Object value = OgnlCache.getValue(expression, parameterObject);
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof Number) {
return !new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO);
}
return value != null;
}
...public final class OgnlCache {
....
public static Object getValue(String expression, Object root) {
try {
Map<Object, OgnlClassResolver> context = Ognl.createDefaultContext(root, new OgnlClassResolver());
return Ognl.getValue(parseExpression(expression), context, root);
} catch (OgnlException e) {
throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e);
}
}用的是表達式求值,Ognl這個類的竟然沒有源碼,apache的官網上找了下,是有相應的源碼的,只是需要單獨下載,真麻煩,算了不看了。據說底層用的是Spring 的ognl表達式,我也不 關心了。但是以后如果不確定mapper中的test是否正確咋個辦?
想了下,還是寫一個工具類在拿不準的時候用一下吧,反正拿不準的時候肯定很少,所以隨便寫了簡單的吧,如下:
public static void main(String[] args) {
ExpressionEvaluator evaluator = new ExpressionEvaluator();
String expression = "score!=null and score!=0";
DynamicContext context = new DynamicContext(new Configuration(), null);
context.bind("score", BigDecimal.valueOf(0.1));
Boolean flag = evaluator.evaluateBoolean(expression , context.getBindings());
System.out.println("the result is " +flag);
}運行結果:
the result is true
關于“如何解決因BigDecimal類型數據引出的問題”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
網頁標題:如何解決因BigDecimal類型數據引出的問題-創新互聯
路徑分享:http://www.yijiale78.com/article10/ddcdgo.html
成都網站建設公司_創新互聯,為您提供動態網站、網站設計、網站維護、小程序開發、網站排名、域名注冊
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯