项目前后台分离,通过ajax异步请求
这个问题困扰了我半个月,找来找去也没有找到办法,网上的资料更是少的可怜!!
报错时好时坏,总让我抱有一丝希望是网络或者服务器的问题,但是!!!计算机没有偶然事件!错就是错!
不多回想辛酸泪,直接贴上报错信息 看大家是否和我一样?
根据报错,很明显, 你不能操作一个已经被关掉的statement,就是当你访问数据库的时候,它已经被关闭了。。
那么遇到这个问题,我是怎么如何一步一步找到了答案,作为学习者,解决的办法千千万万,但是不能逃避这个报错,一定要从根源去分析这个报错到底是因为什么??并不是换一个代码就好了。。
那么开始了,当前台请求的时候,我打开了前台的控制器,查看到了一个现象:
前台居然在一次请求中,并行了5次ajax请求后台,
想了想,难道是我的C3P0连接池跟不上请求么?
查了查资料,我把C3P0改了下,代码如下:
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/bluemsun</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->
<property name="initialPoolSize">15</property>
<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->
<property name="maxIdleTime">30</property>
<!--连接池中保留的最大连接数。默认值: 15 -->
<property name="maxPoolSize">100</property>
<!-- 连接池中保留的最小连接数,默认为:3-->
<property name="minPoolSize">20</property>
<!--c3p0全局的PreparedStatements缓存的大小。如果maxStatements与maxStatementsPerConnection均为0,则缓存不生效,只要有一个不为0,则语句的缓存就能生效。如果默认值: 0-->
<property name="maxStatements">200</property>
<!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->
<property name="checkoutTimeout" >3000</property>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->
<property name="acquireIncrement">7</property>
<!--定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次-->
<property name="acquireRetryAttempts">30</property>
<!--重新尝试的时间间隔,默认为:1000毫秒-->
<property name="acquireRetryDelay" >1000</property>
<!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->
<property name="autoCommitOnClose">false</property>
<!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。默认值: null -->
<property name="automaticTestTable">Test</property>
<!--如果为false,则获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常,但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认: false-->
<property name="breakAfterAcquireFailure">false</property>
<!--每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->
<property name="idleConnectionTestPeriod">60</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。默认值: 0 -->
<property name="maxStatementsPerConnection">0</property>
<!--numHelperThreads:异步操作,提升性能通过多线程实现多个操作同时被执行。Default: 3-->
<property name="numHelperThreads">10</property>
<!--testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>
</named-config>
看样子,他五个同时请求,数据库应该也能给他分配相应的资源....
试了试果然,一切正常,没有异常了.. 但是但是!!!没过一会儿,异常接二连三的开始出现了,而且一发不可收拾!!
因此第一个方案失败了,看来问题不是因为资源分配的不足,并且我上面都是按大的来写!
那么?会不会是因为两个请求同时访问一个方法的时候冲突了????
于是第二个方案来了:(在方法上加上了 synchronized 修饰词) 锁住方法,防止两个请求同时访问
public synchronized List<CommentReply> findPageReply(int pageNum, int i, int comment_id) {
String sql="SELECT * from comment_reply WHERE comment_id='" + comment_id + "' LIMIT ?,?";
Object [] param={pageNum*i-i,i};
List<CommentReply> list=null;
try {
if (conn==null||conn.isClosed()){
conn=JDBCUtil.getConnection();
}
list=queryRunner.query(conn,sql,new BeanListHandler<CommentReply>(CommentReply.class),param);
}catch (Exception ex){
ex.printStackTrace();
}finally {
try {
if(conn!=null&&!conn.isClosed()){
conn.close();
}
}catch (Exception EX){
EX.printStackTrace();
}
}
return list;
调试了下,还是不行!!!
分析了下,得出结论.....到底是谁在在我正在访问的时候关了数据库的接口!!!!
我用的是Dbutils//
然后我就索性把代码全改成了jdbc写
public synchronized List<CommentReply> findPageReply(int pageNum, int i, int comment_id) {
String sql = "SELECT * from comment_reply WHERE comment_id='" + comment_id + "' LIMIT ?,?";
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
conn = JDBCUtil.getConnection();
List<CommentReply> list = new ArrayList<CommentReply>();
try {
st = conn.prepareStatement(sql);
st.setInt(1, pageNum * i - i);
st.setInt(2, i);
rs = st.executeQuery();
while (rs.next()) {
CommentReply commentReply = new CommentReply();
commentReply.setId(rs.getInt("id"));
commentReply.setComment_id(rs.getInt("comment_id"));
commentReply.setContent(rs.getString("content"));
commentReply.setFromer(rs.getString("fromer"));
commentReply.setToer(rs.getString("toer"));
list.add(commentReply);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
rs.close();
st.close();
conn.close();
} catch (Exception ex) {
ex.printStackTrace();
}
return list;
}
没想到可以了!!!再也没有报题目的那个异常了....
那么,到底是哪里出问题了??
看了下Dao层里面的成员变量
public class UserDao {
public static Connection conn;
public static QueryRunner queryRunner = new QueryRunner();
private final static String DRIVER = "com.mysql.jdbc.Driver";
private final static String URL = "jdbc:mysql://localhost:3306/bluemsun?useUnicode=true&characterEncoding=utf-8";
private final static String USERNAME = "root";
private final static String PASSWORD = "root";
queryRunner 是一个静态成员变量
首先我怀疑是
QueryRunner,毕竟它帮我封装了那么多东西.....
于是呢,我就在每次执行查询前,重新new 一个实例。。
QueryRunner queryRunner1 = new QueryRunner();
public synchronized List<CommentReply> findPageReply(int pageNum, int i, int comment_id) {
String sql="SELECT * from comment_reply WHERE comment_id='" + comment_id + "' LIMIT ?,?";
Object [] param={pageNum*i-i,i};
List<CommentReply> list=null;
QueryRunner queryRunner1 = new QueryRunner();
try {
if (conn==null||conn.isClosed()){
conn=JDBCUtil.getConnection();
}
list=queryRunner1.query(conn,sql,new BeanListHandler<CommentReply>(CommentReply.class),param);
}catch (Exception ex){
ex.printStackTrace();
}finally {
try {
if(conn!=null&&!conn.isClosed()){
conn.close();
}
}catch (Exception EX){
EX.printStackTrace();
}
}
return list;
}
以为就此可以解决这个问题了, 跑了下,wtf!!还是抛出异常!!!
实在是想不明白,这个配置啥的,都是在网上的热门博客粘的代码,为什么一到高并发,多线程的时候就出问题???
想来想去,终于知道了为什么了!!!!问题就是在static 这个关键字,
无论我在怎么去new出一个对象,只要最终我的conn关闭了,那么它就影响了我的静态成员变量conn ,直接就把conn给关了,
也就是这样子,当其他的线程访问到sql语句的时候,突然上一个语句执行完毕,把conn给关了!!
数据库给你抛出异常也是理所当然,
最后我修了下:
public class SystemDao {
//如果加了static修饰词的话并发问题无法得到解决!!!
public Connection conn;
public QueryRunner queryRunner = new QueryRunner();
private final static String DRIVER = "com.mysql.jdbc.Driver";
private final static String URL = "jdbc:mysql://localhost:3306/bluemsun?useUnicode=true&characterEncoding=utf-8";
private final static String USERNAME = "root";
private final static String PASSWORD = "root";
private static final Logger LOGGER = Logger.getLogger(SystemDao.class);
成功了!!!!
问题完美解决,同时一次性6次ajax访问同一个方法也不抛出异常了!!!