[info]ru_java


ru.java

все о языке программирования java


Previous Entry в избранное рассказать другу Next Entry
Вопрос по Hibernate
крокодил
[info]alex_mq0 пишет в [info]ru_java
Появился вопрос по Hibernate:

Таблица:

create table tags (
id integer,
objectid integer,
objecttype integer,
tag varchar(200)
)

Ключи созданы по следующим полям:
ID - primary key
objectid+objecttype
objectid+objecttype+tag

map file

<hibernate-mapping>
<class name="logic.tags.Tag" table="tags" lazy="true">
<id name="ID" column="ID" type="long">
<generator class="sequence">
<param name="sequence">tags_seq</param>
</generator>
</id>
<property name="objectID" column="ObjectID"/>
<property name="objectType" column="ObjectType"/>
<property name="tagName" column="Tag"/>
</class>
</hibernate-mapping>


class файл

public class Tag {

private String tagName;
private Long objectID;
private Long objectType;
private Long ID;

public Long getID() {
return ID;
}

private void setID(Long ID) {
this.ID = ID;
}

/**
* Конструктор тега
* @param tagName тег
* @param objectID идентификатор объекта к которому тег привязан
* @param objectType тип объекта
*/
public Tag(String tagName, Long objectID, Long objectType) {
this.tagName = tagName;
this.objectID = objectID;
this.objectType = objectType;
}

/**
* Конструктор тега
* @param ID ID
* @param tagName тег
* @param objectID идентификатор объекта к которому тег привязан
* @param objectType тип объекта
*/
public Tag(Long ID,String tagName, Long objectID, Long objectType) {
this.tagName = tagName;
this.objectID = objectID;
this.objectType = objectType;
this.ID = ID;
}

public Tag(){

}

public void setTagName(String tagName) {
this.tagName = tagName;
}

public void setObjectID(Long objectID) {
this.objectID = objectID;
}

public void setObjectType(Long objectType) {
this.objectType = objectType;
}

public String getTagName() {
return tagName;
}

public Long getObjectID() {
return objectID;
}

public Long getObjectType() {
return objectType;
}
}

в таблицу загружаю данные следующим образом:

int objectCount = 100;
int tagCount = 10;

con.createStatement().executeUpdate("delete from tags");
Statement stat = con.createStatement();
for (int i=0;i<objectCount;i++){
for (int j=0;j<tagCount;j++){
String tag = //здесь генерирую случайную строку;
stat.executeUpdate("insert into tags(ID,ObjectID,ObjectType,Tag) values("+tagCount*i+j+","+i+",1,'"+tag+"')");
}
}

Код загрузки Hibernate данных

        HashMap<Integer,List> data = new HashMap<Integer, List>();
        long startHibernate = System.currentTimeMillis();
        for (int i=0;i<objectCount;i++){
            String query = "from Tag " +
                    "where objectID = :objectID and objectType=:objectType";
            Query query1 = session.createQuery(query);
            query1.setLong("objectID",(long)i);
            query1.setLong("objectType",(long)1);
            List list = query1.list();
            data.put(i,list);
        }
        long stopHibernate = System.currentTimeMillis();
        System.out.println(new Date()+"Time Count:"+(stopHibernate-startHibernate));
 

Измеряю результат для загрузки напрямую через JDBC... результат немного удручающий: для 1`000`000 тегов при 10 тегах на объект я не дождался загрузки через Hibernate. JDBC загружалось чуть больше минуты.

Теперь вопрос:
Адекватные ли данные я получил? Можно ли как-нибудь оптимизировать производительность, и вообще предназначен ли Hibernate для загрузки такого объема данных?



(комментировать)
Код "загрузки" через Hibernate - в студию.

Гм забыл.... прошу прощения обновил пост

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

А почему вы думаете, что вопрос о количестве сессий имеет значение?
И опишите нормально, что вы делать собираетесь, или мы будем ещё долго выяснять разные интригующие подробности.

