Импорт и нормализация XML с помощью Hibernate - PullRequest
3 голосов
/ 12 июля 2009

При импорте xml в БД с помощью Hibernate, есть ли способ разрешить атрибут, состоящий из значений, разделенных запятыми, для заполнения связанных таблиц?

В этом (несколько запутанном) примере у меня есть файл xml, каждая строка которого представляет человека. У Person есть свойство Hobbies, которое содержит список значений через запятую. Отношения человек-хобби много-много. На самом деле у меня есть данные для обработки.

При импорте каждого человека в таблицу PEOPLE я хотел бы добавить каждое хобби в таблицу HOBBIES (игнорируя дубликаты), а затем добавить отображение в таблицу PEOPLE_HOBBIES.

Я установил свои файлы сопоставления с двунаправленными ассоциациями, и Hibernate, кажется, создает таблицы так, как я ожидал (подробности ниже), однако я не вижу, какой механизм я могу использовать для извлечения / заполнения Хобби и PEOPLE_HOBBIES при обработке PEOPLE.

С благодарностью получены все справочные и / или справочные материалы RTFM.

Это файл, который я обрабатываю (people.xml):

<People>
  <Person Id="1" Name="Dave" Hobbies="drinking, walking"/>
  <Person Id="2" Name="Geoff" Hobbies="football, ballet"/>
  <Person Id="3" Name="Anne" Hobbies="walking, karate"/>
  <Person Id="4" Name="Frank" Hobbies="karate, cross-stitch"/>
</People>

Person.hbm.xml (без учета xml decl):

<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="name.seller.rich.hobby">  
  <class name="Person" node="Person" table="PEOPLE">
    <id name="id" node="@Id" column="PEOPLE_ID"/>
    <property name="name" node="@Name" column="NAME" type="string"/>
    <property name="hobbies" node="@Hobbies" column="HOBBIES" type="string"/>
    <set name="hobbiesSet" table="PEOPLE_HOBBIES">
      <key column="PEOPLE_ID"/>
      <many-to-many column="HOBBY" class="Hobby"/>
    </set>
  </class>
</hibernate-mapping>

Hobby.hbm.xml:

<hibernate-mapping package="name.seller.rich.hobby">  
  <class name="Hobby" node="Hobby" table="HOBBIES">
    <id name="hobby" column="HOBBY" type="string"/>
    <set name="people" table="PEOPLE_HOBBIES" inverse="true">
      <key column="HOBBY"/>
      <many-to-many column="PEOPLE_ID" class="Person"/>
    </set>
  </class>
</hibernate-mapping>

Это класс Person, в методе setHobbies () я заполняю hobbiesSet экземплярами Hobby:

package name.seller.rich.hobby;

import java.util.HashSet;
import java.util.Set;

public class Person {

    private long id;

    private String name;

    private String hobbies;

    private Set hobbiesSet = new HashSet();

    public String getHobbies() {
        return hobbies;
    }

    public Set getHobbiesSet() {
        if (hobbiesSet == null) {
            hobbiesSet = new HashSet();
        }
        return hobbiesSet;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setHobbies(final String hobbies) {
        this.hobbies = hobbies;
    }

    public void setHobbiesSet(final Set hobbiesSet) {
        this.hobbiesSet = hobbiesSet;
    }

    public void setId(final long id) {
        this.id = id;
    }

    public void setName(final String name) {
        this.name = name;
    }
}

Это код, который я использую для обработки файла:

package name.seller.rich.hobby;

import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

public class DataImporter {

    public static void main(final String[] args) {
        File baseDir = new File("C:\\workspaces\\hobby");
        DataImporter importer = new DataImporter();
        Configuration config = importer.setupDb(baseDir);

        if (config != null) {
            importer.importContents(new File(baseDir, "people.xml"), config);
        }
    }

    private void importContents(final File file, final Configuration config) {
        SessionFactory sessionFactory = config.buildSessionFactory();
        Session session = sessionFactory.openSession();    
        Transaction tx = session.beginTransaction();
        Session dom4jSession = session.getSession(EntityMode.DOM4J);

        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(file);

            List list = document.selectNodes("//Person");
            Iterator iter = list.iterator();

            while (iter.hasNext()) {
                Object personObj = iter.next();
                dom4jSession.save(Person.class.getName(), personObj);
            }

            session.flush();
            tx.commit();
            session.close();
        } catch (HibernateException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private Configuration setupDb(final File baseDir) throws HibernateException {
        Configuration cfg = new Configuration();
        cfg.addFile(new File(baseDir, "name/seller/rich/hobby/Person.hbm.xml"));
        cfg.addFile(new File(baseDir, "name/seller/rich/hobby/Hobby.hbm.xml"));

        SchemaExport export = new SchemaExport(cfg);

        export.setOutputFile("hobbyDB.txt");
        export.execute(false, true, false, false);
        return cfg;
    }
}

Это итоговое содержание в таблице PEOPLE.

PEOPLE_ID           |NAME        |HOBBIES              
-------------------------------------------------------
1                   |Dave        |drinking, walking    
2                   |Geoff       |football, ballet     
3                   |Anne        |walking, karate      
4                   |Frank       |karate, cross-stitch 

... а это пустые таблицы HOBBIES и PEOPLE_HOBBIES:

ХОББИ:

HOBBY
----------------------

0 rows selected

PEOPLE_HOBBIES:

PEOPLE_ID           |HOBBY
---------------------------------------

0 rows selected

Ответы [ 4 ]

2 голосов
/ 16 июля 2009

Возможно, вы захотите предварительно обработать ваш xml во что-то более подходящее. Как правило, лучше представлять списки вещей в виде элементов, чем значений атрибутов, разделенных запятыми.

Например:

<People>
  <Person Id="1" Name="Dave" Hobbies="drinking, walking"/>
  <Person Id="2" Name="Geoff" Hobbies="football, ballet"/>
  <Person Id="3" Name="Anne" Hobbies="walking, karate"/>
  <Person Id="4" Name="Frank" Hobbies="karate, cross-stitch"/>
</People>

Будет лучше как:

<People>
  <Person Id="1" Name="Dave">
    <Hobbies>
      <Hobby>drinking</Hobby>
      <Hobby>walking</Hobby>
    </Hobbies>
  </Person>

