2009-07-12 2 views
3

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

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

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

Я установил свои файлы сопоставления с двунаправленными ассоциациями, и Hibernate, как представляется, построит таблицы, как я ожидал (подробности ниже), однако я не вижу, какой механизм я могу использовать для извлечения/заполнения HOBBIES и 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 Децл):

<?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 с экземплярами Хобби:

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_ID   |NAME  |HOBBIES    
------------------------------------------------------- 
1     |Dave  |drinking, walking  
2     |Geoff  |football, ballet  
3     |Anne  |walking, karate  
4     |Frank  |karate, cross-stitch 

... и эти пустые ХОББИ и PEOPLE_HOBBIES столы:

ХОББИ:

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

0 rows selected 

PEOPLE_HOBBIES:

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

0 rows selected 
+0

повторно «в setHobbies() метод, который я заселить hobbiesSet с хобби, например» я не вижу, что я просто вижу общественные недействительные setHobbies (окончательное Струнные хобби) { this.hobbies = хобби; } – Mark

ответ

1

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

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

+0

Hibernate 3 поддерживает XML-сопоставление, поэтому мне представляется разумным поддерживать этот вид обработки. Я знаю, что могу сворачивать свои собственные, но такого рода поражения используют ORM. Реализация setHobbies() была частью моего исследования, чтобы увидеть, как работает сопоставление, я полагаю, что это не очень хороший способ справиться с настройкой свойства, но, как вы говорите, он не вызывается при обработке XML (didn ' я знаю, пока не попытаюсь), так что это не очень актуально. Я удалю содержимое метода, чтобы избежать путаницы. –

+1

Режим сохранения XML отлично подходит для хранения XML без изменения его структуры.Но вы меняете структуру, и поэтому нет смысла хранить ее в одной структуре и пытаться восстановить ее в другой. – skaffman

0

Я нашел частичное решение и подумал, что стоит записать его здесь. К сожалению, если в атрибуте 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); 
} 

Как только я обновил файлы сопоставления для обработки дочерних элементов и переименовал соответствующие методы в объектах домена, он сохраняет связанные данные (, пока t здесь нет дубликатов в хобби 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> 
2

Вы могли бы рассмотреть предварительной обработки вашего 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 - Best way to split and render comma separated text as HTML для примера.

Это должно облегчить импорт в спящий режим по своему желанию.

+0

Мое «частичное решение» эффективно делает это в памяти. Похоже, что это проблема спящего режима, где поддержка EntityMode.DOM4J еще не завершена. –

+0

Я заставлю «поддержка EntityMode.DOM4J еще не завершена». Мы столкнулись с проблемами Hibernate JIRA HHH-422, HHH-1039, HHH-1265 и HHH-1999, когда пытались использовать его пару лет назад. Все эти проблемы все еще открыты сегодня. –

0

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

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

Смежные вопросы