Вообще не интригующие ;) Прошу прощения, что не указал сразу
Это прототип фабрики для загрузки тегов объекта. Те вызывается метод вида:
public List getTagForObject(Object o);
который возвращает список тегов. именно это я и пытаюсь изобразить =) Обращение к фабрике, вероятнее всего, будет происходить в несколько потоков. Пока я проверил как это выглядит для одного потока.... меня очень удивил такой результат =((( Но ошибку я найти не могу.

Давайте сначала. Вот этот фрагмент
for (int i=0;i<objectCount;i++){ предполагает загрузку всех тэгов из БД сразу. Это, очевидно, НЕ нормальный код для использования в короткоживущей сессии. Такой код имеет смысл, например, при cache preloading, когда приложение, очевидно, ещё не готово обслуживать кучу пользователей сразу. Я не могу даже приближённо представить себе, какой смысл может имет _параллельная_ загрузка одного и того же огромного набора объектов в разные области оперативки (целесообразность вообще загрузки такого массива данных в память опустим пока). Если вы хотите реальной помощи, опишите, какая перед вами стоит задача.

Можно считать что это тестовый вариант случайного разброса ID.
Большое количество пользователей запрашивает теги для различных объектов.

Я что-то наверное не понял. Судя по коду, ваш пользователь запрашивает миллион тэгов. Если это не так, пожалуйста, исправьте код.

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

Разница есть. Hibernate session имеет встроенный session-level cache, а вы пытаетесь работать с миллионом (тысячей?) объектов в одной сессии.
1. Разница между миллионом и тысячей в таком случае очень существенна.
2. Сам принцип дурной по самое немогу. Вы НЕ моделируете реальную ситуацию. Каждый пользователь будет иметь _свою_ транзакцию ergo _свою_ hibernate сессию. Вот транзакции-то и надо запускать в цикле/потоками или ещё как.

вопрос не совсем в тему: а second-level он общий или нет?

Спасибо...
(Ответы заморожены) (Уровень выше) (Ветвь дискуссии)

Вы мне ЧЕРЕЗВЫЧАЙНО сильно помогли...
(Ответы заморожены) (Уровень выше) (Ветвь дискуссии)

Ещё раз: или давайте _реальный_ код, что будут делать ваши юзеры, или внимательно ознакомьтесь с hibernate reference (лдя начала), чтобы убедиться, что ваша модель (бенчмарк) имеет какое-то отношение к реальной ситуации.

После перепрочтения поста, я вообще выпал в осадок. Вы вставляете тысячу тэгов, а говорите о миллионе.

Константы можно менять ;) и сделать objectCount = 100000 :)

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

предназначен. индексация данных.

objectid+objecttype - вот этот лишний. по крайней мере для оракла и майсиквела.

Ваш HashMap довольно часто реаллокейтится. Для миллиона итераций это вполне заметно.

Разве createQuery нужно делать в каждой итерации?

Очень смущает следующая конструкция:

String query = "from Tag " +
"where objectID = :objectID and objectType=:objectType";

В 1.4 это наверное будет "оптимайзнуто" в StringBuffer.append(). и так миллион раз. хотя складываете вы константы. В 1.5 это может будет StringBuilder.

и это мы еще не добрались до работы с базой и особенностей хибернейта. об этом вам выше уже написали.

проглядел с конкорнацией строк... исправляю проверяю

CreateQuery нужно делать для каждой операции... скажем так, такая особенность окружения... все остальное лучше не трогать и принять как особенность окружения и цель работы проверить скорость и оптимизировать именно в этом окружении... я пытался рассказать для чего мне это нужно... но сейчас понимаю что лучше не объяснять... так нужно. Нужно проверить скорость работы именно для создаваемых запросов каждый раз...
Спасибо за строчки =))) прирост очень значительный... мне прям стыдно =(((( проглядел такую мелочь =((((

Если у HashMap выставишь правильные capacity и factor, то наверняка заметишь значительный прирост.

Может подскажите место где можно прочитать про это?

как это ни банально - http://java.sun.com/j2se/1.4.2/docs/api/java/util/HashMap.html

и в сырцых самого класса, есть замечательные комментарии от Джошуа Блоха.

из методов put и addEntry можно легко понять, когда происходит resize и полное копирование всех элементов мэпки.

> В 1.4 это наверное будет "оптимайзнуто" в StringBuffer.append()

Зачем гадать, дизассемблируйте. Или прочтите JLS. Будете удивлены. Никакого аппенда.

> по крайней мере для оракла и майсиквела.

Ага, не указана. Есть ещё вариант оптимизации -- использовать встроенные memory-only DB типа hsqldb. Тогда у чела вообще тестирование памяти будет. :)

> Зачем гадать, дизассемблируйте. Или прочтите JLS. Будете удивлены. Никакого аппенда.

Что, неуж-то константа?

(комментировать)