1. Этот сайт использует файлы cookie. Продолжая пользоваться данным сайтом, Вы соглашаетесь на использование нами Ваших файлов cookie. Узнать больше.
  2. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

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

Тема в разделе "Разработка плагинов для новичков", создана пользователем Klavy, 15 авг 2018.

  1. Автор темы
    Klavy

    Klavy Активный участник Пользователь

    Баллы:
    66
    Итак, приветствую. В этом туториале я расскажу и покажу на наглядном примере работу с 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:)

    Если ничего не понятно, вот вам первая помощь:
     
    Последнее редактирование: 15 авг 2018
  2. alexandrage

    alexandrage Старожил Пользователь

    Баллы:
    173
    Могу добавить свой класс для примера.
    Код:
    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;
        }
    }
     
  3. Rider3217

    Rider3217 Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Planet0111
    Найс гайд...
    Почему все статичное? Ты учишь их как побыстрее начать говнокодить?
    Асинк? У тебя там жёсткая обработка какая-то?
    Почему обязательно нужно выкладывать свои гайды как только выучил что-то?
    Найс рофл. @alexandrage. Надеюсь, что это рофл, конечно
    Может вы лучше научите как делать нормальные менеджеры баз данных? А лучше покажите как юзать HikariPool, вот это рили нормальная тема для новеньких будет
     
  4. Rider3217

    Rider3217 Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Planet0111
    Лел, может быть)0
     
  5. Автор темы
    Klavy

    Klavy Активный участник Пользователь

    Баллы:
    66
    Асинк на любой случай жизни. Мало ли кто там что захочет.
    Статик лишь для понимания, я не плагин все-таки выкладываю. Кто понимает объекты, пусть делают объектом.
    Этот гайд по MySQL, HikariPool в другом будет :cool: (Все же так хотят MySQL)
     
  6. Rider3217

    Rider3217 Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Planet0111
    Всем так не терпится зашкварится от mysql?)
    В Hikari тоже есть поддержка mysql)
    Излишний асинк не гуд, особенно в плагинах на сто строк, где половина занимает этот самый асинк
     
  7. Автор темы
    Klavy

    Klavy Активный участник Пользователь

    Баллы:
    66
    Любят его... Скорее всего потому что 75% плагинов на майсикуэле :D
     
  8. Автор темы
    Klavy

    Klavy Активный участник Пользователь

    Баллы:
    66
    Исправил.
    Теперь новички будут в недоумении :eek:
    Ну, только те, кто объектов Java не знать
     
  9. Rider3217

    Rider3217 Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Planet0111
    Я ему не о работе с mysql говорил, а в целом за асинк, baaka:lol:
    Кстати, покажешь мне эти "тяжелые запросы"?)
     
  10. alexandrage

    alexandrage Старожил Пользователь

    Баллы:
    173
    Научите челика в Completablefuture. Если его прет от асинса.
     
  11. Автор темы
    Klavy

    Klavy Активный участник Пользователь

    Баллы:
    66
    Как все поняли, о mysql речь и шла
     
  12. Exception_Prototype

    Exception_Prototype Активный участник Пользователь

    Баллы:
    96
  13. LuckyZeeRo

    LuckyZeeRo Активный участник Пользователь

    Баллы:
    96
    Имя в Minecraft:
    i0xHeX
    Даже не учитывая что нет пулов слабовато немного. Асинхронные методы как по моему мнению не совсем правильны. Если это туториал для совсем новичков, то пусть хотя бы без них сначала обойдутся. Потом познают многопоточность и уже разберутся что нужно добавлять, а сейчас они могут наделать кучу ошибок, тупо копируя код. Про правильность асинхронного использования - я думаю, что любые взаемодействия с тем самым Connection должны быть синхронизированны между собой, ибо асинхронно менять состояние обьекта в неск разных местах не совсем безопасно.

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

    В самом туториале код как то разбросан, что лично мне сложно складывать все части воедине. Нет четких примеров использования. Ну в общем нужно сделать получше. Пока так, на 2/5.
     
  14. FrostDelta123

    FrostDelta123 Активный участник Пользователь

    Баллы:
    76
    Имя в Minecraft:
    FrostDelta123
    1. Зачем асинк?
    2. Ох уж этот хардкод url
    3. Ну добавил ещё зачем-то лишнего текста
    4. Чистая придирка под конец. Форматирование ужас
     
  15. Slavkaa

    Slavkaa Активный участник Пользователь

    Баллы:
    76
    Имя в Minecraft:
    Slavok2001
  16. Rider3217

    Rider3217 Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Planet0111
    Ты знаешь зачем и где нужен AutoCloseable ? Так вот у тебя он не нужен там, а Connection и так его наследует
     

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