Shared rss feeds:

Servare Mentem - blog

Мой предыдущий блог

среда, 27 февраля 2008 г.

Вконтакте появился платный сервис

Ура, товарищи! Мы наконец-то дожили до дня, когда бескорыстность авторов самого популярного в России(и не только) сайта "ВКонтакте.ру" достигла своего предела! Мы видим новый платный сервис! Авторы, однако, в описании нового сервиса предусмотрительно завуалировали слово "получение наконец-то хоть какой-то прибыли" словами "во избежание спама и накруток".
Цитата с сайта:
При помощи рейтинга популярности ВКонтакте предоставляет пользователям возможность определять порядок вывода участников при поиске, в списках друзей, в списках участников групп и т.д. Таким образом, в поиске первыми всегда выводятся активные, уважаемые, популярные и красивые люди.
Для того, чтобы избежать спама и накруток, мы просим Вас завалидировать Ваш голос с помощью SMS на номер 4444 с текстом ...

Что ж, друзья. Ничто не вечно. И мир полностью бесплатных Интернет-сервисов, тем более имеющих подобную популярность, не исключение. Тем более, что на сайте ВКонтакте многие давно зарабатывают неплохие деньги путём рекламы или подбора персонала. Популярность этой социальной сети просто потрясающая. Я, честно говоря, удивлён, что подобный платный сервис появился только сейчас. До этого на ВКонтакте я видел сервис "работа" или что-то подобное, который позволяет подыскивать сотрудников. Но он не был так явно открыт для всех, да и аудитория, нуждающаяся в этом сервисе достаточно ограничена. Теперь же все пользователи узнают что от них требуется для накрутки своего/чужого рейтинга...
Честно говоря, я вполне понимаю и даже в какой-то степени одобряю авторов за данное нововведение. Но я огорчён осознанием того, что ничего полностью бескорыстного в этом мире просто не может быть.
А вдруг, завтра Google тоже сделает, к примеру, свою почту платной, как это изначально и планировалось? Вот это будет уже сильным огорчением...

вторник, 26 февраля 2008 г.

MSSQL 2005 : Full Text Catalogs

