Russian Qt Forum
Ноябрь 05, 2024, 22:10 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: 4.3.2: подписка на сигналы о событиях от субд?  (Прочитано 18435 раз)
ритт
Гость
« : Ноябрь 04, 2007, 00:27 »

о подписке на сигналы о событиях от субд в кутэ4.4 уже было сказано в ветке http://prog.org.ru/forum/index.php/topic,6124.0.html. в блогах появилась заметка http://labs.trolltech.com/blogs/2007/11/02/asynchronous-database-event-notifications/, где говорится, что в снапшотах уже можно подписываться на такие нотификэйшены, используя постгре и птица

буду должен 5рэ или пиво (на выбор), если кто-нть найдёт время и хакнет эту фичу из снапшота под 4.3, т.к. у меня сейчас абсолютно нет свободного времени этим заниматься, а фича весьма полезная

плюс на будущее: как сделать такой нотификашен на мускуле? обязательно создавать ХП или можно как-то иначе/проще?
« Последнее редактирование: Ноябрь 04, 2007, 17:43 от xep » Записан
Dodge
Гость
« Ответ #1 : Ноябрь 04, 2007, 00:58 »

я конечно не датабайс-гуру, но суть вопроса не совсем понял... что конкретно нада?
Записан
Tonal
Гость
« Ответ #2 : Ноябрь 04, 2007, 09:43 »

Здесь как-то пробегал код по этому поводу для FB поищи. :-)

Да, на FB для запуска события тебе всяко или триггер или процедура нужна. Сам сервер ничего не шлёт. :-)
Другое дело, что там 1 строчка. А вот если сервер этого не поддерживает, то всю коммуникацию по сети придётся устраивать самому.
Поэтому на том же мускуле проще опрашивать по таймеру...
Записан
ритт
Гость
« Ответ #3 : Ноябрь 04, 2007, 11:54 »

если ты про "опрашивать по таймеру", то я имею представление о какой ветке идёт речь (поискать).
непреемлемо, т.к. база предполагает быть весьма объёмной - вариант с таймером буду использовать в самом крайнем случае

ну, допустим, сделал я процедуру, выполняющуюся при добавлении строки в таблицу, что и куда процедура возвращать должна, чтобы клиент на другом конце получил сигнал?
раньше не приходилось с этим сталкиваться Грустный
Записан
SerjVarshavskiy
Гость
« Ответ #4 : Ноябрь 04, 2007, 14:00 »

а зачем тебе опрашивать большую табл по таймеру?

делай процедуру и пускай она выставляет флаг в маленькой табл с твоими
всяческистранными флагами

вот как раз эту мини табл и опрашивай

в итоге минимум напряг на базу и сервер
Записан
ритт
Гость
« Ответ #5 : Ноябрь 04, 2007, 14:29 »

итого: что в маленькой всяческистранной таблице?
Записан
SerjVarshavskiy
Гость
« Ответ #6 : Ноябрь 04, 2007, 14:34 »

ну типа каждое поле это флаг
каждый флаг(поле) выставл в 1(труе) из тригеров в базе при каком-либо событии и тп
или в др значение

опрашиваешь эту табл по таймеру, если флаг поднят или больше-меньше какого-либо значения - делаешь
свои дела
Записан
baka
Гость
« Ответ #7 : Ноябрь 04, 2007, 15:20 »

недели три назад я с такой проблемой столкнулся.


сделал по простому

добавил в БД таблицу в которой регистрируется каждое клиентское приложение
при подключении к БД. ( оно там оставляет свой ip, порт и т.д.).

при изменении к.либо данных каждый клиент оповещает других через TCP - сокеты
(о существовании других он узнает из БД).

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

реализовал пока топорно, еще буду переделывать, вот исходники :

.h
Код:
class Master : public QObject
{
// Обьект передающий уведомление о том что к.либо таблица изменилась

Q_OBJECT

public :
static Master * getObject();

public slots :
void tableChange(const QString & );
// этот слот связан со всеми  виджетами с помощью которых пользователь может менять данные
// он будет вызыватся каждый раз когда к.либо обьект проги изменил данные.

void beforeQuit();
void acceptConnection();

signals :
void RefreshTable(const QString & );
// сигнал рассылается всем виджетам
// в качестве параметра передается имя таблицы которая была изменена.

private :
Master(QObject * parent = 0);
static Master * pointer;
sendThread * sThread;
QTcpServer * tcpServer;
quint16 port;
QHostAddress hAdr;

};
class sendThread : public QThread
{
// Поток в котором будем рассылать уведомления другим клиентским прогам.

Q_OBJECT

public :
sendThread(QObject * parent = 0);
void start(const QString &);
void setAddress(QHostAddress & ,quint16 port);

protected:
void run();

private :
int q;
QString tName;
QSqlDatabase db;
QHostAddress hAdr;
quint16 port;
};


