<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>CTAPbIu_MABP&#039;s BLOG &#187; SQL</title>
	<atom:link href="http://mabp.kiev.ua/category/programming/sql/feed/" rel="self" type="application/rss+xml" />
	<link>http://mabp.kiev.ua</link>
	<description>Не вижу проблем, кроме лени! &#169; Старый Мавр</description>
	<lastBuildDate>Tue, 07 Sep 2010 20:05:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Cascading comboboxes on GWT, ExtGWT, ExtJS и MySQL</title>
		<link>http://mabp.kiev.ua/2009/06/12/cascading-comboboxes-on-gwt-extgwt-extjs-and-mysql/</link>
		<comments>http://mabp.kiev.ua/2009/06/12/cascading-comboboxes-on-gwt-extgwt-extjs-and-mysql/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 12:27:44 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JAVA]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[combobox]]></category>
		<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[GWT]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1078</guid>
		<description><![CDATA[Я уже давно грозился выложить свежую версию каскадных выпадающих списков на GWT 1.5.3, ExtGWT 1.2, ExtJS 2.2.1 и MySQL 5.1. Код был написан еще в конце апреля, но меня все ломало написать к нему текстовку. А сейчас когда на подходе выход ExtGWT 2.0 и ExtJS 3.0 я решил пошевелиться и накрапать небольшое описалово. Действительно небольшое [...]]]></description>
			<content:encoded><![CDATA[<p>Я уже давно грозился выложить свежую версию каскадных выпадающих списков на GWT 1.5.3, ExtGWT 1.2, ExtJS 2.2.1 и MySQL 5.1. Код был написан еще в конце апреля, но меня все ломало написать к нему текстовку. А сейчас когда на подходе выход ExtGWT 2.0 и ExtJS 3.0 я решил пошевелиться и накрапать небольшое описалово. Действительно небольшое потому что все настройки я уже описал в <a href="http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/">предыдущей статье</a>, так что тут будут описаны только изменения.</p>
<span id="more-1078">
</span>
<p>Для начала опишу изменения в БД, я ее полностью поменял. Теперь у нас вместо машинок будут города и страны, но смысл тот же.</p>
<pre>
<code class="sql">

DROP TABLE IF EXISTS `city`;
CREATE TABLE IF NOT EXISTS `city` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `parent` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=50 ;


INSERT INTO `city` (`id`, `name`, `parent`) VALUES
(1, 'Kiev', 1),
(2, 'Lviv', 1),
(3, 'Odesa', 1),
(4, 'Kharkov', 1),
(5, 'Lugansk', 1),
(6, 'Zaporizhzhia', 1),
(7, 'Mariupol', 1),
(8, 'Moskow', 2),
(9, 'Volgograd', 2),
(10, 'Samara', 2),
(11, 'Kazan', 2),
(12, 'Perm', 2),
(13, 'Eburg', 2),
(14, 'Chelyabinsk', 2),
(15, 'Minsk', 3),
(16, 'Homel', 3),
(17, 'Grodno', 3),
(18, 'Vitebsk', 3),
(19, 'Le Havre', 4),
(20, 'Rennes', 4),
(21, 'Nantes', 4),
(22, 'Berdeaux', 4),
(23, 'Toulouse', 4),
(24, 'Montpellier', 4),
(25, 'Marseille', 4),
(26, 'Lyon', 4),
(27, 'Paris', 4),
(28, 'Reims am Main', 5),
(29, 'Berlin', 5),
(30, 'Bremen', 5),
(31, 'Hannover', 5),
(32, 'Hamburg', 5),
(33, 'Frankfurt', 5),
(34, 'Munchen', 5),
(35, 'Strasbourg', 5),
(36, 'Milano', 6),
(37, 'Bologna', 6),
(38, 'Genova', 6),
(39, 'Roma', 6),
(40, 'Napoli', 6),
(41, 'Palermo', 6),
(42, 'London', 7),
(43, 'Bristol', 7),
(44, 'Birmingham', 7),
(45, 'Liverpool', 7),
(46, 'Manchester', 7),
(47, 'Edinburg', 7),
(48, 'Glasgow', 7),
(49, 'Belfast', 7);


DROP TABLE IF EXISTS `country`;
CREATE TABLE IF NOT EXISTS `country` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;


INSERT INTO `country` (`id`, `name`) VALUES
(1, 'Ukrain'),
(2, 'Russia'),
(3, 'Belarus'),
(4, 'France'),
(5, 'German'),
(6, 'Italia'),
(7, 'Uniited Kingdom');

</code>
</pre>
<p>И EntryPoint у нас на этот раз будет по проще. В нем не будет кучи виджетов, а только один.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client;


import com.extjs.gxt.ui.client.Events;
import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Viewport;
import com.extjs.gxt.ui.client.widget.form.FormPanel;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.layout.FlowLayout;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import ua.kiev.mabp.client.utility.CityToCountryUtility;
import ua.kiev.mabp.client.widget.CitySuggestBox;
import ua.kiev.mabp.client.widget.CountrySuggestBox;


public class HelloWorld implements EntryPoint {


    public void onModuleLoad() {


        // создаем рабочую область
        ContentPanel contentPanel = new ContentPanel();
        contentPanel.setLayout(new FitLayout());
        contentPanel.setHeading("Cascading combobox");


        // и новый комбобокс для стран
        CountrySuggestBox countrySuggestBox = new CountrySuggestBox();
        countrySuggestBox.setDisplayField("name");
        countrySuggestBox.setFieldLabel("Country");


        // вот тут вся магия
        CityToCountryUtility.getInstance().setCountryComboBox(countrySuggestBox);


        // ну и листенер конечно
        countrySuggestBox.addListener(Events.SelectionChange, new Listener&lt;BaseEvent&gt;(){
            public void handleEvent(BaseEvent be) {
                CityToCountryUtility.getInstance().eraseCity();
            }
        });


        // новый комбобокс для городов
        CitySuggestBox citySuggestBox = new CitySuggestBox();
        citySuggestBox.setDisplayField("name");
        citySuggestBox.setFieldLabel("City");
        citySuggestBox.setForceSelection(false);


        // еще немного магии
        CityToCountryUtility.getInstance().setCityComboBox(citySuggestBox);


        // дальше манипуляции с отображением 
        FormPanel formPanel = new FormPanel();
        formPanel.setHeaderVisible(false);
        formPanel.add(countrySuggestBox);
        formPanel.add(citySuggestBox);


        contentPanel.add(formPanel);


        Viewport viewport = new Viewport();
        viewport.setLayout(new FlowLayout());
        viewport.setScrollMode(Scroll.AUTO);
        viewport.add(contentPanel);
        RootPanel.get().add(viewport);
    }
}

</code>
</pre>
<p>Тут интересны два, точнее три объекта, CountrySuggestBox, CitySuggestBox и CityToCountryUtility, но два практически одинаковые, поэтому я буду рассказывать только про больший.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.widget;


import com.extjs.gxt.ui.client.data.BasePagingLoadConfig;
import com.extjs.gxt.ui.client.data.BasePagingLoadResult;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.RpcProxy;
import ua.kiev.mabp.client.proxy.CitySuggestBoxRpcProxy;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 16.04.2009
 * Time: 22:46:56
 */
public class CitySuggestBox&lt;D extends ModelData&gt; extends AbstractSuggestBox&lt;D&gt; {


    @Override
    protected RpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt; getRpcProxy() {
        return new CitySuggestBoxRpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt;();
    }
}

</code>
</pre>
<p>CitySuggestBox и CountrySuggestBox всеголишь обертки, поэтому показываю AbstractSuggestBox.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.widget;


import com.extjs.gxt.ui.client.data.*;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.form.ComboBox;
import com.google.gwt.user.client.Element;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 16.04.2009
 * Time: 22:46:09
 */


public abstract class AbstractSuggestBox&lt;D extends ModelData&gt; extends ComboBox&lt;D&gt; {


    public AbstractSuggestBox() {
        RpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt; proxy = getRpcProxy();
        PagingLoader&lt;BasePagingLoadConfig&gt; bpl = new BasePagingLoader&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt;(proxy);
        ListStore&lt;D&gt; store = new ListStore&lt;D&gt;(bpl);
        setStore(store);
        setTypeAhead(true);
        setForceSelection(true);
        setPageSize(5);
        setWidth("500px");
    }


    protected abstract RpcProxy&lt;BasePagingLoadConfig, BasePagingLoadResult&lt;D&gt;&gt; getRpcProxy();


    @Override
    protected void onRender(Element parent, int index) {
        setEditable(true);
        setTriggerAction(TriggerAction.QUERY);
        super.onRender(parent, index);
    }
}

</code>
</pre>
<p>Тут тоже практически ничего интересного - главное получить правильно параметризированый RpcProxy. Дальше дело техники. Прокси на самом деле тоже только обертка.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.proxy;


import com.extjs.gxt.ui.client.data.BasePagingLoadConfig;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.widget.Info;
import com.google.gwt.user.client.rpc.AsyncCallback;
import ua.kiev.mabp.client.HelloWorldService;
import ua.kiev.mabp.client.HelloWorldServiceAsync;
import ua.kiev.mabp.client.utility.CityToCountryUtility;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 16.04.2009
 * Time: 22:51:48
 */
public class CitySuggestBoxRpcProxy&lt;C extends BasePagingLoadConfig, D&gt; extends RpcProxy&lt;C, D&gt; {


    final HelloWorldServiceAsync service = HelloWorldService.App.getInstance();


    @Override
    protected final void load(C loadConfig, final AsyncCallback&lt;D&gt; asyncCallback) {
        String parernt = CityToCountryUtility.getInstance().getParentForCity();
        loadConfig.getParams().put("parent", parernt);
        service.getCity(loadConfig, new AsyncCallback&lt;D&gt;() {
            public void onSuccess(D result) {
                Info.display("Success", "success");
                asyncCallback.onSuccess(result);
            }


            public void onFailure(Throwable caught) {
                Info.display("Failure", "fail");
                asyncCallback.onFailure(caught);
            }


        });
    }
}

</code>
</pre>
<p>Но только эта обертка для городов имеет две лишних строки (по сравнению с оберткой для стран) в которых происходит получение выбранной страны и подставления этого значения в параметры запроса. Для этого я использую класс CityToCountryUtility, который реализует паттерн Registry.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.utility;


import com.extjs.gxt.ui.client.data.ModelData;
import ua.kiev.mabp.client.widget.CitySuggestBox;
import ua.kiev.mabp.client.widget.CountrySuggestBox;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 28.04.2009
 * Time: 15:20:29
 */
public class CityToCountryUtility {


    private static CityToCountryUtility manager;
    private CountrySuggestBox country;
    private CitySuggestBox city;


    private CityToCountryUtility() {


    }


    public static CityToCountryUtility getInstance() {
        if (manager == null) {
            manager = new CityToCountryUtility();
        }
        return manager;
    }


    public void setCountryComboBox(CountrySuggestBox country) {
        this.country = country;
    }


    public void eraseCity() {
        //city.clearSelections();
        city.getStore().removeAll();
    }


    public void setCityComboBox(CitySuggestBox city) {
        this.city = city;
    }


    public String getParentForCity() {
        ModelData value = country.getValue();
        return value != null ? (String) value.get("abbr") : "";
    }
}

</code>
</pre>
<p>Пожалуй это все что надо для того, чтобы сделать каскадные комбобоксы. Повторюсь что как настроить мавен, какие либы подключать и как замапить комбобокс к базе данных описано в <a href="http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/">предыдущей статье</a>. Осталось только прикрепить <a href="http://mabp.kiev.ua/content/source/cascading_combobox.rar">проект</a>.</p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/06/12/cascading-comboboxes-on-gwt-extgwt-extjs-and-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GWT+EXTJS подключаем MySQL</title>
		<link>http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/</link>
		<comments>http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/#comments</comments>
		<pubDate>Sun, 12 Apr 2009 14:22:40 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JAVA]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[combobox]]></category>
		<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[GWT]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1051</guid>
		<description><![CDATA[Очень долго, чуть больше недели готовил эту статью, начал с того что написал по статье об установке каждого используемого компонента, а теперь не знаю с чего начать :)


Начну с описания того, что буду делать. Я попробую создать тонкий клиент используя GWT и GWT-Ext. Кто уже на этом месте начал кричать "GWT-Ext устарела надо использовать GXT" [...]]]></description>
			<content:encoded><![CDATA[<p>Очень долго, чуть больше недели готовил эту статью, начал с того что написал по статье об установке каждого используемого компонента, а теперь не знаю с чего начать :)</p>
<span id="more-1051">
</span>
<p>Начну с описания того, что буду делать. Я попробую создать тонкий клиент используя GWT и GWT-Ext. Кто уже на этом месте начал кричать "GWT-Ext устарела надо использовать GXT" может смело закрывать вкладку браузера [x]. Остальные если не знают что такое и как установить <a href="http://mabp.kiev.ua/2009/04/09/install-intellij-idea/">IntelliJ IDEA</a> и <a href="http://mabp.kiev.ua/2009/04/09/install-maven-2/">Maven</a> отвлекаются от статьи на 20 минут на прочтение инструкций и установку софта. Инструкция об установке <a href="http://mabp.kiev.ua/2009/04/10/install-google-web-toolkit/">Google Web Toolkit</a> тоже имеется, а вот по установке MySQL мне было лень писать.</p>
<p>Теперь собственно о тонком клиенте, он будет немного не доделан. Дело в том что в одну статью все не впихнуть и в этой будут продемонстрированы только некоторые части интерфейса, потому что основной темой будет взаимодействие с базой данных MySQL. Все известные мне мануалы рассказываю как передавать данные в GWT используя XML или JSON сгенерированные PHP, а я хочу чтобы GWT получал данные напрямую из базы и передавал на клиент в сериализированом виде.</p>
<p>Если вы хотите попробовать запустить пример вот ссылка для скачивания <a href="http://mabp.kiev.ua/content/source/HelloWorld.zip" rel="nofollow">проекта</a> и исходников <a href="http://yogurtearl.com/ext-2.0.2.zip" rel="nofollow external">ext-js 2.0.2</a>
</p>
<p>Теперь об используемых компанентах: помимо всего прочего я буду использовать Spring Framefork, Hibernate, Log4j, JUnit и базу данных MySQL. Для того чтобы не разруливать немыслимое количество зависимостей в этих компонентах я и предложил вам поставить Maven, а еще он удобен для сборки билда. Я написал конфигурационный файл в который включил все необходимые зависимости и плагины для сборки. Я, если честно, не очень силен в конфигурации Maven'а, поэтому большая часть настроек была скопирована из мануала по GWT. Конфиг очень большой и его нет смысла выкладывать здесь, поэтому чтоб его посмотреть придется скачать проект.</p>
<p>Когда загрузите проект откроете закладку Maven'а и нажмете на кнопку закачать все артефакты</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/maven.jpg" alt="Control Panel" width="260" height="600"/>
</div>
<p>Если у вас медленное соединение или еще какие-то проблемы Maven может закачать не все и отказаться скачивать повторно. В общем я советую сразу через командную строку зайти в папку с проектом и выполнить команду:</p>
<pre>
<code class="bat">

mvn eclipse:eclipse -Declipse.downloadSources=true -DdownloadJavadocs=true

</code>
</pre>
<p>Эта команда скачает плагин для Eclipse, который умеет качать исходники и доки, правда он попутно закачивает еще какие-то свои файлы, а после работы создает файлы проекта Eclipse которые можно смело удалять по завершению загрузки. В общей сложности Maven скачает примерно 120 метров кода и документации.</p>
<p>В начале статьи была ссылка на EXT-JS, нужно скачать именно версию 2.0.2 (или 2.0.5 но тогда вы нарушите лицензию) c более поздними GWT-EXT работать не хочет. EXT-JS нужно распаковать в D:ideaHelloWorldsrcmainjavauakievmabppublicjsext , позже я объясню почему именно сюда.</p>
<p>Теперь когда у вас есть все исходники, нужно настроить базу данных, откройте файл D:ideaHelloWorldsrcmainresourcesdb.properties и начинайте править настройки подключения.</p>
<pre>
<code class="ini">

jdbc.driverClassName=com.mysql.jdbc.Driver			<- не менять
jdbc.url=jdbc:mysql://localhost:3306/helloworld		<- helloworld это имя базы
jdbc.username=root						<- пользователь
jdbc.password=qwerty					<- пароль
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect	<- диалект
hibernate.default_schema=helloworld			<- схема, можно не трогать

</code>
</pre>
<br />
Первую строку строгать не надо, во второй, после последнего слеша надо написать название БД, в третей - имя пользователя, в четвертой - пароль, разрабатывал на MySQL 5.1 если у вас 4 то в пятой надо убрать цифру 5, шестую можно не трогать. Структура таблицы и дамп данных наже:</p>
<pre>
<code class="sql">

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";


CREATE TABLE IF NOT EXISTS `car` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `model` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=43 ;


INSERT INTO `car` (`id`, `model`) VALUES
(1, 'Audi'),
(2, 'BMW'),
(3, 'Lexus'),
(4, 'Ford'),
(5, 'Honda'),
(6, 'Chevrolet'),
(7, 'KIA'),
(8, 'Mercedes'),
(9, 'Mitsubishi'),
(10, 'Nissan'),
(11, 'Opel'),
(12, 'Rover'),
(13, 'Subaru'),
(14, 'Toyota'),
(15, 'Volkswagen'),
(16, 'Volvo'),
(17, 'Mazda'),
(18, 'Skoda'),
(19, 'Lincoln'),
(20, 'Infiniti'),
(21, 'Cadillac'),
(22, 'Renault'),
(23, 'Daevoo'),
(24, 'Jeep'),
(25, 'Pontiac'),
(26, 'Hummer'),
(27, 'Bentley'),
(28, 'Isuzu'),
(29, 'Seat'),
(30, 'Fiat'),
(31, 'GMC'),
(32, 'Land Rover'),
(33, 'Porsche'),
(34, 'Jaguar'),
(35, 'Hyundai'),
(36, 'Peugeot'),
(37, 'Aston Martin'),
(38, 'Alfa Romeo'),
(39, 'Dodge'),
(40, 'Ferrari'),
(41, 'Saab'),
(42, 'Chrysler');

</code>
</pre>
<p>Все с базой почти закончили. Теперь надо зайти в Tools->Data Source (в восьмой идеи это панелька сбоку а в седьмой новое окно) и добавить новый источник данных как показано на скриншоте:</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/datasouce.jpg" alt="Control Panel" width="554" height="694"/>
</div>
<p>И наконец надо закачать данные о структуре при помощи кнопки Refresh Tables. Вот теперь с базой все.</p>
<p>Предполагается что вы все делали как я описывал в статьях по установке и все находиться на диске D:Program Files , если нет заходите в Project Structure и настраивайте </p>
<p>В закладке GWT указываете путь к GWT, после этого нажимать на кнопку FIX не нужно, потому что у вас dll'ки лежат в одной директории (D:Program Filesgwt-windows-1.5.3), а jar'ники в другой (D:Program Filesapache-maven-2.1.0repocomgooglegwtgwt-user1.5.3).</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-gwt.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>В закладке Spring скорее всего ничего править не придется потому что все три конфига подтянутся сами, но проверьте на всякий случай.</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-spring.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>В закладке JPA подключите только что созданный источник данных.</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-jpa.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>В закладке WEB тоже ничего делать не надо там все само подхватывается.</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-web.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>Возвращайтесь в корень настроек проекта, там три закладки, на первой (Sources) отметьте папки с исходниками (main/java), тестами (test/java)</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-sources.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>На второй (Paths) кажется ничего делать не надо</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-paths.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>А на закладке (Dependencies) проверьте чтоб не было пакета gwt-user (это значит вы все-таки нажали на кнопку FIX на закладке GWT) и был файл javaee.jar</p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/structure-dependencies.jpg" alt="Control Panel" width="500" height="300"/>
</div>
<p>Так все настройки закончили переходим к запуску программы.</p>
<p>Maven и GWT навязываю нам свою структуру проекта. От Maven нам достается разделение кода на исходники (main) и тесты (test), а от GWT три пакета: client - то что в последствии станет javascript кодом, public - статические ресурсы, сюда кладется ext-js, и server - классы которые работают на сервере, например для работы с БД.</p>
<p>Для минимального запуска нужно всего несколько файлов. Клаcc ua.kiev.mabp.client.HelloWorld расширяет точку входа в приложение, там задаются основные layuot'ы. HelloWorld.css и HelloWorld.html нужны для каркаса с которым будут работать все js функции. В HelloWorld.gwt.xml указываеться какие модули наследует сценарий. И наконец web.xml определяет точку входа (servlet) в приложение.</p>
<p>В примере есть несколько layaut'ов. Первый это выбор страны и города, демонстрирует функционал связанных выпадающих списков. Второй выбор даты, календари тоже должны были не пересекаться но к сожалению в ext-js 2.0.2 этого функционала еще не было (я опишу его в следующей статье где использую ext-js 2.2.1) поэтому использована валидация полей. Выбор марки автомобиля собственно тат самый виджет ради которого вся статья. И последний это выбор цвета, там совсем все просто.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client;


import com.google.gwt.core.client.EntryPoint;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.Viewport;
import com.gwtext.client.widgets.form.FieldSet;
import com.gwtext.client.widgets.layout.AnchorLayout;
import com.gwtext.client.widgets.layout.AnchorLayoutData;
import com.gwtext.client.widgets.layout.ColumnLayout;
import com.gwtext.client.widgets.layout.ColumnLayoutData;
import ua.kiev.mabp.client.widgets.CarPanel;
import ua.kiev.mabp.client.widgets.ColorPanel;
import ua.kiev.mabp.client.widgets.DatePanel;
import ua.kiev.mabp.client.widgets.LocationPanel;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class HelloWorld implements EntryPoint {


	public void onModuleLoad() {
		Panel panel = new Panel();
		panel.setBorder(false);
		panel.setPaddings(15);
		panel.setLayout(new AnchorLayout());


		panel.add(new FieldSet(), new AnchorLayoutData("100%"));


		Panel wrapperPanel = new Panel();
		wrapperPanel.setBorder(false);


		Panel columnOnePanel = new Panel();
		columnOnePanel.setBorder(false);
		columnOnePanel.setLayout(new AnchorLayout());
		columnOnePanel.add(new LocationPanel(), new AnchorLayoutData("100%"));
		columnOnePanel.add(new DatePanel(), new AnchorLayoutData("100%"));
		columnOnePanel.add(new CarPanel(), new AnchorLayoutData("100%"));


		Panel columnTwoPanel = new Panel();
		columnTwoPanel.setBorder(false);
		columnTwoPanel.setLayout(new AnchorLayout());
		columnTwoPanel.add(new ColorPanel(), new AnchorLayoutData("100%"));


		wrapperPanel.setLayout(new ColumnLayout());
		wrapperPanel.add(columnOnePanel, new ColumnLayoutData(.5));
		wrapperPanel.add(columnTwoPanel, new ColumnLayoutData(.5));


		panel.add(wrapperPanel, new AnchorLayoutData("100%"));
		panel.add(new FieldSet(), new AnchorLayoutData("100%"));


		new Viewport(panel);
	}
}

</code>
</pre>
<p>Немного поигравшись с layaut'ами можно начать прикручивать БД, данные из которой получает выпадающий список.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.widgets;


import com.gwtext.client.core.Template;
import com.gwtext.client.core.UrlParam;
import com.gwtext.client.data.*;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.form.ComboBox;
import com.gwtext.client.widgets.form.FieldSet;
import com.gwtext.client.widgets.form.event.ComboBoxListenerAdapter;
import ua.kiev.mabp.client.proxies.MyProxy;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class CarPanel extends FieldSet {
	public CarPanel(){
	super("Select your car");


		// определяе вид записи
		FieldDef[] fieldDefs = new FieldDef[]{new StringFieldDef("id"), new StringFieldDef("model")};
		RecordDef recordDef = new RecordDef(fieldDefs);


		// создаем хранилище
		final Store store = new Store(new MyProxy(), new ArrayReader(recordDef), true);
		store.setBaseParams(new UrlParam[]{new UrlParam("paramName", "paramValue")});


		// создаем новую панель
		// сюда будет дублироваться выбраный результат
		final Panel instructionPanel = new Panel();
		instructionPanel.setBorder(false);
		instructionPanel.setHeight(24);
		instructionPanel.setHtml("Car model doesn't selected yet");


		// создаем новый шаблон
		final Template template = new Template("&lt;div class="x-combo-list-item"&gt;" +
				"&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;" +
				"&lt;td style="width:50px;border-right:2px dotted #A3BAE9;"&gt;{id}&lt;/td&gt;" +
				"&lt;td style="width:200px;border-right:2px dotted #A3BAE9;"&gt;{model}&lt;/td&gt;" +
				"&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;" +
				"&lt;div class="x-clear"&gt;&lt;/div&gt;&lt;/div&gt;");


		// создаем новый выпадающий список 
		// и присваеваем ему хранилище и шаблон
		final ComboBox carComboBox = new ComboBox();
		carComboBox.setMinChars(1);
		carComboBox.setFieldLabel("Label");
		carComboBox.setStore(store);
		carComboBox.setDisplayField("model");
		carComboBox.setMode(ComboBox.LOCAL);
		carComboBox.setTriggerAction(ComboBox.ALL);
		carComboBox.setForceSelection(true);
		carComboBox.setEmptyText("Choose car model");
		carComboBox.setTypeAhead(true);
		carComboBox.setSelectOnFocus(true);
		carComboBox.setResizable(true);
		carComboBox.setTpl(template);
		carComboBox.setHideLabel(true);


		// устанавливаем листенер
		carComboBox.addListener(new ComboBoxListenerAdapter() {
			public void onSelect(ComboBox comboBox, Record record, int index) {
				instructionPanel.setHtml(record.getAsString("id") + " " + record.getAsString("model"));
			}
		});


		// добавляем все в текущий layout
		add(carComboBox);
		add(instructionPanel);


		// загружаем в список 5 первых элементов
		store.load(0, 5);
	}
}



</code>
</pre>
<p>Итак кроме того что виджет CarPanel создает ComboBox с листенером он использует для получения данных прокси MyProxy.</p>
<p>Итак кроме того что виджет CarPanel создает ComboBox с листенером он использует для получения данных прокси MyProxy.</p>
<pre>
<code class="java">

package ua.kiev.mabp.client.proxies;


import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.gwtext.client.core.UrlParam;
import ua.kiev.mabp.client.HelloWorldService;
import ua.kiev.mabp.client.beans.ArrayBean;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class MyProxy extends GWTProxy {


	/**
	 * Загружает данные в объект
	 *
	 * @param start позиция первого элемента
	 * @param limit количество элемента
	 * @param sort сортировка
	 * @param dir направление сортировки
	 * @param javaScriptObject объект
	 */
	public void load(int start, int limit, String sort, String dir, JavaScriptObject javaScriptObject) {
		load(start, limit, sort, dir, javaScriptObject, null);
	}


	/**
	 * Загружает данные в объект
	 *
	 * @param start позиция первого элемента
	 * @param limit количество элемента
	 * @param sort сортировка
	 * @param dir направление сортировки
	 * @param o объект
	 * @param baseParams параметры запроса
	 */
	public void load(int start, int limit, String sort, String dir, final JavaScriptObject o, UrlParam[] baseParams) {
		String[][] params = new String[0][];
		HelloWorldService.App.getInstance().getCars(start, limit, sort, dir, params, new AsyncCallback() {
			public void onFailure(Throwable caught) {
				loadResponse(o, false, 0, (JavaScriptObject) null);
			}


			public void onSuccess(Object result) {
				ArrayBean response = (ArrayBean) result;
				loadResponse(o, true, response.totalPages, response.data);
			}
		});
	}
}



</code>
</pre>
<p>Класс MyProxy наследует класс GWTProxy который был выдран мной из проекта gwt-ext-ux (com.gwtextux.client.data.GWTProxy), который я счел не нужным подключать из-за одного класса. Самый большой интерес в классе MyProxy представляет вызов метода HelloWorldService.App.getInstance().getCars() .</p>
<pre>
<code class="java">

package ua.kiev.mabp.client;


import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import ua.kiev.mabp.client.beans.ArrayBean;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public interface HelloWorldService extends RemoteService {


	public static class App {
		private static HelloWorldServiceAsync ourInstance = null;


		public static synchronized HelloWorldServiceAsync getInstance() {
			if (ourInstance == null) {
				ourInstance = (HelloWorldServiceAsync) GWT.create(HelloWorldService.class);
				((ServiceDefTarget) ourInstance).setServiceEntryPoint(GWT.getModuleBaseURL() + "ua.kiev.mabp.HelloWorld/HelloWorldService");
			}
			return ourInstance;
		}
	}


	public ArrayBean getCars(int start, int limit, String sort, String dir, String params[][]);
}

</code>
</pre>
<p>HelloWorldService это интерфейс описанный в пакете client и имеющий реализацию в пакете server. Внутренний класс App знает о бэкенде у которого можно получить данные. Этот бекэнд описывается в конфигах в HelloWorld.gwt.xml и web.xml .</p>
<pre>
<code class="java">

package ua.kiev.mabp.server;


import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ua.kiev.mabp.server.beans.DaoManager;
import ua.kiev.mabp.server.beans.ArrayHelper;
import ua.kiev.mabp.server.beans.entity.Car;
import ua.kiev.mabp.client.HelloWorldService;
import ua.kiev.mabp.client.beans.ArrayBean;
import java.util.Collection;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class HelloWorldServiceImpl extends RemoteServiceServlet implements HelloWorldService {
	DaoManager daoManager = DaoManager.getInstance();


	public ArrayBean getCars(int start, int limit, String sort, String dir, String params[][]) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:ApplicationConfig.xml");
		Collection&lt;Car&gt; col = daoManager.getCarDao().getCarByCriteria(start, limit, "ASC".equals(dir), sort);
		return new ArrayBean(ArrayHelper.collectionToArrayString(col), 30);
	}
}

