Java 异步 MySQL 查询
Java async MySQL queries
首先,我对线程安全编程经验不多
我有一个MySQLclass,我想在多个线程中使用一个实例,以防止在主线程中阻塞代码。我阅读了有关连接池的信息,但我希望它尽可能简单。
这是我的 MySQL class:
package com.vanillage.bukkitutils.mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MySQL {
private final String host;
private final int port;
private final String database;
private final String user;
private final String password;
private Connection connection;
public MySQL(String host, int port, String database, String user, String password) {
if (host == null) {
//TODO
}
if (database == null) {
//TODO
}
if (user == null) {
//TODO
}
if (password == null) {
//TODO
}
this.host = host;
this.port = port;
this.database = database;
this.user = user;
this.password = password;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getDatabase() {
return database;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
public Connection getConnection() {
return connection;
}
public synchronized void connect() throws SQLException {
connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?autoReconnect=true", user, password);
}
public synchronized void checkConnection(int timeout) throws SQLException {
if (connection == null) {
connect();
} else {
boolean connectionValid = false;
try {
connectionValid = connection.isValid(timeout);
} catch (SQLException e) {
e.printStackTrace();
connect();
connectionValid = true;
}
if (!connectionValid) {
connect();
}
}
}
public synchronized ResultSet query(String query) throws SQLException {
return connection.prepareStatement(query).executeQuery();
}
public synchronized boolean update(String query) throws SQLException {
return connection.prepareStatement(query).execute();
}
public synchronized void close() throws SQLException {
if (connection != null) {
connection.close();
connection = null;
}
}
public synchronized boolean hasConnection(boolean checkOpen, boolean checkValid, int timeout) throws SQLException {
return connection != null && (!checkOpen || !connection.isClosed()) && (!checkValid || connection.isValid(timeout));
}
@Override
public String toString() {
return host + ":" + port + ", " + database + ", " + user + ", " + password;
}
}
是否可以使用 synchronized 关键字使我的 MySQL class 线程安全,因为我已经在上面的代码中使用了它?
我使用这个 class 就像来自不同线程的那样:
try {
mySQL.checkConnection(0);
try {
ResultSet resultSet = mySQL.query("SELECT * FROM Example");
if (resultSet.next()) {
System.out.println(resultSet.getString("Example"));
}
} catch (SQLException e) {
System.out.println("Error while executing query: " + e.getMessage());
}
} catch (SQLException e) {
System.out.println("Could not create a valid connection: " + e.getMessage());
}
我的问题:它是线程安全的吗?
编辑:
package testprogramm;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class TestProgramm {
private static final Scanner scanner = new Scanner(System.in);
private static Map<String, ComboPooledDataSource> dataSources = new HashMap<>();
public static void main(String[] args) {
//setup connections
ComboPooledDataSource testDataSource = new ComboPooledDataSource();
try {
testDataSource.setDriverClass("com.mysql.jdbc.Driver");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
testDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/database");
testDataSource.setUser("user");
testDataSource.setPassword("password");
dataSources.put("test", testDataSource);
while (true) {
String line = scanner.nextLine();
ComboPooledDataSource dataSource = dataSources.get(line);
if (dataSource != null) {
new Thread(new Runnable() {
ComboPooledDataSource dataSource = null;
public Runnable init(ComboPooledDataSource dataSource) {
this.dataSource = dataSource;
return this;
}
@Override
public void run() {
try {
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM Example");
ResultSet resultSet = preparedStatement.executeQuery();
int i = 0;
while (resultSet.next()) {
i ++;
}
resultSet.close();//TODO: move to finally clause with null check and try/catch
preparedStatement.close();
connection.close();
System.out.println(i + " entries");
} catch (SQLException e) {
System.out.println("Error while executing statement: " + e.getMessage());
}
}
}.init(dataSource)).start();
} else {
System.out.println("No such connection");
}
}
}
}
您的问题:线程安全吗?
我的回答:不,不是。
打破它的最简单方法:让您的线程之一调用 mySQL.getConnection().close();
除此之外:大多数连接根本不喜欢并行语句。那应该是什么交易范围呢?
您应该认真地考虑使用连接池。我最喜欢的选择是 c3p0。有关快速入门示例,请参阅 http://www.mchange.com/projects/c3p0/#quickstart。
确保安全
您无需传递 MySQL
的实例,而是创建并配置 ComboPooledDataSource
(或您要使用的任何其他数据源)。然后在您的 classes 中,从该池中获取 Connection
,执行您的 SQL 语句,然后关闭它。最方便的方法是使用 Java 7:
引入的 try-with-resource
try(Connection con = pool.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM whatever");
ResultSet rs = ps.executeQuery()) {
while(rs.next()) {
//handle resultset
}
}
有关您现有 class
的更多信息
如果你这样做,你将无法清理大量的语句
public synchronized ResultSet query(String query) throws SQLException {
//Statement never closed
return connection.prepareStatement(query).executeQuery();
}
public synchronized boolean update(String query) throws SQLException {
//Statement never closed
return connection.prepareStatement(query).execute();
}
另一种选择是使用像 jasync-sql.
这样的异步驱动程序
它是这样使用的:
// Connect to DB
Connection connection = new MySQLConnection(
new Configuration(
"username",
"host.com",
3306,
"password",
"schema"
)
);
CompletableFuture<?> connectFuture = connection.connect()
// Wait for connection to be ready
// ...
// Execute query
CompletableFuture<QueryResult> future =
connection.sendPreparedStatement("select * from table");
// Close the connection
connection.disconnect().get()
希望对您有所帮助。
免责声明:我维护这个项目。
首先,我对线程安全编程经验不多
我有一个MySQLclass,我想在多个线程中使用一个实例,以防止在主线程中阻塞代码。我阅读了有关连接池的信息,但我希望它尽可能简单。
这是我的 MySQL class:
package com.vanillage.bukkitutils.mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MySQL {
private final String host;
private final int port;
private final String database;
private final String user;
private final String password;
private Connection connection;
public MySQL(String host, int port, String database, String user, String password) {
if (host == null) {
//TODO
}
if (database == null) {
//TODO
}
if (user == null) {
//TODO
}
if (password == null) {
//TODO
}
this.host = host;
this.port = port;
this.database = database;
this.user = user;
this.password = password;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getDatabase() {
return database;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
public Connection getConnection() {
return connection;
}
public synchronized void connect() throws SQLException {
connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?autoReconnect=true", user, password);
}
public synchronized void checkConnection(int timeout) throws SQLException {
if (connection == null) {
connect();
} else {
boolean connectionValid = false;
try {
connectionValid = connection.isValid(timeout);
} catch (SQLException e) {
e.printStackTrace();
connect();
connectionValid = true;
}
if (!connectionValid) {
connect();
}
}
}
public synchronized ResultSet query(String query) throws SQLException {
return connection.prepareStatement(query).executeQuery();
}
public synchronized boolean update(String query) throws SQLException {
return connection.prepareStatement(query).execute();
}
public synchronized void close() throws SQLException {
if (connection != null) {
connection.close();
connection = null;
}
}
public synchronized boolean hasConnection(boolean checkOpen, boolean checkValid, int timeout) throws SQLException {
return connection != null && (!checkOpen || !connection.isClosed()) && (!checkValid || connection.isValid(timeout));
}
@Override
public String toString() {
return host + ":" + port + ", " + database + ", " + user + ", " + password;
}
}
是否可以使用 synchronized 关键字使我的 MySQL class 线程安全,因为我已经在上面的代码中使用了它?
我使用这个 class 就像来自不同线程的那样:
try {
mySQL.checkConnection(0);
try {
ResultSet resultSet = mySQL.query("SELECT * FROM Example");
if (resultSet.next()) {
System.out.println(resultSet.getString("Example"));
}
} catch (SQLException e) {
System.out.println("Error while executing query: " + e.getMessage());
}
} catch (SQLException e) {
System.out.println("Could not create a valid connection: " + e.getMessage());
}
我的问题:它是线程安全的吗?
编辑:
package testprogramm;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class TestProgramm {
private static final Scanner scanner = new Scanner(System.in);
private static Map<String, ComboPooledDataSource> dataSources = new HashMap<>();
public static void main(String[] args) {
//setup connections
ComboPooledDataSource testDataSource = new ComboPooledDataSource();
try {
testDataSource.setDriverClass("com.mysql.jdbc.Driver");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
testDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/database");
testDataSource.setUser("user");
testDataSource.setPassword("password");
dataSources.put("test", testDataSource);
while (true) {
String line = scanner.nextLine();
ComboPooledDataSource dataSource = dataSources.get(line);
if (dataSource != null) {
new Thread(new Runnable() {
ComboPooledDataSource dataSource = null;
public Runnable init(ComboPooledDataSource dataSource) {
this.dataSource = dataSource;
return this;
}
@Override
public void run() {
try {
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM Example");
ResultSet resultSet = preparedStatement.executeQuery();
int i = 0;
while (resultSet.next()) {
i ++;
}
resultSet.close();//TODO: move to finally clause with null check and try/catch
preparedStatement.close();
connection.close();
System.out.println(i + " entries");
} catch (SQLException e) {
System.out.println("Error while executing statement: " + e.getMessage());
}
}
}.init(dataSource)).start();
} else {
System.out.println("No such connection");
}
}
}
}
您的问题:线程安全吗?
我的回答:不,不是。
打破它的最简单方法:让您的线程之一调用 mySQL.getConnection().close();
除此之外:大多数连接根本不喜欢并行语句。那应该是什么交易范围呢?
您应该认真地考虑使用连接池。我最喜欢的选择是 c3p0。有关快速入门示例,请参阅 http://www.mchange.com/projects/c3p0/#quickstart。
确保安全
您无需传递 MySQL
的实例,而是创建并配置 ComboPooledDataSource
(或您要使用的任何其他数据源)。然后在您的 classes 中,从该池中获取 Connection
,执行您的 SQL 语句,然后关闭它。最方便的方法是使用 Java 7:
try(Connection con = pool.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM whatever");
ResultSet rs = ps.executeQuery()) {
while(rs.next()) {
//handle resultset
}
}
有关您现有 class
的更多信息如果你这样做,你将无法清理大量的语句
public synchronized ResultSet query(String query) throws SQLException {
//Statement never closed
return connection.prepareStatement(query).executeQuery();
}
public synchronized boolean update(String query) throws SQLException {
//Statement never closed
return connection.prepareStatement(query).execute();
}
另一种选择是使用像 jasync-sql.
这样的异步驱动程序它是这样使用的:
// Connect to DB
Connection connection = new MySQLConnection(
new Configuration(
"username",
"host.com",
3306,
"password",
"schema"
)
);
CompletableFuture<?> connectFuture = connection.connect()
// Wait for connection to be ready
// ...
// Execute query
CompletableFuture<QueryResult> future =
connection.sendPreparedStatement("select * from table");
// Close the connection
connection.disconnect().get()
希望对您有所帮助。
免责声明:我维护这个项目。