Помогите Java & SQLite

Korvinius

Активный участник
Пользователь
Баллы
88
Пришло время, когда мне понадобилась база данных. В моем случае, самый оптимальный вариант, это SQLite. Долго гуглил, ковырялся в чужих плагинах, но, что то у меня обучение работы с БД почти не продвигается, поэтому прошу совета у Вас, подскажите где можно об этом почитать, на русском, украинском или белорусском языках.
Я вот навырезал с чужих плагинов кусков:
Java:
    public static Connection conn = null;
    public static ResultSet rset = null;

    public static void connect() {
        try {
            Class.forName("org.sqlite.JDBC").newInstance();
            conn = DriverManager.getConnection("jdbc:sqlite://" + Mystery.instance.getDataFolder().getAbsolutePath() + "/Mystery.db");
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        executeSync("CREATE TABLE IF NOT EXISTS `three_legacy` (`id` INTEGER PRIMARY KEY, "
                + "`legacy` TEXT varchar(255) NOT NULL UNIQUE, "
                + "`lord_uniqueId` TEXT varchar(255) NOT NULL UNIQUE, "
                + "`legacyLevel` INTEGER NOT NULL");
    }

    public static void executeSync(String query) {
        if(!hasConnected()) {
            connect();
        }
        try {
            conn.createStatement().execute(strip(query));
        } catch (Exception e) {
        }
    }

    public static boolean hasConnected() {
        try {
            return !conn.isClosed();
        } catch (Exception e) {
            return false;
        }
    }

    public static String strip(String str) {
        str = str.replaceAll("<[^>]*>", "");
        str = str.replace("\\", "\\\\");
        str = str.trim();
        return str;
    }

    public static ResultSet executeQuery(String query) {
        if(!hasConnected()) {
            connect();
        }
        ResultSet res = null;
        try {
            res = conn.createStatement().executeQuery(strip(query));
        } catch (Exception e) {
        }

        return res;
    }
насколько я смог понять, метод connect() нужен для подключения к БД, чтения данных или создания таблиц, если она пуста, остальные методы как бы воспомогательные, или есть среди них ненужные? Что мне сюда нужно еще добавить, для полноценной работы с БД, то есть, записи, перезаписи, сохранения и так далее? Или это, что я наваял вообще никуда не годится? Короче прошу советов и ссылок на обучающие материалы 🙂
 
Если я правильно понял - вызвать load()
вероятно я его не правильно вызывал, так как этот способ:
JavaScript:
    private SQLite sqlite;

    @Override
    public void onEnable() {   

        sqlite.load();
приводит к ошибке:

Код:
[17:27:40 ERROR]: Error occurred while enabling Mystery v0.4/1 (Is it up to date?)
java.lang.NullPointerException
        at net.wealth_mc.mystery.Mystery.onEnable(Mystery.java:106) ~[?:?]
        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:292) ~[server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:340) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:405) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.loadPlugin(CraftServer.java:361) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.enablePlugins(CraftServer.java:321) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.t(MinecraftServer.java:411) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.l(MinecraftServer.java:376) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.a(MinecraftServer.java:331) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.DedicatedServer.init(DedicatedServer.java:269) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.run(MinecraftServer.java:527) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at java.lang.Thread.run(Unknown Source) [?:1.8.0_77]
И 106-я строка, именно эта: sqlite.load();
 
вероятно я его не правильно вызывал, так как этот способ:
JavaScript:
    private SQLite sqlite;

    @Override
    public void onEnable() {  

        sqlite.load();
приводит к ошибке:

Код:
[17:27:40 ERROR]: Error occurred while enabling Mystery v0.4/1 (Is it up to date?)
java.lang.NullPointerException
        at net.wealth_mc.mystery.Mystery.onEnable(Mystery.java:106) ~[?:?]
        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:292) ~[server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:340) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:405) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.loadPlugin(CraftServer.java:361) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.enablePlugins(CraftServer.java:321) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.t(MinecraftServer.java:411) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.l(MinecraftServer.java:376) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.a(MinecraftServer.java:331) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.DedicatedServer.init(DedicatedServer.java:269) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.run(MinecraftServer.java:527) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at java.lang.Thread.run(Unknown Source) [?:1.8.0_77]