.cpp
Код:

Master * Master::pointer = 0;

Master::Master(QObject * parent)
:QObject(parent)
{
QSqlDatabase db = G::getDB();

hAdr = QHostInfo::fromName(QHostInfo::localHostName()).addresses().last();
// создаем сервер
tcpServer = new QTcpServer(this);
tcpServer->listen(hAdr);

port = tcpServer->serverPort();

// ДОБАВЛЯЕМ СЕБЯ В ip_table .
QSqlQuery query(G::getDB());
while(true)
{
if(!query.exec("SELECT max(`key`) FROM ip_table;"))
{
QMessageBox::information(0,"111",query.lastError().text(),QMessageBox::Yes);
};
query.next();
int key = query.value(0).toInt();
key++;

QString insert = "INSERT INTO ip_table VALUES("+QString::number(key)
+",'"+hAdr.toString()+"',"+QString::number(port)+",0);";
if(query.exec(insert)) break;
};

// Создаем sendThread
sThread = new sendThread(this);
sThread->setAddress(hAdr,port);

QObject::connect(tcpServer,SIGNAL(newConnection()),this, SLOT(acceptConnection()));
QObject::connect(QApplication::instance(),SIGNAL(aboutToQuit()),this,SLOT(beforeQuit()));
};

void Master::beforeQuit()// Слот связан с QCoreApplication::aboutToQuit ()   [signal]
{// Удаляем из ip_table данные о себе.
QString q("DELETE FROM ip_table WHERE ip='"+hAdr.toString()+"' AND port="+QString::number(port)+";");
QSqlQuery query(G::getDB());
if(!query.exec(q))
{
QMessageBox::information(0,QString("Удаление из ip_table не произошло.")
,query.lastError().text()+"\n"+q,QMessageBox::Yes);
};
// Если данные о себе не удалим перед выходом то ничего страшного они будут удалены другой клиентской программой. ( потом).
};


void Master::acceptConnection()
{
QTcpSocket * incomeConnection = tcpServer->nextPendingConnection();

QString str;
QDataStream in(incomeConnection);
in.setVersion(QDataStream::Qt_4_0);

while (incomeConnection->bytesAvailable() < (int)sizeof(quint16))
{
if (!incomeConnection->waitForReadyRead(6660))
{
return;
            };
};

quint16 size;
in>>size;
while(incomeConnection->bytesAvailable()<size)
{
if(!incomeConnection->waitForReadyRead(6660))
{
return;
};
};
in>>str;
emit RefreshTable(str);
};

void Master::tableChange(const QString & tName)
{
emit RefreshTable(tName); // все виджеты в данном экземпляре проги обновятся.
// и еще рассылаем всем другим прогам сообщение.
sThread->start(tName);
//G::debug("tableChange -"+tName);
};

Master * Master::getObject()
{
if(!pointer)
{
pointer = new Master(QApplication::instance());
};
return pointer;
};

sendThread::sendThread(QObject * parent)
:QThread(parent)
,port(0)
{
db=G::getDB();
};
void sendThread::setAddress(QHostAddress & adr,quint16 q)
{
hAdr = adr;
port = q;
};

void sendThread::start(const QString & tn)
{
if(hAdr.isNull())
{
QMessageBox::information(0,"Alarm",QString::fromLocal8Bit("Не инициализировали"),QMessageBox::Ok); 
return;
};
if(!port)
{
return ;
};

tName=tn;
QThread::start();
};