</code>
</pre>
<p>Реализующий интерфейс HelloWorldService класс HelloWorldServiceImpl, знает о конфигах Spring. Этот конфиг знает о файле db.properties в котором описано как подключиться к базе. Я не буду опубликовывать тут весь конфиг, только самую интересную его часть. </p>
<pre>
<code class="xml">

&lt;bean id="car" class="ua.kiev.mabp.server.beans.dao.CarDAO"&gt;
	&lt;property name="sessionFactory" ref="sessionFactory"/&gt;
&lt;/bean&gt;
&lt;bean id="daoManager" class="ua.kiev.mabp.server.beans.DaoManager" factory-method="getInstance"&gt;
	&lt;property name="carDao" ref="car"/&gt;
&lt;/bean&gt;

</code>
</pre>
<p>Тут описывается что singleton класс DaoManager для своего создания использует метод getInstance и что ему нужно наполнить свойство carDao классом CarDAO</p>
<pre>
<code class="java">

package ua.kiev.mabp.server.beans.dao;


import ua.kiev.mabp.server.beans.entity.Car;


import java.util.List;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


public class CarDAO extends DAOBean  {


	/**
	 * Сохраняет элемент
	 *
	 * @param bean элемент
	 */
	public void save(Car bean) {
		getHibernateTemplate().save(Car.class.getName(), bean);
	}