И 106-я строка, именно эта: sqlite.load();
Ты пишешь "sqlite.load(); ", а в jvm оно выглядит как "null.load();".
 
Ты пишешь "sqlite.load(); ", а в jvm оно выглядит как "null.load();".
а, блин, точно, забыл добавить sqlite = new SQLite(this); спасибо, с этим прошло, родило новые ошибки, походу что то я напартачил в самом SQL запросе, попробую сначала сам разобраться...
 
Спасибо. С ошибкой я разобрался.
Но, к сожалению с толкнулся с новой, решение к которой пока не могу найти. Суть в том, что когда база уже создана, но в ней еще нет записей, есть метод, который проверяет БД на наличие в ней определенной записи перед добавлением/обновлением. В результате получаю такую ошибку:
Код:
java.sql.SQLException: ResultSet closed
        at org.sqlite.RS.checkOpen(RS.java:63) ~[server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.sqlite.RS.markCol(RS.java:77) ~[server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.sqlite.RS.wasNull(RS.java:208) ~[server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.wealth_mc.mystery.db.MysterySQLite.getThisMysteryPlayerToNameDB(MysterySQLite.java:39) [Mystery.jar:?]
        at net.wealth_mc.mystery.Mystery.getConfigPlayerPrefix(Mystery.java:172) [Mystery.jar:?]
        at net.wealth_mc.mystery.Mystery.loadConfMystery(Mystery.java:128) [Mystery.jar:?]
        at net.wealth_mc.mystery.Mystery.onEnable(Mystery.java:83) [Mystery.jar:?]
        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:292) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:340) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:405) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.loadPlugin(CraftServer.java:361) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at org.bukkit.craftbukkit.v1_9_R1.CraftServer.enablePlugins(CraftServer.java:321) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.t(MinecraftServer.java:411) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.l(MinecraftServer.java:376) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.a(MinecraftServer.java:331) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.DedicatedServer.init(DedicatedServer.java:269) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at net.minecraft.server.v1_9_R1.MinecraftServer.run(MinecraftServer.java:527) [server11486.jar:git-Spigot-944aa20-8d16fc0]
        at java.lang.Thread.run(Unknown Source) [?:1.8.0_77]
вот этот кусок кода:
Java:
    public MysteryPlayer getThisMysteryPlayerToNameDB(String playername) {
        MysteryPlayer pl = null;
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        conn = getSQLConnection();
        try {
            ps = conn.prepareStatement("SELECT * FROM " + mystery_player_list
                    + " WHERE displayName = '" + playername.toLowerCase() + "';");
            rs = ps.executeQuery();

/*MysterySQLite.java:39*/pl = new MysteryPlayer(UUID.fromString(rs.getString("uniqueId")), rs.getString("displayName"),
                    Gender.valueOf(rs.getString("gender")), Legacy.valueOf(rs.getString("legacy")),
                    rs.getInt("level"), rs.getInt("timeLevel"), rs.getInt("killLevel"),
                    rs.getInt("killTheirLegacy"), rs.getInt("killAlienLegacy"), rs.getInt("killNotLegacy"));

        } catch (SQLException ex) {
            plugin.getLogger().log(Level.SEVERE, Errors.sqlConnectionExecute(), ex);
        } finally {
            try {
                if (ps != null)
                    ps.close();
                if (conn != null)
                    conn.close();
            } catch (SQLException ex) {
                plugin.getLogger().log(Level.SEVERE, Errors.sqlConnectionClose(), ex);
            }
        }
        return pl;
    }

я знаю, что ошибка из-за того, что запрос не может выполниться, так как в базе нет совпадений этому запросу, по сути в базе еще совсем нет записей, но как прервать метод, если строка не найдена?
Пробовал : if (rs == null) return pl; не помогает, вероятно переменная не совсем пустая, а такие варианты:
if (rs.wasNull()) return pl; или if (rs.first()) return pl; приводят к аналогичной ошибке на них-же, подскажите, как можно прервать выполнение этого метода, если искомая запись не найдена?
 
Код:
at net.wealth_mc.mystery.db.MysterySQLite.getThisMysteryPlayerToNameDB
   (MysterySQLite.java:39)
