使用Hibernate导入和规范化XML

时间:2009-07-12 15:43:18

标签: java xml hibernate

使用Hibernate将xml导入数据库时​​,是否有办法解析由逗号分隔值组成的属性以填充相关表格?

在这个(有点混淆)的例子中,我有一个xml文件,每行代表一个Person。 Person有一个Hobbies属性,其中包含逗号分隔的值列表。人与爱的关系是多对多的。实际上,我有数据要处理。

当将每个Person导入PEOPLE表时,我想将每个Hobby添加到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 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()方法中我用Hobby实例填充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表中的结果内容。

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 个答案:

答案 0 :(得分: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

这样就可以更容易地以您希望的方式导入Hibernate。

答案 1 :(得分:1)

当Hibernate读取hobbies属性时,它只是将其作为文本直接存储到Person表中。此时无法知道hobbiesSet,因为您填充集合的唯一时间是将对象再次从数据库中读回。但由于该数据集从未在数据库中填充,因此无效。

你配置爱好和爱好的方式令人困惑,我不建议混合爱好和爱好。我强烈建议您自己将XML读入对象模型,包括拆分业余爱好者字符串,然后使用业余爱好集合以正常方式将手动构造的对象持久化为Hibernate。

答案 2 :(得分:0)

我找到了部分解决方案,并认为值得在此处录制。 不幸的是,如果list-attribute中存在重复的键,则需要对元素执行合并而不是保存,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>

答案 3 :(得分:0)

我们尝试在同一个应用程序中使用Hibernate的DOM4J和POJO实体模式。也许它现在已经成熟了,但我们只有DOM4J实体模式的问题。

我建议在你的POJO中使用Hibernate,并使用类似XStream或原始DOM4J的东西来与POJO进行XML序列化。