	/**
	 * Удаляет элемент
	 *
	 * @param bean элемент
	 */
	public void delete(Car bean) {
		getHibernateTemplate().delete(bean);
	}


	/**
	 * Получает список объектов
	 *
	 * @param start позиция первого элемента
	 * @param limit количество элемента
	 * @param sort сортировка
	 * @param fieldName название поля
	 * @return список
	 */
	public List&lt;Car&gt; getCarByCriteria(int start, int limit, boolean sort, String fieldName) {
		return getObjectsByCriteria(start, limit, sort, fieldName, Car.class);
	}


	/**
	 * Получает весь список операций
	 *
	 * @return коллекция операций
	 */
	public List&lt;Car&gt; getAllCar() {
		return getHibernateTemplate().loadAll(Car.class);
	}


	/**
	 * Получает список операция по производству машины
	 *
	 * @param s где произведена
	 * @return коллекция операция
	 */
	public List&lt;Car&gt; getCarByModel(String s) {
		return getHibernateTemplate().findByNamedQueryAndNamedParam("findByModelFromCar", "model", s);
	}


	/**
	 * Получает операции по id
	 *
	 * @param id номер операции
	 * @return коллекция операций
	 */
	public Car getCarById(Integer id) {
		return (Car) getHibernateTemplate().get(Car.class, id);
	}
}