void sendThread::run()
{

QString q("SELECT `key`,`ip`,`port`,`error_count` FROM ip_table WHERE NOT (ip='"+hAdr.toString()+"' AND port="+QString::number(port)+");");
QSqlQuery query(db);
if(!query.exec(q))// выбираем из БД адреса и порты других приложений.
{
QMessageBox::information(0,"QueryError",query.lastError().text(),QMessageBox::Yes);
return;
};

QTcpSocket * tcpSocket = new QTcpSocket(this);
while(query.next())
{
int temp_key = query.value(0).toInt();
QString temp_ip = query.value(1).toString();
int temp_port = query.value(2).toInt();
tcpSocket->connectToHost(QHostAddress(temp_ip),temp_port,QIODevice::WriteOnly);


if(!tcpSocket->waitForConnected(666))
{
QSqlQuery del_q(db); // Если не дозвонились до к.либо клиента то увеличиваем счетчик ошибок иесли их больше трех удаляем из БД.
QString update("UPDATE ip_table SET error_count=error_count+1 WHERE ip= '"
+temp_ip+"' AND port="+QString::number(temp_port)+";");
if(!del_q.exec(update))
{
QMessageBox::information(0,"",del_q.lastError().text(),QMessageBox::Yes);
};
//QMessageBox::information(0,"QueryError","I am in (!tcpSocket->waitForConnected())",QMessageBox::Yes);
QString del("DELETE FROM ip_table WHERE error_count>3");
del_q.exec(del);
}
else
{
//отправляем имя таблицы.
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << tName;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));

tcpSocket->write(block);
tcpSocket->disconnectFromHost();
};
tcpSocket->waitForDisconnected(666);
};
};


чтобы использовать такой механизм
каждый виджет работающий с БД должен содержать
сигнал для оповещения Master'а о том что таблица с таким-то именем была изменена
и слот принимающий имя таблицы которую надо переSELECT'ить
Код:
		
public slots:
void slt_refresh(const QString&);

signals :
void iChangeTable(const QString & );
Код:
		

void eMAbstractGrid::slt_refresh(const QString& tName)
{
if(tName!=this->tName) return;
setQuery(strQuery);
};
   
         
« Последнее редактирование: Ноябрь 04, 2007, 15:22 от baka » Записан
Вячеслав
Гость
« Ответ #8 : Ноябрь 04, 2007, 15:26 »

Мдя Подмигивающий Перефразируя "чего только народ н придумает ,шоб трехзвенку не юзать "Подмигивающий А почему-бы не замутить app-server и не цепляться к нему ? Подмигивающий Хотя все зависит от масшабов задачи - если клиентов пара-тройка - то и тригеры\event'ы сойдут, а если пара десятков да еще с hot-репликацией бд - то ой ....
Записан
ритт
Гость
« Ответ #9 : Ноябрь 04, 2007, 15:27 »

спасибо. вполне развёрнуто.
только в моём случае, к сожалению, данный вариант неприменим по ряду причин Грустный
Записан
Tonal
Гость
« Ответ #10 : Ноябрь 04, 2007, 18:54 »

Цитировать
Вот  мой унитаз, вот так я на нём сижу, дайте мне таки мою туалетную бумагу! (c) рус.нар.анекдот.
Решение для FB:
Для таблицы, для которой нужно получать извещение о том, что данные в ней добавились/изменились/удалились, создаём триггер:
Код:
CREATE OR ALTER trigger PHYS_PERSONS_MODIFY for PHYS_PERSONS
active after insert or update or delete position 0
AS
begin
  post_event 'PHYS_PERSONS_MODIFY';
end
На клиенте ждём в потоке прихода события.
По получению выясняем что произошло, и корректируем представления.
Для трёхзвенки так же.
Всё.
Записан
ритт
Гость
« Ответ #11 : Ноябрь 04, 2007, 19:06 »

вооот. популярно и доходчиво!
тонал, +1

а для мускуля, говорите, никак? надо бы костыль придумать...
для трёзвенки реализация очевидна...с клиент-сервер сложнее
пока что из мыслей только отправка предопределённого мессага по тцп/ип - надо доки курить
у кого ещё какие соображения?
Записан
WW
Гость
« Ответ #12 : Ноябрь 06, 2007, 00:33 »

Тут как-то приводились модифицированные исходники qt-драйвера для PostreSQL. Поищи. С утра гляну на работе. Реализуют нотификацию.
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #13 : Май 08, 2008, 23:06 »

Решение для FB:
...
На клиенте ждём в потоке прихода события.
Tonal, объясни с этого места пожалуйста, можешь пример кода привести?
Записан

Юра.
Tonal
Гость
« Ответ #14 : Май 10, 2008, 13:27 »

А что объяснять то? Поток, ожидание, корректировку?
Поток - QThread
Ожидание - смотри доки FB/IB по работе с api событий.
Корректировка моделей - зависит от твоих моделей. Улыбающийся

Да, вроде Троли обещали события базы поддержать для PG/FB/IB - смотри, может в 4.4 уже всё есть. Улыбающийся
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.05 секунд. Запросов: 23.