Столкнулся сегодня на работе с проблемой: поиск на production у заказчика стал работать как-то странно: выдаёт не все результаты. Думаю, ну всё, придётся копать эту кошмарную процедуру в 450 строк, которую писал не я, да ещё и с динамическим sql, курсорами и временными таблицами... Ужас ситуации заставил меня содрогнуться... Но делать было нечего. Для начала запустил профайлер и посмотрел как именно вызывается эта процедура из слоя COM объектов (да-да, в этом приложении ещё используются COM'ы, написанные на VB6). Профайлер показал мне буквально следующее:
exec groupSearch N'"word"',0,0,NULL,NULL,0,NULL,NULL,NULL,1,10,0, 
N'DECLARE @a0 NVARCHAR(999) SET @a0=N''%manifold%''',N'((A LIKE @a0)) 
OR ((B LIKE @a0)) OR ((C LIKE @a0)) OR ((D LIKE @a0)) OR ((E LIKE @a0)) 
OR ((F LIKE @a0)) OR ((G LIKE @a0)) OR ((H LIKE @a0)) OR ((I LIKE @a0)) 
OR ((J LIKE @a0)) OR ((K LIKE @a0)) OR ((L LIKE @a0)) OR ((M LIKE @a0)) 
OR ((N LIKE @a0))',68,0,0,1
Я оценил красоту кода и пошёл дальше. Теперь меня стало интересовать какой SQL генерит процедура когда её вызывают подобным образом. Я вывел его при помощи print и запустил. К своему неудовольствию я обнаружил, что вместо любимого мной оператора LIKE в запросе используется какой-то непонятный CONTAINS. Почитав справку про этот CONTAINS я понял что вещь достаточно удобная, поскольку, ему можно скормить не одно поле и даже не несколько. Можно в буквальном смысле в CONTAINS передать *, что будет означать что поиск будет осуществлён по всем полям таблицы. Однако, поэксперементировав с CONTAINS я увидел что результаты, возвращаемые им зачастую отличаются от результатов, которые возвращает LIKE. И как бы я не писал запрос, всё равно факт оставался - некоторые записи просто не попадали в выборку. Например:
select * from table where CONTAINS(Name,'"word"');
select * from table where Name like '%word%';
Первый запрос возвращал мне 3 записи, а второй все 6. В чём же дело. Сначала я подумал что проблема именно в принципе работы CONTAINS - мол, он и должен возвращаеть только записи, где искомое слово в конце или в начале текста (так получалось по результатам). Но перспектива переписывать ужасную процедуру на LIKE меня не радовала, поэтому я начал копать. Оказывается contains работатет только при включённом полнотекстовом индексировании (Full Text Search) для таблицы. А чтобы включить его, необходимо добавить в бу так называемый Full Text Catalog (полнотекстовый каталог). Активация функции полнотекстового индексирования для таблицы Microsoft SQL Server 2005 состоит из двух этапов:
  1. Создание полнотекстового каталога для хранения полнотекстовых индексов.
  2. Создание полнотекстовых индексов.
Чтобы создать полнотекстовый каталог AdvWksDocFTCat, нужно выполнить инструкцию CREATE FULLTEXT CATALOG:
CREATE FULLTEXT CATALOG AdvWksDocFTCat
Но удобнее сделать это через SQL Management Studio. Для этого нужно перейти к Storage->Full Text Catalogs. В этот момент я понял что полнотекстовый каталог в базе уже есть и полез смотреть его свойства. Мне показалось что всё настроено верно и нужно просто перестроить сам каталог, что я и попытался сделать. Вопреки ожиданиям (на форумах писали что rebuild идёт очень долго), rebuild прошёл моментально и я увидел, что это какой-то глюк mssql, поскольку реально он ничего не пересобрал и просто начал отображать нулевые значения (фактически он просто убил старый каталог, а создавать его заново и не подумал). Нажав на rebuild ещё несколько раз я наконец-то добился желаемого результата и каталог пересобрался. Однако, победа оказалась неокончательной - теперь запрос с CONTAINS перестал выдавать какие-либо результаты, будто каталог полнотекстового поиска был убит вовсе. Поиграв с настройками я случайно выяснил что для нормальной работы полнотекстового поиска по таблице необходимо ещё и указать Track changes: Automatically для таблиц, которые в нём нуждаются. После этого запросы с CONTAINS стали работать так же хорошо как и LIKE.

Ярлыки: ,

Hibernate + MySQL + c3p0

При тестировании приложения, использующего Hibernate и коннект к MySQL обнаружилось, что если какое-то время соединение с базой "простаивает", возникает исключение типа org.hibernate.exception.JDBCConnectionException. Проблема известная, заключается в том, что по таймауту сервер MySQL отваливает соединение, не извещая об этом клиента. С помощью idle-timeout-in-seconds и max-wait-time-in-millis это не настраивается.
Решение - использование пула коннекшенов, который умеет мониторить состояние соединения, посылая периодически запросы к серверу. Таким образом нужно обновляемое соединение. Собственный алгоритм помещения соединений в пул (connection pooling algorithm) у Hibernate довольно прост. Его назначение помочь пользователю как можно быстрее начать использовать Hibernate без дополнительных настроек и он не предназначен для использования в рабочих системах, а также для тестирования производительности. Для лучшей производительности и стабильности нужно использовать третьесторонний пул соединений, т.е. нужно заменить свойство hibernate.connection.pool_size определенными настройками пула соединений.
С3PO -- JDBC пул соединений с открытыми исходными кодами (open source connection pool), распространяемый вместе с Hibernate в каталоге lib. Hibernate будет использовать встроенный провайдер соединений C3P0ConnectionProvider для пула, если установить свойства hibernate.c3p0.*. Также существует встроенная поддержка для Apache DBCP и Proxool. Для того чтобы использовать DBCPConnectionProvider, необходимо установить свойства DBCP пула соединений hibernate.dbcp.*. Кэширование подговленных JDBC-выражений (prepared statement caching) включается если установлены следующие свойства hibernate.dbcp.ps.* (DBCP statement cache properties).
Вот пример использования C3PO (hibernate.cfg.xml):



           
        org.postgresql.Driver
        jdbc:postgresql://localhost/mydatabase
        xxx
        xxx
        true
        xxx
        net.sf.hibernate.dialect.PostgreSQLDialect
        5
        20
        1800        
        50        
        
        
    
 

Ярлыки: , , ,

среда, 20 февраля 2008 г.

Java vs C#

Начну свой новый блог, пожалуй, с очень интересной и актуальной темы: сравнение возможностей Java и C#. Нашёл очень хорошую статью, где всё разобрано подробно и разложено по полочкам. Для сравнения на некоторых сайтах попадались совершенно безграмотные обзоры. Очевидно, что люди совершенно не знают о возможностях Java и судят о них с потолка. Например, видел где-то упоминание о том, что в Java нет очень полезного и удобного оператора foreach или аналогичного ему (который, впрочем, MS спёрли у Perl), тогда как в java есть полный аналог foreach: for({type} {identifier} : {array})
Вообще, решил заняться этим вопросом, когда мне начали говорить, что C# несравнимо более богатый и продвинутый язык, нежели Java. Меня это несколько удивило - ведь я всегда считал, что C# - это просто фактическая копия явы с чуть изменённым синтаксисом. А утверждается, что в C# в отличие от Java есть: нормальные Generic, delegates, partial classes и closures.

closures & delegates

Я начал с последнего, залез в справку и увидел, что closures в Java в прямом понимании действительно нет (их введут только с JDK1.7). Но вот что интересно - так ли эти closures важны - то есть неужели их наличие играет огромную роль? Сам я, как человек, имеющий достаточно небольшой опыт работы и с Java и с C#, их никогда не использовал. Давайте посмотрим. Что такое по сути closure? Первый раз при программировании я столкнулся с этим понятием когда писал класс для рендеринга на C++ Builder и хотел спрятать таймер, по которому происходила бы отрисовка сцены внутрь него. Соответственно мне требовался callback, который задавался бы этому таймеру и я хотел задать в качестве оного - метод этого класса. Но с удивлением тогда для себя увидел, что компилятор этого мне этого сделать не позволяет. Почитав справку, я узнал что у него на это есть две причины – во-первых, чисто формальная. По правилам языка, указатель на метод класса имеет тип не указателя на функцию, а указателя на метод. И его нельзя приводить к указателю на функцию. Из-за второй причины – когда мы вызываем какой-то метод, то мы его вызываем не сам по себе, а для какого-то объекта. И указатель на этот объект передается неявно как указатель this, таким образом этот метод знает поля какого именно объекта надо модифицировать. Поэтому при использовании callback'а - метода класса, нужно как-то передавать ещё и указатель на объект. Для Borland эта проблема решилась изменением стандарта C++ и добавлением модификтора __closure в свой компилятор. Closure – это специальный вид XE(расширения стандарта) указателя на функцию, используемый в большинстве функций в библиотеках Windows. В отличие от обычных указателей на функции, эти содержат не только адрес вызываемой функции (четырехбайтный указатель), но так же и указатель на объект , для которого вызывается событие (указатель this). Использование выражения __closure некоторым образом ограничивает возможности системы, так как при нем возможно использовать лишь ограниченное число объектов одного класса. Считается, что использование указателей closure – весьма важная концепция, осуществленная в CBuilder. Хотя, лично я считаю, что любая концепция, которая там осуществлена и которая при этом отдаляет их среду от стандарта - есть зло. Для того, чтобы использовать closure и при этом не нарушать стандарт можно легко обойтись маленькой библиотчкой, которую вполне можно написать самому. Подобная же вещь реализована в библиотеке boost без всяких дополнительных модификаторов. Итак, а что понимается под closure в C# и почему же это так важно? В шарпе под closure понимается функция, которая захватывает значения своего лексического окружения, когда она создается во время исполнения. Лексическое окружение функции есть множество локальных переменных, полей и других членов классов, видимых из этой функции. Определение замыкания подразумевает понятие создания функции во время исполнения. Рассмотрим пример использования замыканий на C#:
int[] ary = { 1, 2, 3 };
int x = 2;
Array.ConvertAll(ary, delegate(int elem) { return elem * x; }); // { 2, 4, 6 }
Таким образом, C# использует анонимные методы, способные замыкаться на контекст в котором они определяются. Вещь, конечно, удобная и полезная - спору нет. Но ведь в Java тоже есть замыкания! Они реализуются с помощью анонимных классов. Анонимный класс имеет доступ к полям класса, в лексическом контексте которого он определен, а так же к переменным с модификатором final в лексическом контексте метода. Например:
public final calculateInSeparateThread(final URI uri) {
 // The expression "new Runnable() { ... }" is an anonymous class.
 Runnable runner = new Runnable() {
         void run() {
             // It can access final local variables:
             calculate(uri);
             // It can access private fields of the enclosing class:
             btnSave.setEnabled(true);
         }
     };
 new Thread(runner).start();
}
Согласен, в подобном синтаксисе есть некоторая громоздкость, но не говорите мне что в Java нет замыканий. Что же касается делегатов, то вполне можно обойтись без них. В C# delegate - это по сути указатель на метод какого-то объекта. Допустим, один объект представляет собой часть какой-то работы и не хочет завязываться на input/output, но нужно реализовать лог этой работы. Всё что нужно знать объекту - сигнатуру метода, который будет вести лог (например, выводить информацию о ходе процесса на консоль). То есть, объект ничего не будет знать о реализации данного метода, но будет его вызывать. Лично на мой взгляд подобная ситуация легко заменяется имплементацией объектом, предоставляющим вывод на консоль какого-либо интерфейса ILogger. Таким образом, классу выполняющему работу нужно будет знать только о существовании этого интерфейса. А чем это собственно, хуже? По-моему, это менее запутанная тропа чем какие-то сомнительные делегаты...

Generics

Generics в C# и Java реализованы действительно по-разному. Кто-то мне говорил что в Java генерики так сказать "формальные", то есть их там фактически нет. Я решил понять что он имеет ввиду. А выяснилось следующее. В Java они реализованы с использованием "затирания типа" (type erasure). Это значит, что представление о типа существует только на этапе компиляции, после которой компилятор заменяет тип во всех generic-объявлениях на Object. Затем компилятор добавляет приведения типов в нужные места. Причина реализации данного подхода состоит в требованиях совместимости кода, использующего Generics и старого кода, который их не поддерживает. Главная проблема с затиранием типа состоит в том, что информация о типе generic не доступна во время выполнения через Reflection или идентификацию типа во время выполнения. Другое последствие этого подхода в том, что generic типы структур данных должны всегда объявляться, используя объекты а не не примитивные типы. Таким образом нужно создавать Stack<Integer> вместо Stack<int> для работы с целыми числами.
В C# есть явная поддержка generics на уровне языка MSIL. Когда generic тип компилируется, произведенный MSIL содержит информацию об определенных типах. Во время выполнения, когда ссылка применена к generic типу (например, List<int>) система проверяет был ли осуществлён запрос типа или нет. Если тип уже запрашивался, то будет возвращен созданный предварительно определенный тип. В противном случае JIT компилятор создаст новый тип, заменив параметр generic типа в MSIL определенным типом (например заменит List<T> на List<int>). Нужно отметить, что, если требуемый тип - ссылочный, тогда параметр generic типа заменяется на Object. Однако, приведения типов во среде выполнения .NET при доступе к типам нет.
Ещё одним отличием generics в Java и в C# являются constraints(ограничения) на параметр типа. В Java всего одно ограничение - derivation constraint ограничение происхождения(можно указать что тип должен обязательно расширять какой-либо класс). В C# есть ещё 2 типа ограничений: можно указать, что generic тип должен обязательно иметь пустой конструктор(конструктор по-умолчанию), а так же наложить ограничение на то ссылочный ли это тип или простой тип.
Итак, в чём это принципиальное отличие которое делает C# языком с большой буквы в сравнении с Java? Лишь в том, что нельзя определить во время выполнения какой тип передан в generic в качестве параметра? Так ведь и с этим можно справиться более простыми методами. Например, можно опять же отнаследовать свой generic от класса, в котором определить метод:
protected Class<T> genericParameter;
//...
public Class<T> getGenericParameter() {
    return genericParameter;
}
Теперь для объекта MyGeneric<Integer> mygeneric я могу получить тип Integer вызвав: mygeneric.genericParameter(). Всё предельно просто и ясно.

partial classes

Угу. Есть такой момент - в C# можно тело класса раскидать на кучу файлов. В Java же всё строго - один класс - один файл. Да, не спорю, это немного стесняет свободу. Но что же делать - идеология Java нам советует дробить программу как только можно. Если класс разрастает до такой степени, что ему становится тесно в своём файле - значит это неправильный класс с точки зрения архитектуры, его следует разбить на подзадачи и вынести их в свои классы. А идеология Microsoft - это "давайте напишем такую штуку которая будет делать ВСЁ". И на тебе - всю функциональность давайте запихнём в один класс, вот будет прекрасно. Нет уж, ребята, по-моему, это лишнее и только затрудняет понимание архитектуры приложения, как, впрочем и все остальные вещи которые якобы выводят C# на несравнимо более высокий уровень.

conclusion

Ну что сказать. Разумеется, C# в отличие от Java предоставляет много "вкусностей". Но на мой взгляд они зачастую излишни и затрудняют понимание назначения кода, запутывают программиста. Конечно, я разобрал лишь малую толику отличий. В предлагаемой статье разобрано куда больше. Я не учёл такие моменты как: принудительный вызов GC (зачем?!); передача аргументов нессылочных типов по ссылке(излишне); различие между зубчатыми массивами и многомерными массивами(достаточно полезно, хотя и не столь принципиально); свойства(properties); препроцессорные директивы; перегрузка оператора []; недвусмысленная имплементация интерфейсов; дружественные пространства имён; и ещё многая прочая муть (подробнее - в статье).
В Java многое из этого не было реализовано именно из-за того, что эти вещи часто затрудняли читабельность кода в C++. C# больше похож на C++ чем Java - это факт. Но становится ли он от этого на ступень выше Java - вопрос очень спорный. Это тоже самое что сказать "Марс лучше Юпитера" или, скажем, "Интерференция на ступень выше дифракции" или "Берёза лучше Дуба"... Я не противник C#. Согласен, он предоставляет множество удобств, которых в Java нет. Но они не столь существенны. Оба языка дают широкие возможности для выражения собственных мыслей, и нет проблем, которые можно было бы решить на C# и нельзя на Java.

[to be continued...]

Ярлыки: , ,

-->