</code>
</pre>
<p>Класс CarDAO содержит метод getCarByCriteria который вызывается в классе HelloWorldServiceImpl. Метод возвращает список бинов Car. Немного отвлекусь и расскажу о дополнительных возможностях. Вместо вызова getCarByCriteria можно вызывать getCarByModel передавая модель машины, это будет полезно в каскадных списках, при этом findByModelFromCar описано в классе Car в виде аннотаций.</p>
<pre>
<code class="java">

package ua.kiev.mabp.server.beans.entity;


import javax.persistence.*;
import java.io.Serializable;


/**
 * Created by IntelliJ IDEA.
 * User: CTAPbIu_MABP
 * Date: 10.04.2009
 * Time: 22:58:15
 */


@Entity
@Table(name = "car")
@NamedQueries({
	@NamedQuery(name = "findByModelFromCar", query = "select t from Car t where t.model=:model order by t.model"),
	@NamedQuery(name = "findByIdFromCar", query = "select t from Car t where t.id=:id")
})
public class Car implements Serializable {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id", unique = true, nullable = false, length = 11)
	private Integer id;
	@Column(name = "model")
	private String model;
	private static final long serialVersionUID = 5664909385244426031L;


	/**
	 * Getter for property 'id'.
	 *
	 * @return Value for property 'id'.
	 */
	public Integer getId() {
		return id;
	}


