Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

Стартап (Туториал) Работаем с MySQL

Klavy

Активный участник
Пользователь
Баллы
66
Имя в Minecraft
Klavy
Итак, приветствую. В этом туториале я расскажу и покажу на наглядном примере работу с MySQL на Bukkit.

Если вы не знакомы с малейшими основами Java или Bukkit, рекомендую сначала обратиться к туториалам, часто названных как "Первый плагин Bukkit" и тп.

Использовать мы будем драйвер JDBC, он уже содержится в Bukkit'е, импортировать ничего дополнительного не надо.

С MySQL работать желательно только в асинхронном потоке, что мы и будем делать.

Для начала, следует создать отдельный класс для БД. В моём случае он будет называться SQLConnection. Класс будет объектом, конструктор будет пустым (все самое необходимое в connect() будет).
Код:
public SQLConnection() {}

Далее создаём метод для соединения. Он будет у нас публичным, не статическим. В нём запрашиваем данные для соединения - хост, имя пользователя, пароль, имя базы. Если вы хотите добавить поддержку нестандартного порта (не 3306), добавьте еще int port
Код:
public void connect(String host, String username, String password, String databaseName)
{

}

Теперь, добавим приватную переменную типа Connection (пакета java.sql). В ней будем держать соединение к базе. Назову её connection. Содержание - null
Код:
private Connection connection;

Так как мы работаем в асинхронном потоке. добавим запрос на BukkitScheduler в connect()
Код:
Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () ->
{

});
На месте Main.getInstance() идёт JavaPlugin, инициализированный класс plugin.yml

После этого, можем добавить содержимое метода connect. Для начала рекомендую проверить, не открыто ли уже соединение. В метод добавим:
Код:
if (connection != null)
{
    close();
}
Метода close() пока нет, добавим его в скором времени.

Добавляем само соединение. Так как DriverManager#getConnection бросает SQLException, используем try и catch. getConnection требует MySQL-ссылку, имя пользователя и пароль.
Код:
try
{
    Class.forName("com.mysql.jdbc.Driver");
    connection = DriverManager.getConnection("jdbc:mysql://" + host + ":3306/" + databaseName + "?useUnicode=true&characterEncoding=utf8&autoReconnect=true" , username, password);
}
catch (SQLException ex)
{
    ex.printStackTrace();
}
Как вы могли заметить, я добавил 3 парамента к соединению. Первые 2 для нормального отображения кодировки utf8. autoReconnect можете заменить по вкусу; он отвечает за автоподключение при потере соединения, запрашивает булеан.

С методом close() все достаточно просто. У Connection есть метод close, чем мы и воспользуемся. public/private - решайте по нужде.
Код:
public void close()
{
    Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), () ->
    {
        try {
            connection.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    });
}

Можете уже себя похвалить. Осталось только сделать пару методов на запросы. Если будете их ставить в сторонние классы, не забудьте про getter connection'а. Я делаю это в классе SQLConnection, мне геттер не требуется.

Рассмотрим мы 3 метода PreparedStatement и их использование.
executeQuery() - Этот метод используется для получения каких либо данных из базы. Возвращает ResultSet - еще один класс, содержащий в себе ответ.
executeUpdate() - Используется для модификации содержимого базы данных (например: INSERT, DELETE, UPDATE). Возвращает int, содержащий в себе кол-во модифицированных строк.
execute() - Используется для любого типа запроса. Возвращает boolean, зависимый от наличия ResultSet в ответе

Пример использования:
Код:
try
{
    PreparedStatement preparedStatement = connection.prepareStatement(query);
    ResultSet resultSet = preparedStatement.executeQuery();
}
catch (SQLException ex)
{
    ex.printStackTrace();
}
Используйте в асинхронном потоке.

Запрашивать в главном классе очень просто.
Код:
private SQLConnection connection;

public void onEnable()
{
    connection = new SQLConnection();
    connection.connect(/* Параметры */);
}

На этом всё.
Будут какие либо вопросы, пишите :)
(Это лишь первая версия туториала. Могут содержаться опечатки, я не робот :whistle:)

Если ничего не понятно, вот вам первая помощь:
Могу добавить свой класс для примера.
Код:
package Example.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;

public class MySQL {
    private Connection conn;
    private Statement statmt;
    private PreparedStatement preparedStatement = null;

