static,你还敢用吗?(二)

为了压系统,昨天小组在测试环境模拟了一大批订单数据。今天上午查看记录的账单计息日志,发现了一大堆的MySqlException

MySql.Data.MySqlClient.MySqlException (0x80004005): There is already an open DataReader associated with this Connection which must be closed first.

诸如:

2017-01-05 00:40:49.891
账单计息异常/{"BillId":1000012082,"OrderId":"DD201701040002672"}:MySql.Data.MySqlClient.MySqlException (0x80004005): There is already an open DataReader associated with this Connection which must be closed first.
   在 MySql.Data.MySqlClient.ExceptionInterceptor.Throw(Exception exception)
   在 MySql.Data.MySqlClient.MySqlCommand.Throw(Exception ex)
   在 MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
   在 MySql.Data.MySqlClient.MySqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   在 System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
   在 CommonLibrary.CommonOrm.CommonOrm_Dapper.<QueryImpl>d__11`1.MoveNext() 位置 e:\work\yijia\trunk\CommonLibrary\CommonOrm\CommonOrm_Dapper.cs:行号 1554
   在 System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   在 System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   在 CommonLibrary.CommonOrm.CommonOrm_Dapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) 位置 e:\work\yijia\trunk\CommonLibrary\CommonOrm\CommonOrm_Dapper.cs:行号 1444
   在 GateWay.DAL.BillsDal.BillsDal.GetOrdersBillList(String bizOrderId) 位置 e:\work\yijia\trunk\GetWay.DAL\BillsDal\BillsDal.cs:行号 123
   在 GateWay.BLL.Bills.BillsBll.GetOrdersBill(String bizOrderId, BillTypeEnum billType) 位置 e:\work\yijia\trunk\GetWay.BLL\Bills\BillsBll.cs:行号 27
   在 GateWay.BLL.Bills.PrincipalBillsInterest.InterestBill(t_bills principleBill) 位置 e:\work\yijia\trunk\GetWay.BLL\Bills\PrincipalBillsInterest.cs:行号 186
   在 GateWay.BLL.Bills.PrincipalBillsInterest.Interest() 位置 e:\work\yijia\trunk\GetWay.BLL\Bills\PrincipalBillsInterest.cs:行号 77

通过分析程序,发现dal层的所有方法都是静态的,其中还包括一个静态的db连接对象:

public class BillsDal
{
    static IDbConnection _conn = ConnUtility.GateWayConntion;

    /// <summary>
    /// 获取指定业务订单的所有账单(by 商户code和订单号)
    /// </summary>
    /// <param name="bizOrderId">订单Id,唯一</param>
    /// <returns></returns>
    public static List<t_bills> GetOrdersBillList(string merCode, string bizOrderNo)
    {
        var obj = _conn.Query<t_bills>("select * from t_bills where merCode=‘" + merCode + "‘ and orderNo=‘" + bizOrderNo + "‘");
        return obj.ToList();
    }
}

突然想到之前整理的blog《static,你还敢用吗?》,所以,不难分析出来原因:问题就出在这个静态的db连接对象_conn上,因为所有类的实例始终是用一个db连接,当并发出现时,又没有lock数据操作代码,那么,就很容易出现连接在未关闭时又要被建立并打开,这样就出现了db连接异常。

经模拟多线程来测试,的确如此。

问题即答案!修复这个bug的话,有如下2个方案:

  1. 如果仍然要保留这个static的_conn字段,就要用lock来锁住数据操作代码(GetOrdersBillList),以控制并发冲突
  2. 每次查询时用一个新的连接对象。

方案分析:第1种,涉及到对象只能在被释放(关闭)掉才能再次被使用(打开),性能低下,不可取。 第2种呢,其实在dal层,绝大多数的程序猿都是按照每一个数据操作只用一个db连接的方式来编码的。 由于大家一般不会把dal类的成员定义成static,所以,也就不会遇到这样的db连接异常。而我呢,倾向于用static方法,考虑到封装,就把这个db连接对象封装成静态字段了,反而忽视了静态数据成员带来的隐患——数据量小时几乎是暴露不出来问题,一旦数据量大起来,有了并发,就会出现资源被同时使用,这样的话,多个线程实例都要修改其状态时,就出现了并发异常。

由此来看,依然用static的话,就要把_conn当做只读的私有属性(不考虑代码味道):

    static IDbConnection _conn
    {
        get { return ConnUtility.GateWayConntion; }
    }

这样,当每次访问这个属性时,都会返回一个新的连接对象。

再次模拟多线程测试,ok!

时间: 2024-10-28 22:09:12

static,你还敢用吗?(二)的相关文章

PHP之static静态变量详解(二)

在看别人项目过程中,看到函数里面很多static修饰的变量,关于static修饰的变量,作用域,用法越看越困惑,所以查了下资料. static用法如下: 1.static 放在函数内部修饰变量 2.static放在类里修饰属性,或方法 3.static放在类的方法里修饰变量 4.static修饰在全局作用域的变量 所表示的不同含义如下: 1.在函数执行完后,变量值仍然保存 如下所示: <?php function testStatic() { static $val = 1; echo $val

面向对象程序设计-C++ Type conversion (Static) &amp; Inheritance &amp; Composition【第十二次上课笔记】

这节课继续讲解了 static 作为静态数据成员 / 成员函数的用法 具体详解我都已注释出来了,大家可以慢慢看 有任何问题都可以在这篇文章下留言我会及时解答 :) //static 静态数据成员 //static 静态成员函数 #include <iostream> using namespace std; class Integer { public: int i; static int number; //Declaration, 整个类只有一个版本,所有对象共享 //const stat

(转)Java中的static关键字解析

转载: http://www.cnblogs.com/dolphin0520/p/3799052.html 一.static关键字的用途 在<Java编程思想>P86页有这样一段话: "static方法就是没有this的方法.在static方法内部不能调用非静态方法,反过来是可以的.而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法.这实际上正是static方法的主要用途." 这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键

java实现手机扫描二维码进行登录

转自:http://www.daxueit.com/article/2581.html 项目结构: 实现流程: pc端: 1:打开二维码登录网页index.html 2:index.html调用GetQrCodeServlet 3:GetQrCodeServlet干2件事 a:生成随机的uuid,是一个唯一标识,该标识贯穿整个流程 b:生成二维码图片,二维码信息:http://60.28.201.37:8380/QrCodeLoginPro/Login.html?uuid=" + uuid 4:

Java中的static关键字解析

http://www.cnblogs.com/dolphin0520/p/3799052.html 一.static关键字的用途 在<Java编程思想>P86页有这样一段话: “static方法就是没有this的方法.在static方法内部不能调用非静态方法,反过来是可以的.而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法.这实际上正是static方法的主要用途.” 这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键字的基本作用,简而言之,一句

java中static关键字解析

static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键字的用法和平常容易误解的地方,最后列举了一些面试笔试中常见的关于static的考题.以下是本文的目录大纲: 一.static关键字的用途 二.static关键字的误区 三.常见的笔试面试题 若有不正之处,希望谅解并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin05

qr 生成二维码

package com.common; import com.swetake.util.Qrcode; import jp.sourceforge.qrcode.QRCodeDecoder; import com.swetake.util.Qrcode; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.

java中的static详解

如果一个类成员被声明为static,它就能够在类的任何对象创建之前被访问,而不必引用任何对象.static 成员的最常见的例子是main( ) .因为在程序开始执行时必须调用main() ,所以它被声明为static. 声明为static的变量实质上就是全局变量.当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量,例如:声明一个static的变量count作为new一个类实例的计数.声明为static的方法有以下几条限制: 1.它们仅能调用其他的s

数据结构--二项队列分析及实现

一,介绍 什么是二项队列,为什么会用到二项队列? 与二叉堆一样,二项队列也是优先级队列的一种实现方式.在 数据结构--堆的实现之深入分析 的末尾 ,简单地比较了一下二叉堆与二项队列. 对于二项队列而言,它可以弥补二叉堆的不足:merge操作的时间复杂度为O(N).二项队列的merge操作的最坏时间复杂度为O(logN). 二,二项队列的基本操作及实现 在详细介绍二项的队列的基本操作之前,先了解下二项队列这种数据结构: 1)一个二项队列是若干棵树的集合.也就是说,二项队列不仅仅是一棵树,而是多棵树

java中的static

Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键字的用法和平常容易误解的地方,最后列举了一些面试笔试中常见的关于static的考题.以下是本文的目录大纲: 一.static关键字的用途 二.static关键字的误区 三.常见的笔试面试题 若有不正之处,希望谅解并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnb