正在加载...

hibernate入门使用系列 3-- xml关系映射篇(中)

上篇讲了1:1,那么这次继续讲1:n和n:1。

这次用到的例子是Father和child之间的关系。一个father可以有n个child,但是1个child只有一个father。这里只说生父。至于其他的继父、养父、干爹等等,不再范围之内。

好吧。还是同前面的一样。现建立实体模型如下:

One2Many

根据模型创建数据库。sql脚本如下:

use HibernateQuickUse;
drop table if exists Child;
drop table if exists Father;

create table Father (
    id varchar(32) primary key,
    name varchar(32) not null
);

create table Child (
    id varchar(32) primary key,
    name varchar(128) not null,
    father_id varchar(32) not null,
    foreign key(father_id) references Father(id)
);

根据模型创建java对象。

Father.java:

package org.py.hib.relation.one2many;

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

/**
 * Father entity.
 */

@SuppressWarnings("serial")
public class Father implements java.io.Serializable
{
    private String id;

    private String name;

    private Set children = new HashSet(0);

    public Father()
    {
    }

    public String getId()
    {
        return this.id;
    }

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

    public String getName()
    {
        return this.name;
    }

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

    public Set getChildren()
    {
        return children;
    }

    public void setChildren(Set children)
    {
        this.children = children;
    }
}

Child.java:

package org.py.hib.relation.one2many;

/**
 * Child entity.
 * @author MyEclipse Persistence Tools
 */

@SuppressWarnings("serial")
public class Child implements java.io.Serializable
{
    private String id;

    private String name;

    private Father father;

    public Child()
    {
    }

    public String getId()
    {
        return this.id;
    }

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

    public Father getFather()
    {
        return this.father;
    }

    public void setFather(Father father)
    {
        this.father = father;
    }

    public String getName()
    {
        return this.name;
    }

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

}

映射文件如下:

Father.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>  
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  

<hibernate-mapping>  
    <class name="org.py.hib.relation.one2many.Father" table="father">  
        <id name="id" type="java.lang.String" column="id" length="32">  
            <generator class="uuid" />  
        </id>  

        <property name="name" type="java.lang.String" column="name" length="32" not-null="true"/>  

        <set name="children" table="child" cascade="all" inverse="true">  
            <key column="father_id" />  
            <one-to-many class="org.py.hib.relation.one2many.Child" />  
        </set>  
    </class>  
</hibernate-mapping>

这里要说说 "set" 这个标签里面的内容。

"name"是Father里面的属性的名字。

"table"表示它对应的是数据库中的哪个表。

cascade="all" 表示所有的操作都级联操作。

"inverse"表示关系的维护由谁来执行。true表示不由自己执行,而有对应的另外一方执行。false则相反,表示由自己维护关系。这里设置成 true 是由原因的。如果说把它设置成为false,那么就由他来维护关系了。

这里得说一下inverse属性的问题。在one-to-many中,如果关系由one来维护,那么会很麻烦,性能也会很低。每次对many一方的一条记录进行增、删、改 时都会多一次update操作。原因很简单,因为关系的维护设置在了one这一方,所以对many的每一次操作,one这一方都要维护一次双方的关系。

这个就好像皇帝和老百姓的关系。试问,是来一个老百姓,皇帝就宣布他是我的子民,还是由老百姓直接选择做那个皇帝的子民更加有效率呢?呵呵。不知道这个例子大家有没有明白。关于inverse的更具体的说明,在javaeye上搜一下,就会发现有很多。这里推荐一篇,我认为讲得很明白的: 主题:inverse。

"key" 中的 "column" 表示在table(这里的table是child)中, 跟Father关联的字段名称。这里是"father_id"。可以看看开始的sql脚本。

one-to-many 表示father和children的关系。class则表示是同哪个类是这种关系。

Child.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>  
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  

<hibernate-mapping>  
    <class name="org.py.hib.relation.one2many.Child" table="child">  
        <id name="id" type="java.lang.String" column="id" length="32" >  
            <generator class="uuid" />  
        </id>  
        <property name="name" type="java.lang.String" column="name" length="128" not-null="true"/>  

        <many-to-one name="father" class="org.py.hib.relation.one2many.Father" column="father_id" />  
    </class>  
</hibernate-mapping>

这个里面主要就是多了一个many-to-one,表示child 和 father 的关系是"many-to-one"

测试代码如下:

One2ManyTest.java

package org.py.hib.relation.one2many;

import java.util.Set;

import junit.framework.Assert;
import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;

public class One2ManyTest extends TestCase
{
    private SessionFactory factory;

    private static final String[] childname = new String[] { "child_1", "child_2", "child_3" };

    private static final String[] newchildname = new String[] { "new_child_1", "new_child_2", "new_child_3" };

    @Before
    public void setUp() throws Exception
    {
        Configuration conf = new Configuration().configure();
        factory = conf.buildSessionFactory();
    }

    /**
     * 测试添加
     * @throws Exception
     */
    public void testSave() throws Exception
    {
        System.out.println("\n=== test save ===");

        Father father = new Father();
        father.setName("Father_1");

        Child child1 = new Child();
        child1.setName(childname[0]);

        Child child2 = new Child();
        child2.setName(childname[1]);

        Child child3 = new Child();
        child3.setName(childname[2]);

        father.getChildren().add(child1);
        father.getChildren().add(child2);
        father.getChildren().add(child3);

        child1.setFather(father);
        child2.setFather(father);
        child3.setFather(father);

        Session session = null;
        Transaction tran = null;
        try
        {
            session = factory.openSession();
            tran = session.beginTransaction();
            session.save(father);

            tran.commit();

            Assert.assertNotNull(father.getId());

            Assert.assertNotNull(child1.getId());
            Assert.assertNotNull(child2.getId());
            Assert.assertNotNull(child3.getId());

        } catch (Exception ex)
        {
            tran.rollback();
            throw ex;
        } finally
        {
            if (session != null)
            {
                try
                {
                    session.close();
                } catch (Exception ex)
                {
                    // nothing to do
                } finally
                {
                    if (session != null)
                        session = null;
                }
            }
        }
    }

    private boolean isChildrenName(String name)
    {
        for (String n : childname)
        {
            if (n.equals(name))
                return true;
        }

        return false;
    }

    private boolean isNewChildrenName(String name)
    {
        for (String n : newchildname)
        {
            if (n.equals(name))
                return true;
        }

        return false;
    }

    /**
     * 测试查询
     * @throws Exception
     */
    public void testFind() throws Exception
    {
        System.out.println("\n=== test find ===");
        Session session = null;
        try
        {
            session = factory.openSession();
            Father father = (Father) session.createQuery("from Father").list().get(0);

            Assert.assertNotNull(father.getId());
            Assert.assertEquals("Father_1", father.getName());

            Set children = father.getChildren();
            for (Child child : children)
            {
                Assert.assertEquals(child.getFather(), father);

                Assert.assertNotNull(child.getId());

                Assert.assertTrue(isChildrenName(child.getName()));
            }
        } catch (Exception ex)
        {
            throw ex;
        } finally
        {
            if (session != null)
            {
                try
                {
                    session.close();
                } catch (Exception ex)
                {
                    // nothing to do
                } finally
                {
                    if (session != null)
                        session = null;
                }
            }
        }
    }

