LINUX.ORG.RU

PostgreSQL+JDBC: AUTO_INCREMENT - все плохо.

 , , , ,


0

1

Здравствуй, интеллектуальный островок рунета. Уже с месяц тыкаю базы данных на примере PostgreSQL, но так и не понял что там у него с инкрементируемыми полями. Я разобрался с псевдо-типами {big}serial, понял что юзать счетчики надо, и при обращении к бд SQL запросом проблем никаких.
Но моя программа на джаве (точнее, модель JTable к-я отображает выборку таблицы) общается с БД не SQL запросом, а через CachedRowSet . И тут либо я туплю, либо JDBC, ибо при добавлении row со значение id=null (пусть, к id у меня привязан счетчик) вылетает эксепшен

javax.sql.rowset.spi.SyncProviderException: N conflicts while synchronizing 
Далее при любом обращении к бд вылетает такое
javax.sql.rowset.spi.SyncProviderException: ERROR: current transaction is aborted, commands ignored until end of transaction block
Гугл сказал про Connection.rollback(), но он особо не помог (SyncProviderException перестало кидать, но конфликты синхровизации остались).
Куда копать? А то очень обидный эксепшен.

//PS и да, а получается что число таких полей ограничено всего лишь BIGINTEGER? Маловато же...

★★★★

Смысла в auto_increment честно гря я не вижу. Всегда гораздо удобнее получить id через SELECT nextval(seq_table_id) и потом его использовать при создании записи и связывании с другими.

_Возможно_ в твоём случае надо выключить AutoCommit, вставить запись, получить её id через SELECT last_value FROM seq_table_id и чего-нить обновить этим id, а потом ужо закоммитать.

Размер bigint посмотри. за всю жизнь столько не создашь.

vtVitus ★★★★★
()
Ответ на: комментарий от vtVitus

_Возможно_ в твоём случае надо выключить AutoCommit, вставить запись, получить её id через SELECT last_value FROM seq_table_id и чего-нить обновить этим id, а потом ужо закоммитать.

Хмм, т.е. проверть на null id и если true то через запрос получить след. id из счетчика, присвоить его и уже полную строку коммитеть? Спасибо, честно говоря я об этом не думал

Размер bigint посмотри. за всю жизнь столько не создашь.

Я если учесть что один счетчик можно использовать для нескольких таблиц, и при удалении строк счетчик не пересчитывается, а продолжает инкрементить старое значение ? Не так много. У меня только тест а уже id за 500 залез :) Тч 9*10^18 строек для базы, в которую допустим каждую сек. добавляется запись в одну из некоторого кол-ва таблиц с одним счетчиком в течении нескольких лет вполне возможно превысить предел.

comp00 ★★★★
() автор топика
Последнее исправление: comp00 (всего исправлений: 1)

вы передаете id=null в INSERT'е ? попробуйте не передавать его вообще.

ЗЫ: в JDBC не силен, но полагаю там все равно SQL запрос передается адаптеру в виде строки.

//PS и да, а получается что число таких полей ограничено всего лишь BIGINTEGER? Маловато же...

Вам bigint мало ??? Вы не сможете заполнить таблицу так, чтобы переполнился индекс.

codeogre
()
Ответ на: комментарий от comp00

Если вы будете писать по одной записи в секунду, то счетчик переполнится через 2^63/(60*60*24*(365+0.25-0.01+0.0025)) = 292277024626 лет по Григорианскому календарю.

Даже если будете писать по миллиону записей в секунду, то все равно не доживете.

codeogre
()
Ответ на: комментарий от codeogre

вы передаете id=null в INSERT'е ? попробуйте не передавать его вообще.

Я вроде писал

при обращении к бд SQL запросом проблем никаких.Но моя программа на джаве (точнее, модель JTable к-я отображает выборку таблицы) общается с БД не SQL запросом, а через CachedRowSet

Т.е. не использую INSERT напрямую, за меня это делает jdbc.

ЗЫ: в JDBC не силен, но полагаю там все равно SQL запрос передается адаптеру в виде строки.

в данном случае нет :) Иначе бы сильно осложнилась реализация модели таблицы

