hjack 发表于 2005-8-17 13:47

[原创]hibernate 一对一实践

最近做的一个工程要用到hibernate的一对一关联,比如论坛的一个主题对应一个作者。
hibernate的一对一关系有两种形式,一种是共享主键方式,另一种是惟一外键方式,因为这里用到的是在主题表里与作者表之间的对应关系,所以介绍的是惟一外键方式的一以一关联。
由于网上很多教程都说得不清楚,给出的实例不能正确运行,所以写下这份笔记,以便以后查询,并与大家分享,如有不对的地方请指正。

本测试使用mysql数据库,eclipse2.1平台,使用tanghan插件生成hbm文件。

1、新建数据库表如下:
    CREATE TABLE `author` (
      `id` int(11) NOT NULL auto_increment,
      `name` varchar(50) default NULL,
      PRIMARY KEY(`id`)
    );
    CREATE TABLE `topic` (
      `id` int(11) NOT NULL auto_increment,
      `name` varchar(50) default NULL,
      `user_id` int(11) default NULL,
      PRIMARY KEY(`id`)
    );

2、用tanghan建立数据库连接,并对这两个表生成相应的hbm文件(也可以手工编写这些文件)。
Topic.hbm.xml文件如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
    <hibernate-mapping>
    <class name="model.Topic" table="topic">
    <id column="id" length="11" name="id" type="integer">
    <generator class="native"/>
    </id>
    <property column="name" length="50" name="name" type="string"/>
    <property column="user_id" length="11" name="user_id" type="integer"/>
    </class>
    </hibernate-mapping>
   
Author.hbm.xml文件如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
    <hibernate-mapping>
    <class name="model.Author" table="author">
    <id column="id" length="11" name="id" type="integer">
    <generator class="native"/>
    </id>
    <property column="name" length="50" name="name" type="string"/>
    </class>
    </hibernate-mapping>
   
Author.java文件如下:
    package model;
   
    import java.io.Serializable;
    import org.apache.commons.lang.builder.EqualsBuilder;
    import org.apache.commons.lang.builder.HashCodeBuilder;
    import org.apache.commons.lang.builder.ToStringBuilder;
   
    /** @author Hibernate CodeGenerator */
    public class Author implements Serializable {
   
      /** identifier field */
      private int id;
   
      /** nullable persistent field */
      private String name;
   
      /** full constructor */
      public Author(java.lang.String name) {
            this.name = name;
      }
   
      /** default constructor */
      public Author() {
      }
   
      public int getId() {
            return this.id;
      }
   
            public void setId(int id) {
                    this.id = id;
            }
   
      public java.lang.String getName() {
            return this.name;
      }
   
            public void setName(java.lang.String name) {
                    this.name = name;
            }
   
      public String toString() {
            return new ToStringBuilder(this)
                .append("id", getId())
                .toString();
      }
   
      public boolean equals(Object other) {
            if ( !(other instanceof Author) ) return false;
            Author castOther = (Author) other;
            return new EqualsBuilder()
                .append(this.getId(), castOther.getId())
                .isEquals();
      }
   
      public int hashCode() {
            return new HashCodeBuilder()
                .append(getId())
                .toHashCode();
      }
   
    }
   
Topic.java文件如下:
    package model;
   
    import java.io.Serializable;
    import org.apache.commons.lang.builder.EqualsBuilder;
    import org.apache.commons.lang.builder.HashCodeBuilder;
    import org.apache.commons.lang.builder.ToStringBuilder;
   
    /** @author Hibernate CodeGenerator */
    public class Topic implements Serializable {
   
      /** identifier field */
      private int id;
   
      /** nullable persistent field */
      private String name;
   
      /** nullable persistent field */
      private int user_id;
   
      /** full constructor */
      public Topic(java.lang.String name, int user_id) {
            this.name = name;
            this.user_id = user_id;
      }
   
      /** default constructor */
      public Topic() {
      }
   
      public int getId() {
            return this.id;
      }
   
            public void setId(int id) {
                    this.id = id;
            }
   
      public java.lang.String getName() {
            return this.name;
      }
   
            public void setName(java.lang.String name) {
                    this.name = name;
            }
   
      public int getUser_id() {
            return this.user_id;
      }
   
            public void setUser_id(int user_id) {
                    this.user_id = user_id;
            }
   
      public String toString() {
            return new ToStringBuilder(this)
                .append("id", getId())
                .toString();
      }
   
      public boolean equals(Object other) {
            if ( !(other instanceof Topic) ) return false;
            Topic castOther = (Topic) other;
            return new EqualsBuilder()
                .append(this.getId(), castOther.getId())
                .isEquals();
      }
   
      public int hashCode() {
            return new HashCodeBuilder()
                .append(getId())
                .toHashCode();
      }
   
    }