    /**
     * 测试修改
     * @throws Exception
     */
    public void testModify() throws Exception
    {
        System.out.println("\n=== test modify ===");
        Session session = null;
        Transaction tran = null;
        try
        {
            session = factory.openSession();
            tran = session.beginTransaction();

            Father father = (Father) session.createQuery("from Father").list().get(0);
            father.setName("Father_2"); // 修改用户名 = m_name2.(原来用户名= m_name)

            Set children = father.getChildren();
            int i = 0;
            for (Child child : children)
            {
                child.setName(newchildname[i++]);
            }

            tran.commit();

        } catch (Exception ex)
        {
            throw ex;
        } finally
        {
            if (session != null)
            {
                try
                {
                    session.close();
                } catch (Exception ex)
                {
                    // nothing to do
                } finally
                {
                    if (session != null)
                        session = null;
                }
            }
        }

        /*
         * 修改后再查询
         */
        System.out.println("\n=== test find after modify ===");
        try
        {
            session = factory.openSession();
            Father father = (Father) session.createQuery("from Father").list().get(0);

            Assert.assertNotNull(father.getId());
            Assert.assertEquals("Father_2", father.getName());

            Set children = father.getChildren();

            for (Child child : children)
            {
                Assert.assertEquals(child.getFather(), father);

                Assert.assertNotNull(child.getId());

                Assert.assertTrue(isNewChildrenName(child.getName()));
            }

        } catch (Exception ex)
        {
            throw ex;
        } finally
        {
            if (session != null)
            {
                try
                {
                    session.close();
                } catch (Exception ex)
                {
                    // nothing to do
                } finally
                {
                    if (session != null)
                        session = null;
                }
            }
        }
    }

    /**
     * 测试删除
     * @throws Exception
     */
    public void testDelete() throws Exception
    {
        System.out.println("\n=== test delete ===");
        Session session = null;
        Transaction tran = null;
        try
        {
            session = factory.openSession();
            tran = session.beginTransaction();

            Father father = (Father) session.createQuery("from Father").list().get(0);
            session.delete(father);
            tran.commit();

        } catch (Exception ex)
        {
            throw ex;
        } finally
        {
            if (session != null)
            {
                try
                {
                    session.close();
                } catch (Exception ex)
                {
                    // nothing to do
                } finally
                {
                    if (session != null)
                        session = null;
                }
            }
        }

        /*
         * 删除后再查询
         */
        System.out.println("\n=== test find after delete ===");
        try
        {
            session = factory.openSession();

            Integer num = (Integer) session.createQuery("from Father").list().size();
            Assert.assertEquals(0, num.intValue());

            num = (Integer) session.createQuery("from Child").list().size();
            Assert.assertEquals(0, num.intValue());

        } catch (Exception ex)
        {
            throw ex;
        } finally
        {
            if (session != null)
            {
                try
                {
                    session.close();
                } catch (Exception ex)
                {
                    // nothing to do
                } finally
                {
                    if (session != null)
                        session = null;
                }
            }
        }
    }

    /**
     * 
     */
    @After
    public void tearDown() throws Exception
    {
        factory.close();
    }

}

这里不得不再重申以下 one-to-many 中 inverse 关系的维护问题。 在one-to-many中,把inverse放到many中来维护是一个好的习惯。大家可以把上面的inverse改成false,看看会发生什么情况。

在inverse=true的时候,输出结果如下:

=== test save ===
Hibernate: insert into father (name, id) values (?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)

=== test find ===
Hibernate: select father0_.id as id13_, father0_.name as name13_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id14_0_, children0_.name as name14_0_, children0_.father_id as father3_14_0_ from child children0_ where children0_.father_id=?

=== test modify ===
Hibernate: select father0_.id as id23_, father0_.name as name23_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id24_0_, children0_.name as name24_0_, children0_.father_id as father3_24_0_ from child children0_ where children0_.father_id=?
Hibernate: update father set name=? where id=?
Hibernate: update child set name=?, father_id=? where id=?
Hibernate: update child set name=?, father_id=? where id=?
Hibernate: update child set name=?, father_id=? where id=?

=== test find after modify ===
Hibernate: select father0_.id as id23_, father0_.name as name23_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id24_0_, children0_.name as name24_0_, children0_.father_id as father3_24_0_ from child children0_ where children0_.father_id=?

=== test delete ===
Hibernate: select father0_.id as id33_, father0_.name as name33_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id34_0_, children0_.name as name34_0_, children0_.father_id as father3_34_0_ from child children0_ where children0_.father_id=?
Hibernate: delete from child where id=?
Hibernate: delete from child where id=?
Hibernate: delete from child where id=?
Hibernate: delete from father where id=?