  ...
</People>

Вы можете сделать это с помощью сценария XSLT - см. XSLT - Лучший способ разбить и отобразить текст через запятую как HTML для примера.

Это должно облегчить импорт в Hibernate так, как вы хотите.

1 голос
/ 12 июля 2009

Когда Hibernate считывает атрибут hobbies, он просто сохраняет его в виде текста непосредственно в таблице Person. На данный момент у него нет возможности узнать о hobbiesSet, поскольку единственный раз, когда вы заполняете набор, это когда объект снова считывается из базы данных. Но так как набор никогда не заполнялся в базе данных, он не работает.

То, как вы настроили как хобби, так и хобби. Набор сбивает с толку, и я бы не советовал смешивать хобби и хобби. Я настоятельно рекомендую вам самостоятельно прочитать XML-код в объектной модели, включая разбиение строки хобби, а затем обычным способом сохранить созданные вручную объекты в Hibernate, используя коллекцию хобби.

0 голосов
/ 15 декабря 2009

Некоторое время назад мы пытались использовать режимы сущностей DOM4J и POJO Hibernate в одном и том же приложении. Возможно, к настоящему моменту он уже созрел, но у нас не было ничего, кроме проблем с режимом сущности DOM4J.

Я бы рекомендовал использовать Hibernate с вашими POJO и использовать что-то вроде XStream или raw DOM4J для выполнения вашей XML-сериализации в и из POJO.

0 голосов
/ 13 июля 2009

Я нашел частичное решение и подумал, что стоит записать его здесь. К сожалению, если в атрибуте list есть повторяющиеся ключи, вам нужно выполнить слияние, а не сохранение элемента, и это еще не поддерживается для EntityMode.DOM4J. Это комментарий от org.hibernate.type.CollectionType.replaceElements ():

// TODO: пока не работает для EntityMode.DOM4J!

Вы можете добавить ElementHandler в SAXReader для обработки каждого элемента и динамического превращения атрибутов в дочерние элементы, это моя реализация:

SAXReader saxReader = new SAXReader();
saxReader.addHandler("/People/Person", new ElementHandler() {

    public void onEnd(final ElementPath elementPath) {
        Element element = elementPath.getCurrent();
        Attribute hobbyAttribute = element.attribute("Hobbies");

        if (hobbyAttribute != null) {
            String hobbies = hobbyAttribute.getValue();
            Element hobbiesList = new DefaultElement("Hobbies");
            element.add(hobbiesList);
            String[] hobbiesArray = hobbies.split(",");

            for (String hobby : hobbiesArray) {
                if (hobby.trim().length() > 0) {
                    Element hobbyElement = new DefaultElement("Hobby");
                    hobbiesList.add(hobbyElement);
                    Element idElement = new DefaultElement("id");
                    hobbyElement.add(idElement);
                    idElement.setText(hobby.trim());
                }
            }
        }
    }

    public void onStart(final ElementPath elementPath) {
        //no-op;
    }
});

И последний цикл изменяется следующим образом:

while (iter.hasNext()) {
    Object peopleObj = iter.next();
    dom4jSession.merge(Person.class.getName(), peopleObj);
}

Как только я обновил файлы сопоставления для обработки дочерних элементов и переименовал соответствующие методы в объектах домена, он сохраняет связанные данные (, пока в хобби natch нет дубликатов).

Обновлен Hobby.hbm.xml:

<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="name.seller.rich.hobby">  
  <class name="Hobby" node="Hobby" table="HOBBIES">
    <!--id name="id" column="HOBBY_ID">
      <generator class="native"/>
    </id-->
    <id name="id" column="HOBBY_ID" type="string"/>
    <set name="people" table="PEOPLE_HOBBIES" inverse="true">
      <key column="HOBBY_ID"/>
      <many-to-many column="PEOPLE_ID" class="Person"/>
    </set>
  </class>
</hibernate-mapping>

Обновлено Person.hbm.xml:

<hibernate-mapping package="name.seller.rich.hobby">  
  <class name="Person" node="Person" table="PEOPLE">
    <id name="id" node="@Id" column="PEOPLE_ID"/>
    <property name="name" node="@Name" column="NAME" type="string"/>
    <!-- property name="hobbies" node="@Hobbies" column="HOBBIES" type="string"/-->
    <set name="hobbies" node="Hobbies" table="PEOPLE_HOBBIES" cascade="save-update,persist">
    <key column="PEOPLE_ID"/>
    <many-to-many column="HOBBY_ID" class="Hobby"/>
    </set>
  </class>
</hibernate-mapping>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...