to be continued

hjack 发表于 2005-8-17 13:48

3、修改Topic.java文件。
找到 private int user_id;
修改成private Author author;
找到 构造函数public Topic(java.lang.String name, int user_id),把参数int user_id改为Author author, 把函数里的this.user_id = user_id; 改为this.author = author;
找到以下两个函数
      public int getUser_id() {
      return this.user_id;
      }
   
            public void setUser_id(int user_id) {
                    this.user_id = user_id;
            }
修改为
            public Author getAuthor() {
                    return author;
            }
   
            public void setAuthor(Author author) {
                    this.author = author;
            }
然后保存。以上文件保存在model包里。

4、修改Topic.hbm.xml文件。
删除下面这行
<property column=\"user_id\" length=\"11\" name=\"user_id\" type=\"integer\"/>
在</class>前添回<many-to-one>项如下
<many-to-one name=\"author\" class=\"model.Author\" column=\"user_id\" unique=\"true\"/>

通过以上操作就建立了Topic表与Author表之间的单向一对一关系,因为本工程中只需要从主题表去了解作者的信息,所以只需要单向的一对一就可以完成了。

5、建立测试用例。
1)、新建test包,在test包内建立HibernateUtil类。
    /*
   * 创建日期 2005-8-4
   *
   * TODO 要更改此生成的文件的模板,请转至
   * 窗口 - 首选项 - Java - 代码样式 - 代码模板
   */
    package test;
   
    /**
   * @author hjack<br>
   *
   * TODO 要更改此生成的类型注释的模板,请转至
   * 窗口 - 首选项 - Java - 代码样式 - 代码模板
   */
   
   
    import net.sf.hibernate.HibernateException;
    import net.sf.hibernate.Session;
    import net.sf.hibernate.SessionFactory;
    import net.sf.hibernate.cfg.Configuration;
   
    public class HibernateUtil {
   
            private static final SessionFactory sessionFactory;
            private static Configuration cfg = null;
   
            static {
                    try {
                            cfg = new Configuration();
                            sessionFactory =cfg.configure().buildSessionFactory();
                    } catch (HibernateException ex) {
                            throw new RuntimeException(
                                    \"Exception building SessionFactory: \" + ex.getMessage(),
                                    ex);
                    }
            }
   
            public static final ThreadLocal session = new ThreadLocal();
   
            public static Session currentSession() throws HibernateException {
                    Session s = (Session) session.get();
                    // Open a new Session, if this Thread has none yet
                    if (s == null) {
                            s = sessionFactory.openSession();
                            session.set(s);
                    }
                    return s;
            }
   
            public static void closeSession() throws HibernateException {
                    Session s = (Session) session.get();
                    session.set(null);
                    if (s != null)
                            s.close();
            }
    }
   
hibernate.cfg.xml文件内容如下:
    <!DOCTYPE hibernate-configuration PUBLIC
            \"-//Hibernate/Hibernate Configuration DTD//EN\"
            \"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd\">
    <hibernate-configuration>
            <session-factory>
                    <!--<property name=\"connection.datasource\">java:comp/env/jdbc/mysql</property>-->
                    <property name=\"dialect\">net.sf.hibernate.dialect.MySQLDialect</property>
                    <property name=\"connection.driver_class\">com.mysql.jdbc.Driver</property>
                    <property name=\"connection.url\">jdbc:mysql://localhost/testhibernate</property>
                    <property name=\"connection.username\">root</property>
                    <property name=\"connection.password\"></property>
                    <property name=\"show_sql\">true</property>
                    <!--mapping files-->
                    <mapping resource=\"model/Author.hbm.xml\"></mapping>
                    <mapping resource=\"model/Topic.hbm.xml\"></mapping>
            </session-factory>
    </hibernate-configuration>   