Это которая строка у тебя в коде выше? Тьфу, я слепой.
Твоя переменная rs является не той самой единственной строкой результата, которую ты ждёшь, а кортежем строк (коих может быть в количестве от 0 до N).
Нужно использовать rs.next(), и если оно даёт true, значит в rs загружена следующая непрочитанная строка. Вот, например, как я извлекаю все полученные строки. Правда, это MySQL, а не SQLite.

Гуру БД при желании могут поправить меня, ибо я далеко не pro.
 
Код:
at net.wealth_mc.mystery.db.MysterySQLite.getThisMysteryPlayerToNameDB
   (MysterySQLite.java:39)
Это которая строка у тебя в коде выше? Тьфу, я слепой.
Твоя переменная rs является не той самой единственной строкой результата, которую ты ждёшь, а кортежем строк (коих может быть в количестве от 0 до N).
Нужно использовать rs.next(), и если оно даёт true, значит в rs загружена следующая непрочитанная строка. Вот, например, как я извлекаю все полученные строки. Правда, это MySQL, а не SQLite.

Гуру БД при желании могут поправить меня, ибо я далеко не pro.
Ребят, читайте больше книг =)
Это же JDBC! Видов реляционных БД много, а стандарт один.(Для noSQL не подойдёт!)
 
Нужно использовать rs.next()
Спасибо большое, это решило мою проблему! Сделал так:
Java:
            while(rs.next()){
                if (rs.getString("displayName").equalsIgnoreCase(playername.toLowerCase())) {
                    pl = new MysteryPlayer(UUID.fromString(rs.getString("uniqueId")), rs.getString("displayName"),
                            Gender.valueOf(rs.getString("gender")), Legacy.valueOf(rs.getString("legacy")),
                            rs.getInt("level"), rs.getInt("timeLevel"), rs.getInt("killLevel"),
                            rs.getInt("killTheirLegacy"), rs.getInt("killAlienLegacy"),
                            rs.getInt("killNotLegacy"));
                }
            }

Еще хотел посоветоваться.

1. Как практичнее, работать напрямую с БД или считывать с базы инфу в массив и там уже обрабатывать?

2. Если работать напрямую с базой, то в отдельные потоки выводить абсолютно всю работу с ней или короткие запросы можно делать в основном, а запись и более сложные в дополнительном?

3. Если все делать в дополнительных потоках, то частые запросы к БД (хоть и не сложные, просто получение инфы), то есть будет создаваться много потоков (конечно в зависимости от онлайна) не создадут ли они большой нагрузки на сервер?
 
3. Если все делать в дополнительных потоках, то частые запросы к БД (хоть и не сложные, просто получение инфы), то есть будет создаваться много потоков (конечно в зависимости от онлайна) не создадут ли они большой нагрузки на сервер?
Иногда уходит больше времени на создание потока, чем на его выполнение.

Вообще лучше (имхо) вытащить всю инфу из бд в память и работать уже с памятью. И где то раз в 10 минут кидать в бд изменения.
 
Вызовы в БД — медленные. Если она удалённая, то особенно.
Всё, что работает медленно, обязано работать в параллельном потоке. Единственное исключение — можно быстренько что-то запросить при загрузке сервера в onLoad или onEnable (менее желательно).
Всё, что можно не вычислять, а хранить готовым локально — съест память, но ускорит работу.
Таковы основные принципы.
 
Создаешь пул (гугли как), после чего задачу, которую нужно выполнить асинхронно, выполняешь в пуле потоков, таким образом берется уже созданный поток, который стоит на паузе, в нем выполняется задача и он снова становится доступен для другой задачи.
 
так ведь SQLite, это локальная БД, чисто теоретически, должна бы работать быстрее, или на практике нет?
а, еще в дополнение, если у меня кое какие задачи выполняются по таймеру, ну то есть "шедулеры", они выполняются в каком потоке? (сори за нубские вопросы🫢)
 
так ведь SQLite, это локальная БД, чисто теоретически, должна бы работать быстрее, или на практике нет?
Это бд в файле, который нужно периодически сохранять/загружать. Некоторые драйверы из-за таймаута просто выгружают бд из памяти, из-за чего нужно бд снова прочитать и загрузить. Возможно, это неверное суждение, поскольку оно у меня работало более менее шустро (от 0 до 16 ms).
 
Назад
Верх