=== test find after delete ===
Hibernate: select father0_.id as id33_, father0_.name as name33_ from father father0_
Hibernate: select child0_.id as id34_, child0_.name as name34_, child0_.father_id as father3_34_ from child child0_

而改成 inverse=false后,testDelete()是没法通过的。输出如下:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.

=== test save ===
Hibernate: insert into father (name, id) values (?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: update child set father_id=? where id=?
Hibernate: update child set father_id=? where id=?
Hibernate: update child set father_id=? where id=?

=== test find ===
Hibernate: select father0_.id as id13_, father0_.name as name13_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id14_0_, children0_.name as name14_0_, children0_.father_id as father3_14_0_ from child children0_ where children0_.father_id=?

=== test modify ===
Hibernate: select father0_.id as id23_, father0_.name as name23_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id24_0_, children0_.name as name24_0_, children0_.father_id as father3_24_0_ from child children0_ where children0_.father_id=?
Hibernate: update father set name=? where id=?
Hibernate: update child set name=?, father_id=? where id=?
Hibernate: update child set name=?, father_id=? where id=?
Hibernate: update child set name=?, father_id=? where id=?

=== test find after modify ===
Hibernate: select father0_.id as id23_, father0_.name as name23_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id24_0_, children0_.name as name24_0_, children0_.father_id as father3_24_0_ from child children0_ where children0_.father_id=?

=== test delete ===
Hibernate: select father0_.id as id33_, father0_.name as name33_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id34_0_, children0_.name as name34_0_, children0_.father_id as father3_34_0_ from child children0_ where children0_.father_id=?
Hibernate: update child set father_id=null where father_id=?

错误信息如下:

出错信息 具体的出错原因是:违反了非空约束。

得修改sql脚本,把Child的建表脚本中的:

father_id varchar(32) not null,

修改成为:

father_id varchar(32),

才能通过。这个时候输出的结果是:

log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.

=== test save ===
Hibernate: insert into father (name, id) values (?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: insert into child (name, father_id, id) values (?, ?, ?)
Hibernate: update child set father_id=? where id=?
Hibernate: update child set father_id=? where id=?
Hibernate: update child set father_id=? where id=?

=== test find ===
Hibernate: select father0_.id as id13_, father0_.name as name13_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id14_0_, children0_.name as name14_0_, children0_.father_id as father3_14_0_ from child children0_ where children0_.father_id=?

=== test modify ===
Hibernate: select father0_.id as id23_, father0_.name as name23_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id24_0_, children0_.name as name24_0_, children0_.father_id as father3_24_0_ from child children0_ where children0_.father_id=?
Hibernate: update father set name=? where id=?
Hibernate: update child set name=?, father_id=? where id=?
Hibernate: update child set name=?, father_id=? where id=?
Hibernate: update child set name=?, father_id=? where id=?

=== test find after modify ===
Hibernate: select father0_.id as id23_, father0_.name as name23_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id24_0_, children0_.name as name24_0_, children0_.father_id as father3_24_0_ from child children0_ where children0_.father_id=?

=== test delete ===
Hibernate: select father0_.id as id33_, father0_.name as name33_ from father father0_
Hibernate: select children0_.father_id as father3_1_, children0_.id as id1_, children0_.id as id34_0_, children0_.name as name34_0_, children0_.father_id as father3_34_0_ from child children0_ where children0_.father_id=?
Hibernate: update child set father_id=null where father_id=?
Hibernate: delete from child where id=?
Hibernate: delete from child where id=?
Hibernate: delete from child where id=?
Hibernate: delete from father where id=?

=== test find after delete ===
Hibernate: select father0_.id as id33_, father0_.name as name33_ from father father0_
Hibernate: select child0_.id as id34_, child0_.name as name34_, child0_.father_id as father3_34_ from child child0_

所以,inverse的设置是很重要的一个事情。

附件中包含了源代码。这里说明一下。源代码中没有包含数据库驱动的jar包和hibernate3.1的jar包。需要大家自己添加。