要把Author.hbm.xml,Topic.hbm.xml这两个hbm文件写进,否则会找不到类的。

2)、新建Test类,用于测试。
    /*
   * 创建日期 2005-8-10
   *
   * 更改所生成文件模板为
   * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
   */
    package test;
   
    import model.Author;
    import model.Topic;
    import net.sf.hibernate.HibernateException;
    import net.sf.hibernate.Session;
    import net.sf.hibernate.Transaction;
   
    /**
   * @author hjack
   *
   * 更改所生成类型注释的模板为
   * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
   */
    public class Test {
   
            Session sess;
            Transaction tx;
           
            public void insertTopic(Topic topic,int userID) throws HibernateException{
                    try{
                            sess = HibernateUtil.currentSession();
                            tx = sess.beginTransaction();
                            //新建一个author对象,并把作者id置入该对象里。                       
                            Author author = new Author();
                            author.setId(userID);                       
                            //新建一个topic对象,设置用户名和把author对象set进去。
                            topic.setAuthor(author);
                            //因为只是插入一个话题,并不必在author表中插入一条记录,所以只需save(topic)                       
                            sess.save(topic);
                            tx.commit();
                    }catch(HibernateException e){
                            System.out.println(e.toString());
                    }finally{
                            if(tx!=null){
                                    tx.rollback();
                            }
                            HibernateUtil.closeSession();
                    }
            }
           
            public void insertAuthor(Author author) throws HibernateException{
                    try{
                            sess = HibernateUtil.currentSession();
                            tx = sess.beginTransaction();
                            sess.save(author);
                            tx.commit();
                    }catch(HibernateException e){
                            System.out.println(e.toString());
                    }finally{
                            if(tx!=null){
                                    tx.rollback();
                            }
                            HibernateUtil.closeSession();
                    }
            }
   
   
            public Topic query(int id) throws HibernateException{
                    Topic topic = null;
                    try{
                            sess = HibernateUtil.currentSession();   
                            topic=(Topic)sess.load(Topic.class,new Integer(id));   
                    }catch(HibernateException e){
                            e.printStackTrace();
                    }finally{
                            HibernateUtil.closeSession();
                    }
                    return topic;
            }
           
            public static void main(String[] args) {
                   
                    Test app = new Test();
                    try {
                            /*测试插入作者
                            Author author = new Author();
                            author.setName(\"jack\");
                            app.insertAuthor(author);
                            */
                            /*测试插入主题
                            Topic topic = new Topic();
                            topic.setName(\"helloworld.\");
                            app.insertTopic(topic,1);
                            */
                            /*测试查询主题
                            Topic topic = app.query(1);
                            System.out.println(topic.getAuthor().getName());
                            */
                    } catch (Exception e) {
                            // TODO 自动生成 catch 块
                            e.printStackTrace();
                    }
                   
            }
    }

测试插入作者如图1所示,测试插入主题如图2所示,测试查询主题结果如下:
Hibernate: select topic0_.id as id1_, topic0_.name as name1_, topic0_.user_id as user_id1_, author1_.id as id0_, author1_.name as name0_ from topic topic0_ left outer join author author1_ on topic0_.user_id=author1_.id where topic0_.id=?
jack
生成的sql语句用到了join,查询结果为jack,与期望相符。
图1
http://photoimg17.qq.com/cgi-bin/load_pic?verify=daLWrMZnz9Wmai1f4wdv8A%3D%3D
图2
http://photoimg16.qq.com/cgi-bin/load_pic?verify=JiRv3LFMah3K8y%2FdZb6WIw%3D%3D