Вам bigint мало ??? Вы не сможете заполнить таблицу так, чтобы переполнился индекс.

Я просто спросил. Все что имеет конец, когда-то заканчивается. Хотя, я подозреваю что в mysql тоже конечное число полей с авто-инкрементом, просто оно очень большое.

comp00 ★★★★
() автор топика
Ответ на: комментарий от codeogre

Даже если будете писать по миллиону записей в секунду, то все равно не доживете.

Это не важно. Базы разные бывают, наверняка есть такие, которые должны жить куда больше, чем живут люди.

comp00 ★★★★
() автор топика
Ответ на: комментарий от TERRANZ

Довольно интересная вещица, но все же хотелось бы разобраться в jdbc, раз уж начал ( да и проект переписывать лень).
Кстати, JPA2 поддерживает создавать таблицу из поля класса в виде массива/коллекции? Или только из отдельных полей? Тогда не очень понятно как динамически создавать пользовательские таблицы( ведь java не позволяет динамически создавать классы и изменять их поля).

comp00 ★★★★
() автор топика

Не проще ли создать хранимую процедуру на PL/pgSQL для добавления данных в таблицу, которая будет возвращать id добавленной строки.

looper
()
Ответ на: комментарий от looper

хранимую процедуру на PL/pgSQL для добавления данных в таблицу, которая будет возвращать id добавленной строки.

Нафига чего-то создавать, если это просто INSERT .. RETURNING id; Впрочем, это к вопросу автора вообще не имеет отношения.

unC0Rr ★★★★★
()

при добавлении row со значение id=null (пусть, к id у меня привязан счетчик) вылетает эксепшен

По идее или вообще не указывать поле id при вставке данных, либо выставлять ему значение id=DEFAULT, тогда будет работать.

unC0Rr ★★★★★
()
Ответ на: комментарий от looper

Не проще ли создать хранимую процедуру на PL/pgSQL для добавления данных в таблицу, которая будет возвращать id добавленной строки.

Вы про RETURNING? Если я не ошибаюсь, придется добавлять через запрос. А через запрос у меня и так получается, у меня проблема с добавлением через интерфейсы jdbc

comp00 ★★★★
() автор топика
Ответ на: комментарий от unC0Rr

либо выставлять ему значение id=DEFAULT, тогда будет работать.

Да работает через INSERT. В общем, я вставляю так, что бы было более ясно:

          tmpModel.getCachRS().moveToInsertRow();
            for (int i = 0; i < newRecFrame.getTblModel().getColumnCount(); i++) {
                Object obj;
                obj = newRecFrame.getTblModel().getValueAt(0, i);
                tmpModel.getCachRS().updateObject(i + 1, obj);
            }

            tmpModel.getCachRS().insertRow();
            tmpModel.getCachRS().moveToCurrentRow();
            tmpModel.getCachRS().beforeFirst();
            CurrentBase.getBase().newConnect(CurrentBase.getBase().getConnect());
             CurrentBase.getBase().getConnect().setAutoCommit(false);
            tmpModel.getCachRS().acceptChanges(CurrentBase.getBase().getConnect()); //Тут эксепшен javax.sql.rowset.spi.SyncProviderException: conflicts while synchronizing
            tmpModel.getCachRS().commit();
            CurrentBase.getBase().getConnect().setAutoCommit(true);
Как видите, я напрямую нигде не использую запрос, за меня это должен делать jdbc. Но если на NULL полях obj=null (3я строка) не вызывает ошибок, то для ID летит эксепшен во время accept.

comp00 ★★★★
() автор топика
Последнее исправление: comp00 (всего исправлений: 1)
Ответ на: комментарий от unC0Rr

Я кстати пробовал отключать автокоммиты, добавлять строку без id, получать ее значение счетчика, обновлять, и только после этого коммитеть. Не помогло.

comp00 ★★★★
() автор топика
Ответ на: комментарий от comp00

JDBC составлением запросов за тебя не занимается. Это фактически, драйвер для БД. Этим занимается твой ORM. Вот в нем и косяк. Либо в том, что в примере не видно инициализации полей вставленной строчки.

vromanov ★★
()
Ответ на: комментарий от comp00