	/**
	 * Setter for property 'id'.
	 *
	 * @param id Value to set for property 'id'.
	 */
	public void setId(Integer id) {
		this.id = id;
	}


	/**
	 * Getter for property 'model'.
	 *
	 * @return Value for property 'model'.
	 */
	public String getModel() {
		return model;
	}


	/**
	 * Setter for property 'model'.
	 *
	 * @param model Value to set for property 'model'.
	 */
	public void setModel(String model) {
		this.model = model;
	}
}

</code>
</pre>
<p>После того как класс HelloWorldServiceImpl получит коллекцию бинов Car он должен превратить ее в двухмерный массив строк, который после сериализации можно передать клиенту. Все стек распутан, а рассказ подошел к концу. И стоит показать как выглядит то ради чего вы все это читали. </p>
<div style="text-align:center">
<img src="/content/img/gwt_extjs_mysql/result.jpg" alt="Control Panel" width="600" height="570"/>
</div>
<p>Все кто дочитал до конца молодец, а я готовлю следующую статью, до скорых встреч.</p>
<br />
]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/04/12/gwt-extjs-connecting-to-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Наконец-то заработал поиск</title>
		<link>http://mabp.kiev.ua/2008/04/26/search_engine_works_now/</link>
		<comments>http://mabp.kiev.ua/2008/04/26/search_engine_works_now/#comments</comments>
		<pubDate>Sat, 26 Apr 2008 15:47:10 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[perfomance]]></category>

		<guid isPermaLink="false">http://mabp.localhost/?p=196</guid>
		<description><![CDATA[





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



Вот я и сделал наконец поиск по сайту, вообще еще много чего надо сделать, а то я даже страницы добавляю через MySQL. Сейчас поиск ищет по заголовку, описанию и собственно телу страницы, самое приятное в нем то, что в нем есть ранжирование результатов. Если кому-то [...]]]></description>
			<content:encoded><![CDATA[<table style="background-color:#EDEFF0;width:100%;">
<tbody>
<tr>
<th>
<img src="/wp-content/themes/inove/img/warning_red.png"/>
</th>
<th style="text-align: center;">Статья более не актуальна, так как относилась к старому, самописному движку.</th>
</tr>
</tbody>
</table>
<p>Вот я и сделал наконец поиск по сайту, вообще еще много чего надо сделать, а то я даже страницы добавляю через MySQL. Сейчас поиск ищет по заголовку, описанию и собственно телу страницы, самое приятное в нем то, что в нем есть ранжирование результатов. Если кому-то интересно как такое сделать то читайте дальше.</p>
<span id="more-196">
</span>
<p>MATCH() ... AGAINST() первое, что приходит в голову, когда говорят о поиске с релевантностью, забудьте об этом бреде!!! Во-первых, это накладывает очень два ограничений: первое и главное - надо делать полнотекстовые индексы, без них никак, второе - индексы можно сделать только на таблицах MyISAM. Во вторых это просто глючно, потому что при поиске небольших фраз по большой базе их релевантность близка к абсолютному нулю, или же если слово присутствует в более 50% строк, то оно не учитывается.</p>
<pre>
<code class="sql">

SELECT 
MATCH('Content') AGAINST ('keyword1 keyword2') as Relevance 
FROM table 
WHERE MATCH ('Content') AGAINST('+keyword1 +keyword2' IN BOOLEAN MODE) 
HAVING Relevance > 0.2 
ORDER BY Relevance DESC

</code>
</pre>
<p>Это так сказать классический способ поиска, взятый с сайта самого мускула, но ИМХО это большая ошибка, тут первый MATCH выдает релевантность, а второй который IN BOOLEAN MODE используется для поиска обоих слов сразу в одной строке, если есть только одно то он выдаст 0. Если слов больше двух то это начинает давать сильную погрешность, выдавая только те ряды, где есть все слова фразы. Более того, куча MATCH сильно тормозит запрос.</p>
<p>Поэтому лучше ввести весовые коэффициенты в запрос. Коэффициенты вы раздаете сами, поэтому их общее число всегда может быть в пределах 100 и к нему можно смело добавлять знак %. Итак у нас есть три колонки title, description и text. Если в каждом из трех полей есть искомое словосочетание то релевантность 100%, при этом каждое поле имеет свой вес: заголовок - самый большой(40%), потом описание(20%) а потом уже текст(10%). Я считаю, что полное словосочетание это 70% релевантности и если есть все слова из него, то это еще 30%. немного запутал, сейчас объясню. Есть словосочетание "мама мыла раму", если оно дословно есть во всех полях то это 70%, а если в каждом из них есть "раму мыла мама", то это только 30%. В результате мы получаем либо 100% (70% все вместе и 30% каждое по отдельности) совпадение со строкой либо только 30% (каждое по отдельности) совпадение, ну это лично мое мнение.</p>
<p>Итак алгоритм будет выгладить следующим образом:</p>
<pre>
<code class="php">

$text = "мама мыла раму";
$query = "SELECT *, 
IF (title like '%".$text."%', 40, 0) + IF (title LIKE '%".str_replace(" ", "%', 5.71, 0) + IF (title LIKE '%", $text)."%', 5.71, 0) + 
IF (description like '%".$text."%', 20, 0) + IF (description LIKE '%".str_replace(" ", "%', 2.86, 0) + IF (description LIKE '%", $text)."%', 2.86, 0) +
IF (text like '%".$text."%', 10, 0) + IF (text LIKE '%".str_replace(" ", "%', 1.43, 0) + IF (text LIKE '%", $text)."%', 1.43, 0) AS rel
FROM pages 
WHERE 
(title LIKE '%".str_replace(" ", "%' OR title LIKE '%", $text)."%') OR
(description LIKE '%".str_replace(" ", "%' OR description LIKE '%", $text)."%') OR
(text LIKE '%".str_replace(" ", "%' OR text LIKE '%", $text)."%') 
ORDER BY rel DESC";

</code>
</pre>
<p>Если подставить значения будет выглядеть менее запутано но все равно непонятно </p>
<pre>
<code class="sql">

SELECT *, 
IF (title LIKE '%мама мыла раму%', 40, 0) + IF (title LIKE '%мама%', 5.71, 0) + IF (title LIKE '%мыла%', 5.71, 0) + IF (title LIKE '%раму%', 5.71, 0)  + 
IF (description LIKE '%мама мыла раму%', 20, 0) + IF (description LIKE '%мама%', 2.86, 0) + IF (description LIKE '%мыла%', 2.86, 0) + IF (description LIKE '%раму%', 2.86, 0)  + 
IF (text LIKE '%мама мыла раму%', 10, 0) + IF (text LIKE '%мама%', 1.43, 0) + IF (text LIKE '%мыла%', 1.43, 0) + IF (text LIKE '%раму%', 1.43, 0) AS rel
FROM pages 
WHERE 
(title LIKE '%мама%' OR title LIKE '%мыла%' OR title LIKE '%раму%') OR
(description LIKE '%мама%' OR description LIKE '%мыла%' OR description LIKE '%раму%') OR
(text LIKE '%мама%' OR text LIKE '%мыла%' OR text LIKE '%раму%')
ORDER BY rel DESC

</code>
</pre>
<p>Вот такой немаленький запросик получился))) Теперь попробуем разобрать что значат все эти непонятные коэффициенты 5.71, 2.86 и 1.43. Для этого я нарисую маленькую табличку.</p>
<table border="1" align="center" style="border-collapse: collapse;">
<tr>
<th>\</th>
<th>Все</th>
<th>мама</th>
<th>мыла</th>
<th>раму</th>
</tr>
<tr>
<td>title</td>
<td>40</td>
<td>5.71</td>
<td>5.71</td>
<td>5.71</td>
</tr>
<tr>
<td>description</td>
<td>20</td>
<td>2.86</td>
<td>2.86</td>
<td>2.86</td>
</tr>
<tr>
<td>text</td>
<td>10</td>
<td>1.43</td>
<td>1.43</td>
<td>1.43</td>
</tr>
</table>
<p>Сумма первого столбика получается как раз 70%, сумма всех остальных ячеек 30% в результате искомые 100% релевантности. Сумма каждого следующего столбика 10%. А сумма каждой ячейки высчитывается по формуле (вес поля) * (общее число процентов 30) / (общее число коэффициентов 7) / (количество слов), если подставить поле title то выйдет 4*30/7/3 = 5.71. В общем, я думаю, поняли, кто не понял, возьмите калькулятор!</p>
<p>В том что такой запрос отрабатывает весьма шустро вы можете перейдя на страницу поиска, там выводиться время потраченное на запрос. </p>
<p>Кстати в моем поиске еще наложена логика отключения полей, если отключено одно поле то коэффициенты становятся 3 и 4 , а если отключено два поля то оставшееся имеет коэффициент 7. И еще не забудьте удалить пробелы в начале и конце строки, а также двойные пробелы в середине. Все!</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2008/04/26/search_engine_works_now/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MySQL Session Manager</title>
		<link>http://mabp.kiev.ua/2007/06/21/mysql_session_manager/</link>
		<comments>http://mabp.kiev.ua/2007/06/21/mysql_session_manager/#comments</comments>
		<pubDate>Thu, 21 Jun 2007 14:00:20 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://mabp.localhost/?p=150</guid>
		<description><![CDATA[Для создания этого сайта мне нужен был класс для управления сессией так как я не хотел использовать обычный механизм, хранения в файлах, а использовать базу данных.
Прежде чем писать свой я решил обратиться к мудрости всезнающего Google и нашел более менее подходящий класс, написанный человеком по имени Robin Schuil. Он был простой и быстрый но использовал [...]]]></description>
			<content:encoded><![CDATA[<p>Для создания этого сайта мне нужен был класс для управления сессией так как я не хотел использовать обычный механизм, хранения в файлах, а использовать базу данных.</p>
<p>Прежде чем писать свой я решил обратиться к мудрости всезнающего Google и нашел более менее подходящий класс, написанный человеком по имени Robin Schuil. Он был простой и быстрый но использовал сразу несколько баз данных для хранения информации, поэтому я взялся его переделать.</p>
<span id="more-150">
</span>
<p>И вот что у меня получилось:</p>
<pre>
<code class="php">

class session {


	public $lt;	// int
	public $db;	// obj


	public function __construct(&#038;$db)
	{
		$this->db = &$db;


		// Read the maxlifetime setting from PHP
		$this->lt = get_cfg_var("session.gc_maxlifetime");


		// Register this object as the session handler
		session_set_save_handler( 
			array( $this, "open" ),
			array( $this, "close" ),
			array( $this, "read" ),
			array( $this, "write"),
			array( $this, "destroy"),
			array( $this, "gc" )
		);
	}


	public function start()
	{
		session_start();
	}


	public function stop()
	{
		session_unset();
		session_destroy();
	}


	public function prolong($id,$sttl)
	{
		$this->lt += $sttl;
		$data = $this->db->Execute("UPDATE `sessions` SET `expires` = UNIX_TIMESTAMP() + '" . $this->lt . "' WHERE `id` = '" . addslashes( $id ) . "';");
		return (bool)$data->RecordCount();
	}


	public function open($save_path, $session_name)
	{
		return true;
	}


	public function close()
	{
		return true;
	}


	public function read($id)
	{
		$data = $this->db->Execute("SELECT `data` FROM `sessions` WHERE `id` = '" . addslashes( $id ) . "' AND `expires` > UNIX_TIMESTAMP();");
		return $data->fields['data'];
	}


	public function write($id,$data)
	{
		$data = $this->db->Execute("INSERT `sessions` (`id`,`data`,`initiation`,`expires`) VALUES( '" . addslashes( $id ) . "', '" . addslashes( $data ) . "', UNIX_TIMESTAMP() , UNIX_TIMESTAMP() + " . $this->lt . ") ON DUPLICATE KEY UPDATE data = '" . addslashes( $data ) . "', expires = UNIX_TIMESTAMP() + " . $this->lt . ";");
		return (bool)$data->RecordCount();
	}


	public function destroy($id)
	{
		$data = $this->db->Execute("DELETE FROM `sessions` WHERE `id` = '" . addslashes( $id ) . "';");
		return (bool)$data->RecordCount();	
	}


	public function gc()
	{
		$data = $this->db->Execute("DELETE FROM `sessions` WHERE `expires` <= UNIX_TIMESTAMP();");
		return true;
	}


	public function __destruct()
	{
		$this->db = null;
	}
}

</code>
</pre>
<p>Для хранения сессий используеться MySQL, а как <acronym title="Data Base Abstract Layer">DBAL</acronym> используеться ADOdb, хотя по желанию ее можно заменить любой другой изменив названия функций Execute и RecordCount на соответствующие.</p>
<p>Естественно чтобы использовать этот класс нужно создать новую таблицу, вот ее структура. </p>
<pre>
<code class="sql">

CREATE TABLE `sessions` (
  `id` varchar(32) NOT NULL,
  `data` text NOT NULL,
  `initiation` int(11) NOT NULL,
  `expires` int(11) NOT NULL,
  UNIQUE KEY `id` (`id`),
  KEY `DATE` (`initiation`,`expires`)
) ENGINE=MyISAM DEFAULT CHARSET=UTF8;

</code>
</pre>
<p>Ну и конечно пример использования.</p>
<pre>
<code class="php">

$session = new session();
$session->start();
if (true){ // например если пользовает залогинелся
	$_SESSION['login'] = 'login';
	$_SESSION['data'] = 'user data';
	$session->prolong($_SESSION['id'],3600);
}else{ // или наоборот если вышел
	$session->close();
}

</code>
</pre>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2007/06/21/mysql_session_manager/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