[ Last edited by hjack on 2005-8-17 at 13:56 ]

wool王 发表于 2005-8-17 14:58

jackven我来支持你了。我好久没来了。。。

受用。。。

wool王 发表于 2005-8-17 15:06

我详细研究下,帮你找下错漏。

hjack 发表于 2005-8-17 21:47

呵呵,有什么错漏的地方一定要指出来哦。

用程序诠释生命 发表于 2005-8-18 15:26

可惜我不懂,虽然最近也在研究一下ORMapping

wool王 发表于 2005-8-18 17:10

楼上的也来写张原创贴出来啊。

用程序诠释生命 发表于 2005-8-18 17:14

我没楼上那么悠闲

wool王 发表于 2005-8-18 21:27

今天上班偷懒去运行的你的hibernate,,,顺便帮你找错漏。。。

结果证实是没错漏的。。。呵呵。。。

为了灌水和帖子好看,我下一贴再说自己的观点。呵呵。

[ Last edited by wool王 on 2005-8-18 at 13:40 ]

wool王 发表于 2005-8-18 21:39

首先我不知道你整个项目的宏观情况,包括需求等等,所以可能你这样的设计是有其他的原因的,我仅仅是表达自己的看法而已,呵呵。。。

然后说说我的看法。topic和author是一对一关联的关系,并且你在程序中也已经将author类耦合在topic类中了,但是我发现你在测试用例中使用了:public void insertTopic(Topic topic,int userID) throws HibernateException这么一个方法,我个人感觉不妥,userID好像完全不必要,我觉得userID本身就是包括在topic.getAuthor()里。我感觉面向对象的编程出现了个本应存在于对象内的东西在外头,很别扭。

wool王 发表于 2005-8-18 21:40

用程序诠释生命在 2005/8/18 09:14 发表:

我没楼上那么悠闲

靠!一看你的ID就想扁你。。。

hjack 发表于 2005-8-18 22:50

因为在数据库的topic表中,user_id是真实存在的,要保存一条主题记录,必须为它指定一个作者,这是通过user_id来关联的。
而因为在与topic表相对应的类Topic中,已把user_id通过对象author来关联了,所以要保存user_id就要实例一个author对象,通过这个对象来保存user_id至topic表中。
可能这不是很好的方法 ,如果你有其它更好的方法,欢迎来讨论。

hjack 发表于 2005-8-18 23:00

理解错你的意思。

对的,可以把userID在执行insertTopic()之前放入topic对象里。
可以这样做:

public void insertTopic(Topic topic) throws HibernateException{
    try{
         sess = HibernateUtil.currentSession();
         tx = sess.beginTransaction();
         sess.save(topic);
          tx.commit();
       }catch(HibernateException e){
            System.out.println(e.toString());
      }finally{
            if(tx!=null){
                   tx.rollback();
            }
            HibernateUtil.closeSession();
         }
      }


/*测试插入主题
Topic topic = new Topic();
Author author = new Author();
author.setId(userID);
topic.setAuthor(author);
topic.setName(\"helloworld.\");
app.insertTopic(topic);
*/

注:以上代码没有运行过,但我相信是可行的。

hjack 发表于 2005-8-18 23:03

你指的是这个意思吧????

wool王 发表于 2005-8-19 10:24

大概是这个意思。。。我感觉测试用例像楼上这么写会比较好。。。

powerwind 发表于 2006-5-8 13:08

原帖由 hjack 于 2005-8-17 13:47 发表
所以介绍的是惟一外键方式的一以一关联

楼主好像忘记在数据厍设置外键了,还是不用设置也可以的?
我不会也不喜欢使用工具生成JAVA代码,楼主做项目是不是常用啊?
把userID在执行insertTopic()之前放入topic对象里,感觉不太好,忘记了怎么办?

楼上不要说我顶旧帖啊,是你叫我看的啊!!

wool王 发表于 2006-5-8 13:19

数据库外键可以不设置的...在hibernate关联里面配置就可以了.
页: [1]
查看完整版本: [原创]hibernate 一对一实践