    public MySQL(String url, String dbName, String user, String pass) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(
                    "jdbc:mysql://" + url + "/" + dbName + "?useUnicode=true&characterEncoding=utf8&autoReconnect=true",
                    user, pass);
            statmt = conn.createStatement();
            statmt.execute(
                    "CREATE TABLE IF NOT EXISTS `users` (`user` varchar(255) PRIMARY KEY,`time` varchar(255) NOT NULL)");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void insert(String user, Long time) {
        try {
            PreparedStatement e = conn
                    .prepareStatement("INSERT INTO users (user,time) VALUES (?,?) ON DUPLICATE KEY UPDATE time = ?;");
            e.setString(1, user);
            e.setLong(2, time);
            e.setLong(3, time);
            e.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ArrayList<String> select(String user) {
        try {
            preparedStatement = conn.prepareStatement("SELECT * FROM users WHERE user = ?;");
            preparedStatement.setString(1, user);
            ResultSet e = preparedStatement.executeQuery();
            ArrayList<String> item = new ArrayList<String>();
            if (e.next()) {
                item.add(e.getString("user"));
                item.add(e.getString("time"));
                return item;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
 
Последнее редактирование:
Могу добавить свой класс для примера.
Код:
package Example.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;

public class MySQL {
    private Connection conn;
    private Statement statmt;
    private PreparedStatement preparedStatement = null;

    public MySQL(String url, String dbName, String user, String pass) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(
                    "jdbc:mysql://" + url + "/" + dbName + "?useUnicode=true&characterEncoding=utf8&autoReconnect=true",
                    user, pass);
            statmt = conn.createStatement();
            statmt.execute(
                    "CREATE TABLE IF NOT EXISTS `users` (`user` varchar(255) PRIMARY KEY,`time` varchar(255) NOT NULL)");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void insert(String user, Long time) {
        try {
            PreparedStatement e = conn
                    .prepareStatement("INSERT INTO users (user,time) VALUES (?,?) ON DUPLICATE KEY UPDATE time = ?;");
            e.setString(1, user);
            e.setLong(2, time);
            e.setLong(3, time);
            e.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ArrayList<String> select(String user) {
        try {
            preparedStatement = conn.prepareStatement("SELECT * FROM users WHERE user = ?;");
            preparedStatement.setString(1, user);
            ResultSet e = preparedStatement.executeQuery();
            ArrayList<String> item = new ArrayList<String>();
            if (e.next()) {
                item.add(e.getString("user"));
                item.add(e.getString("time"));
                return item;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
 
Найс гайд...
Почему все статичное? Ты учишь их как побыстрее начать говнокодить?
Асинк? У тебя там жёсткая обработка какая-то?
Почему обязательно нужно выкладывать свои гайды как только выучил что-то?
Найс рофл. @alexandrage. Надеюсь, что это рофл, конечно
Может вы лучше научите как делать нормальные менеджеры баз данных? А лучше покажите как юзать HikariPool, вот это рили нормальная тема для новеньких будет
 
Могу добавить свой класс для примера.
Код:
package Example.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;

public class MySQL {
    private Connection conn;
    private Statement statmt;
    private PreparedStatement preparedStatement = null;

    public MySQL(String url, String dbName, String user, String pass) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(
                    "jdbc:mysql://" + url + "/" + dbName + "?useUnicode=true&characterEncoding=utf8&autoReconnect=true",
                    user, pass);
            statmt = conn.createStatement();
            statmt.execute(
                    "CREATE TABLE IF NOT EXISTS `users` (`user` varchar(255) PRIMARY KEY,`time` varchar(255) NOT NULL)");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void insert(String user, Long time) {
        try {
            PreparedStatement e = conn
                    .prepareStatement("INSERT INTO users (user,time) VALUES (?,?) ON DUPLICATE KEY UPDATE time = ?;");
            e.setString(1, user);
            e.setLong(2, time);
            e.setLong(3, time);
            e.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ArrayList<String> select(String user) {
        try {
            preparedStatement = conn.prepareStatement("SELECT * FROM users WHERE user = ?;");
            preparedStatement.setString(1, user);
            ResultSet e = preparedStatement.executeQuery();
            ArrayList<String> item = new ArrayList<String>();
            if (e.next()) {
                item.add(e.getString("user"));
                item.add(e.getString("time"));
                return item;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
Найс гайд...
Почему все статичное? Ты учишь их как побыстрее начать говнокодить?
Асинк? У тебя там жёсткая обработка какая-то?
Почему обязательно нужно выкладывать свои гайды как только выучил что-то?
Найс рофл. @alexandrage. Надеюсь, что это рофл, конечно
Может вы лучше научите как делать нормальные менеджеры баз данных? А лучше покажите как юзать HikariPool, вот это рили нормальная тема для новеньких будет
Асинк на любой случай жизни. Мало ли кто там что захочет.
Статик лишь для понимания, я не плагин все-таки выкладываю. Кто понимает объекты, пусть делают объектом.
Этот гайд по MySQL, HikariPool в другом будет :cool: (Все же так хотят MySQL)
 
Асинк на любой случай жизни. Мало ли кто там что захочет.
Статик лишь для понимания, я не плагин все-таки выкладываю.
Этот гайд по MySQL, HikariPool в другом будет :cool: (Все же так хотят MySQL)
Всем так не терпится зашкварится от mysql?)
В Hikari тоже есть поддержка mysql)
Излишний асинк не гуд, особенно в плагинах на сто строк, где половина занимает этот самый асинк
 
Неважно сколько строк в плагине. Работа с внешними ресурсами должна осуществляться асинхронно.
Объясни мне, почему "не гуд"?
Я ему не о работе с mysql говорил, а в целом за асинк, baaka:lol:
Кстати, покажешь мне эти "тяжелые запросы"?)
 
Даже не учитывая что нет пулов слабовато немного. Асинхронные методы как по моему мнению не совсем правильны. Если это туториал для совсем новичков, то пусть хотя бы без них сначала обойдутся. Потом познают многопоточность и уже разберутся что нужно добавлять, а сейчас они могут наделать кучу ошибок, тупо копируя код. Про правильность асинхронного использования - я думаю, что любые взаемодействия с тем самым Connection должны быть синхронизированны между собой, ибо асинхронно менять состояние обьекта в неск разных местах не совсем безопасно.

Далее, нет ничего даже примерно про ResultSet, ах а там есть че рассказать. Какие нибудь предостережения на счет утечки ресурсов, ибо потом заказчики опять мне суют тайминги и говорят что за херня, когда за пару минут гиг оперативы поднимает на сервере, и оказывается какой то говно плагин без закрытия соединений / результатов.

В самом туториале код как то разбросан, что лично мне сложно складывать все части воедине. Нет четких примеров использования. Ну в общем нужно сделать получше. Пока так, на 2/5.
 
1. Зачем асинк?
2. Ох уж этот хардкод url
3. Ну добавил ещё зачем-то лишнего текста
4. Чистая придирка под конец. Форматирование ужас
 

Поделиться этой страницей

Назад
Верх