赞
踩
在Java开发过程中,你可能遇到了这样一个错误信息:
com.mysql.cj.jdbc.exceptions.CommunicationsException: The last packet successfully received from the server was 60,599,873 milliseconds ago. The last packet sent successfully to the server was 60,599,876 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
这个错误信息来源于MySQL的JDBC驱动,它表示MySQL服务器在一段时间内没有收到来自客户端的任何数据包,超过了MySQL服务器设置的’wait_timeout’参数的值。这时,MySQL服务器会主动断开与客户端的连接,当客户端再次尝试使用这个已经被服务器断开的连接时,就会抛出上述错误。
这个报错可能会导致以下的问题:
MySQL的’wait_timeout’参数定义了服务器在关闭非交互式连接前等待活动的秒数。非交互式连接指的是对于命令行客户端,除非在启动时明确指定–interactive-timeout选项,否则其超时值将由’wait_timeout’变量决定。如果在这个时间内,服务器没有收到来自客户端的任何数据,它就会自动关闭连接。
这个参数的默认值可能会根据MySQL的版本和配置有所不同,但通常在几小时到几天之间。
查看’wait_timeout’参数的值,可以在mysql客户端执行以下命令:
SHOW VARIABLES LIKE 'wait_timeout';
修改’wait_timeout’参数的值,可以在mysql客户端执行以下命令:
SET GLOBAL wait_timeout = time_in_seconds;
其中,time_in_seconds
是你想要设置的新的超时时间,单位为秒。
请注意,如果你想要永久修改这个参数的值,你需要在MySQL的配置文件(通常是my.cnf
或my.ini
)中进行设置,并重启MySQL服务器。在配置文件中,你可以在[mysqld]
部分添加或修改以下行:
wait_timeout = time_in_seconds
同样,time_in_seconds
是你想要设置的新的超时时间,单位为秒。
在Java中,我们通常使用JDBC(Java Database Connectivity)API来连接MySQL数据库。以下是一个简单的示例:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class TestConnection { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/testdb"; String username = "testuser"; String password = "testpassword"; try { Connection connection = DriverManager.getConnection(url, username, password); System.out.println("Connected to the database."); // do something with the connection... connection.close(); } catch (SQLException e) { System.out.println("Cannot connect to the database."); e.printStackTrace(); } } }
在这个例子中,我们首先导入了java.sql.Connection
和java.sql.DriverManager
类。然后,我们定义了数据库的URL、用户名和密码。接着,我们使用DriverManager.getConnection()
方法来创建一个新的数据库连接。
在Java程序中,一个数据库连接的生命周期通常如下:
DriverManager.getConnection()
方法时,会创建一个新的数据库连接。Connection
对象的createStatement()
或prepareStatement()
方法来创建一个Statement
或PreparedStatement
对象,然后使用这个对象来执行SQL命令。Connection.close()
方法来关闭连接。这是非常重要的,因为如果你不关闭连接,那么这个连接可能会一直保持打开状态,直到程序结束。这可能会消耗大量的系统资源,并且可能会导致数据库服务器达到最大连接数限制。需要注意的是,如果你的程序使用了连接池(例如,Apache DBCP或C3P0),那么连接的生命周期可能会有所不同。在这种情况下,当你调用Connection.close()
方法时,连接通常不会真的被关闭,而是被返回到连接池中,以便后续再次使用。
在下面的Java代码示例中,我们创建了一个MySQL连接,然后让线程休眠一段时间(例如,超过wait_timeout
参数设置的时间),然后再试图执行一个SQL语句。这将模拟长时间无交互导致的连接断开的情况。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class TestConnection { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/testdb"; String username = "testuser"; String password = "testpassword"; try { Connection connection = DriverManager.getConnection(url, username, password); System.out.println("Connected to the database."); // Sleep for a while to simulate inactivity Thread.sleep(10000); // adjust this value as needed // Now try to do something with the connection Statement stmt = connection.createStatement(); stmt.executeQuery("SELECT 1"); System.out.println("Query executed."); connection.close(); } catch (SQLException e) { System.out.println("Cannot connect to the database."); e.printStackTrace(); } catch (InterruptedException e) { System.out.println("Thread was interrupted."); e.printStackTrace(); } } }
如果在休眠期间MySQL服务器关闭了连接,当我们试图使用这个连接执行SQL语句时,我们可能会收到一个SQLException
。这个异常的消息可能类似于"Communications link failure"或"The last packet successfully received from the server was XXX milliseconds ago.",这表明连接已经被断开。
为了解决这个问题,我们可以:
wait_timeout
参数的值,以允许连接保持更长时间的不活动状态。你可以通过调整MySQL的’wait_timeout’参数来解决这个问题。'wait_timeout’参数定义了非交互式连接(如Java应用程序到MySQL服务器的连接)在空闲状态下的最长等待时间。当连接空闲时间超过这个参数的值时,MySQL服务器将自动关闭该连接。
操作步骤:
SHOW VARIABLES LIKE 'wait_timeout';
SET GLOBAL wait_timeout=28800;
注意: 这种方法的缺点是需要直接修改MySQL服务器的配置,可能需要数据库管理员的权限。
连接池可以有效地管理和复用数据库连接,避免了频繁地创建和关闭连接,同时可以防止连接空闲过久被MySQL服务器自动关闭。在大部分项目中都是用连接池管理的,不用关注该方案
如何使用连接池:
以下是使用HikariCP连接池的示例代码:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
config.setUsername("root");
config.setPassword("password");
config.addDataSourceProperty("autoReconnect", "true");
HikariDataSource ds = new HikariDataSource(config);
// 使用完后记得关闭
ds.close();
将’autoReconnect=true’添加到JDBC连接字符串中,可以使得在连接被MySQL服务器自动关闭后,Java应用程序可以自动重新连接。在大部分项目中都是用连接池管理的,不用关注该方案
如何设置’autoReconnect=true’:
以下是在JDBC连接字符串中设置’autoReconnect=true’的示例代码:
String url = "jdbc:mysql://localhost:3306/test?autoReconnect=true";
Connection connection = DriverManager.getConnection(url, "root", "password");
在方法上使用@Transactional注解,Spring会在该方法执行前自动开始一个新的事务,并在该方法执行后自动提交或回滚事务。
示例代码:
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void updateUser(User user) {
userRepository.save(user);
}
}
当你调用transactionManager.getTransaction()方法时,你需要显式地开始、提交或回滚事务。
这种方式经过实践 如果是在循环中创建编程时事务 还是有可能出现标题中的报错 所以如果有循环处理场景推荐使用方案四
示例代码:
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; @Service public class UserService { @Autowired private PlatformTransactionManager transactionManager; @Autowired private UserRepository userRepository; public void updateUser(User user) { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { userRepository.save(user); transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw e; } } }
在网上翻阅其他人的解决方案时候,发现网络问题也可能导致这个报错
https://zhuanlan.zhihu.com/p/550145054
在网上翻阅其他人的解决方案时候,发现也可以通过修改java应用中的连接池配置来实现 不同的数据库连接池配置名可能略有不同
https://blog.csdn.net/weixin_45204847/article/details/112282739
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。