2015-12-06 2 views
0

Прежде всего, у меня нет большого опыта в программировании с потоком.Java async MySQL запросы

У меня есть класс MySQL, и я хочу использовать один экземпляр в нескольких потоках, чтобы предотвратить блокировку кода в основном потоке. Я читал о пуле соединений, но я хочу сохранить его таким простым, как есть.

Это мой MySQL класс:

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; 
    } 
} 

Можно ли сделать мой класс MySQL Потокобезопасным с синхронизированным ключевым словом, как я уже использовал его в коде выше?

Я использую этот класс, как, что из различных потоков:

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()); 
} 

Мой вопрос: это Потокобезопасная?

Edit:

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"); 
      } 
     } 
    } 
} 

ответ

1

Ваш вопрос: ли это поточно?

Мой ответ: Нет, это не так.

Самый простой способ разбить его: Есть один из ваших нитей называют mySQL.getConnection().close();

Помимо этого: Большинство соединений не любят параллельные заявления вообще. Каким должен быть объем транзакции?

Вы должны серьезно рассмотреть возможность использования пула соединений. Мой любимый выбор был бы c3p0. См. http://www.mchange.com/projects/c3p0/#quickstart для примера быстрого запуска.

делает его безопасным

Вместо прохождения вокруг экземпляра MySQL, необходимо создать и настроить ComboPooledDataSource (или любой другой источник данных, который вы хотите использовать). Затем внутри ваших классов получите 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 
    } 
} 

Некоторые больше информации о существующем классе

Вы не убирать кучу отчетности если вы делаете

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(); 
} 
+0

Да, но я не называю это таким образом. Вот почему я написал метод mySQL.close(); – stonar96

+0

Я знаю. Это просто продемонстрировать * небезопасность * этого подхода. В основном, что MySQL Connection работает на ** одном Socket ** и не будет хорошо реагировать, если некоторые команды отправляются вне порядка. – Jan

+0

Кроме того - код для установки пула c3p0 меньше строк, чем ваш текущий метод checkConnection. Поэтому вы действительно должны это делать. – Jan