Это-то понятно, но может быть есть какая-то возможность сказать, что id - не обычное поле? В Qt, к примеру, используется QSqlField::setGenerated(). Если ничего такого нет, то можно сделать вьюху, которая будет в insert подменять id на default, но это уже костыль.

unC0Rr ★★★★★
()
Ответ на: комментарий от unC0Rr

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

looper
()
Ответ на: комментарий от vromanov

Либо в том, что в примере не видно инициализации полей вставленной строчки.

Я даже так просто код не приведу : Тут довольно запутанно.
В общем, ввод данных осуществляется через диалоговое окно newRecFrame есть таблица с newTblModel. Эта newTblModel использует колонки от TableModel основной таблицы, с которой пользователь собственно и работает. Это сделано для того, что бы не дублировать запрос, тем самым не замедлять скорость работы программы.
Так как модель таблицы вводных данных представляет собой просто копию от основной таблицы ( как-то так) , по сути инициализация полей проходит в модели основной таблицы, следующим кодом: http://pastebin.com/KzG61vRY

comp00 ★★★★
() автор топика
Ответ на: комментарий от looper

которая будет сама генерить значение для ключей

делать ключи джавой, а не PSQL ? Ну это костылище же

comp00 ★★★★
() автор топика
Ответ на: комментарий от comp00

Под хранимой процедурой я имел ввиду процедуру написанную на plpgsql и которая хранится в БД, к jave она не имеет никакого отношения.

looper
()
Ответ на: комментарий от looper

Под хранимой процедурой я имел ввиду процедуру написанную на plpgsql и которая хранится в БД

Хм, не знал о таком

comp00 ★★★★
() автор топика
Ответ на: комментарий от looper

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

Парад фриков продолжается.

anonymous
()
Ответ на: комментарий от anonymous

JDBC тут и не пахнет

да ладно, убрал его импорты — не скомпилялось (

RTFM.

Ты будешь наверно удивлен, но это я вычитал в оф. доках по java, а именно тут http://docs.oracle.com/javase/tutorial/jdbc/basics/jdbcswing.html

comp00 ★★★★
() автор топика
Ответ на: комментарий от comp00

По ссылке сраный туториал, а не FM. Ты, походу, вообще не напрягаешь мозг. Увы, кодинг копипастом очень редко бывает удачным.

anonymous
()
Ответ на: комментарий от anonymous

По ссылке сраный туториал, а не FM

ну дай линк на FM, раз знаешь

кодинг копипастом
копипастом

анонимусы, такие анонимусы.

comp00 ★★★★
() автор топика
Ответ на: комментарий от comp00

вот разберусь

Нет, не разберешься. Ошибки в этом коде нельзя исправить, разве что в потомках.

anonymous
()
Ответ на: комментарий от comp00
...
tmpModel.getCachRS().beforeFirst();
...

емнип, у jdbc драйвера постгреса не так давно были проблемы с beforeFirst. Если пробовать просто first, чего будет?

arkhnchul ★★
()
Ответ на: комментарий от arkhnchul

емнип, у jdbc драйвера постгреса не так давно были проблемы с beforeFirst. Если пробовать просто first, чего будет?

java: cannot find symbol
  symbol:   method First()
  location: variable crs of type javax.sql.rowset.CachedRowSet
comp00 ★★★★
() автор топика
Ответ на: комментарий от anonymous

умник, что-то ты мне надоел. Не хочешь прогуляться по соседним тредам?

comp00 ★★★★
() автор топика
Ответ на: комментарий от comp00

Как видите, я напрямую нигде не использую запрос, за меня это должен делать jdbc. Но если на NULL полях obj=null (3я строка) не вызывает ошибок, то для ID летит эксепшен во время accept.

exception я полагаю из-за того, что драйвер пытается вставить null значение в столбец, который определен(явно или нет) в таблице как not null, а база этого естественно не дает сделать. Попробуйте сделать BEFORE INSERT триггер в базе, который бы инициализировал нужным образом поле если оно установлено в null.

codeogre
()
Ответ на: комментарий от codeogre

То что доктор прописал, спасибо

comp00 ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.