Правильна ли процедура MySQL в этом плагине Minecraft?

Чтобы получить представление о том, как выглядит базовая структура, я скачал денежную систему, включающую MySQL, со Spigot и посмотрел на код.

      public static boolean playerExists(String uuid) {
    try {
      ResultSet rs = Simplecoinsystem.mysql.query("SELECT * FROM CoinData WHERE UUID= '" + uuid + "'");
      if (rs.next())
        return (rs.getString("UUID") != null); 
      return false;
    } catch (SQLException e) {
      e.printStackTrace();
      return false;
    } 
}
  
public static void createPlayer(String uuid) {
    if (!playerExists(uuid))
      Simplecoinsystem.mysql.update("INSERT INTO CoinData (UUID, COINS) VALUES ('" + uuid + 
          "', '" + Simplecoinsystem.getInstance().getConfig().getInt("startcoins") + "');"); 
}
  
public static Integer getCoins(String uuid) {
    Integer i = Integer.valueOf(0);
    if (playerExists(uuid)) {
      try {
        ResultSet rs = Simplecoinsystem.mysql.query("SELECT * FROM CoinData WHERE UUID= '" + uuid + "'");
        if (rs.next())
          Integer.valueOf(rs.getInt("COINS")); 
        i = Integer.valueOf(rs.getInt("COINS"));
      } catch (SQLException e) {
        e.printStackTrace();
      } 
    } else {
      createPlayer(uuid);
    } 
    return i;
}
  
public static void setCoins(String uuid, Integer coins) {
    if (playerExists(uuid)) {
      Simplecoinsystem.mysql.update("UPDATE CoinData SET COINS= '" + coins + "' WHERE UUID= '" + uuid + "';");
    } else {
      createPlayer(uuid);
    } 
}

Правильно ли я понимаю, что на самом деле нецелесообразно создавать новую запись с uuid несуществующего игрока после каждого запроса монет, если игрок не существует?

Не позволит ли это завалить базу тысячами ненужных записей, введя, например, команду «/money (player)» от злого игрока/админа?

А нельзя было просто при входе на сервер спросить, сохранен ли уже uuid и если нет, то просто ввести? Таким образом, будут записи только от игроков, которые уже были на сервере ранее. Нужна ли для этого высокая производительность сервера, я не уверен.

Это мой первый собственный класс MySQL.

      public class MySQL {
    
    private String host, database, user, password;
    private int port;
    
    private Connection con;
    
    
    public MySQL(String host, int port, String database, String user, String password) {
        this.host = host;
        this.port = port;
        this.database = database;
        this.user = user;
        this.password = password;
        
        connect();
    }
    
    public void connect() {
        try {
            con = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?autoReconnect=true", user, password);
            System.out.println("&cDie MySQL Verbindung wurde erfolgreich aufgebaut!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    public void disconnect() {
        try {
            if(this.con != null) {
                this.con.close();
                System.out.println("§cDie MySQL Verbindung wurde erfolgreich beendet!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void update(String query) {
        try {
            Statement st = con.createStatement();
            st.executeUpdate(query);
            st.close();
        } catch (Exception e) {
            e.printStackTrace();
            connect();
        }
    }
    
    public ResultSet qry(String query) {
        ResultSet rs = null;
        
        try {
            Statement st = con.createStatement();
            rs = st.executeQuery(query);
        } catch (Exception e) {
            e.printStackTrace();
            connect();
        }
        return rs;
    }
    
    public Connection getConnection() {
        return this.con;
    }

}

За исключением этой части, оба класса MySQL построены относительно одинаково. Это часть класса MySQL плагина Spigot.

2 ответа

Ваш код имеет несколько проблем.

  1. Когда соединение будет закрыто, в следующий раз у вас будет ошибка. В твоей Mysqlкласс, я предлагаю вам сделать:
      public Connection getConnection() {
   if(con == null || con.isClosed())
      connect();
   return con;
}

Затем используйте его во всех методах, например getConnection().prepareStatement().

  1. Вас могут атаковать с помощью SQL Injection. Чтобы исправить это, попробуйте сделать что-то вроде:
      PreparedStatement st = con.prepareStatement("SELECT * FROM CoinData WHERE UUID = ?");
st.setString(1, uuid.toString()); // Yes it start at 1 !!
st.executeUpdate();

При этом даже при всех значениях вас не могут атаковать инъекциями.

  1. У вас будет ошибка при получении монет:
      if (rs.next()) // go to good line
   Integer.valueOf(rs.getInt("COINS")); // useless convertion
i = Integer.valueOf(rs.getInt("COINS")); // error if no line.

Вы можете просто сделать:

      if(rs.next())
   i = rs.getInt("COINS");
  1. Если столбец «UUID» уникален, у вас не будет повторяющихся строк.
  2. Наконец, о производительности, лучше сделать это один раз: при входе в систему, а не все время. Вы также можете создать объект, хранящийся в хэш-карте, чтобы упростить доступ к нему без использования SQL, например:
      public static HashMap<UUID, Integer> coinsByPlayer = new HashMap<>();

ИЛИ ЖЕ:

      public static HashMap<UUID, MyObject> coinsByPlayer = new HashMap<>();
      public class MyObject {

   private int coins = 0;

   public MyObject(UUID uuid) {
       // make SQL request to get data

   }

   public int getCoins() {
       return coins;
   }

   public void setCoins(int next){
       coins = next;
       // here make "UPDATE" sql query
   }
}

Что ты говоришь? С функцией try/catch все в порядке? @Elikill58

      public Connection getConnection() {
    try {
        if(con == null || con.isClosed()) {
            connect();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return con;
}

редактировать:

      public Connection getConnection_one() throws SQLException {
    if(con == null || con.isClosed()) {
        connect();
        return con;
    } else {
        return con;
    }
}
Другие вопросы по тегам