持久层hibernate创建orm

来源:互联网 发布:奇妙趋势分析软件 编辑:程序博客网 时间:2024/05/21 08:49

            Hibernate是一个开源的、面向java环境的对象/关系数据库映射工具。Object/Relational Mapping(对象/关系数据库映射)表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。其方法就是通过创建对象/关系映射配置文件(XML文件),在实体类(Entity)和数据库表之间建立对应关系,其中,对象属性对应表字段;对象之间的依赖关系,对应数据表之间的主、外键约束。采用Hibernate 作为持久层技术的最大的好处在于:可以完全以面向对象的方式进行系统分析、系统设计。面向对象的分析和面向对象的设计才最接近于程序员的自然思维。   

         有两种方法创建对象/关系映射,一种是先定义实体类及其依赖关系,并据此编写对象/关系映射文件,然后编程生成对应的数据库表结构;另一种是先创建数据库表结构以及表之间的主外键约束,再利用开发工具(如MyEclipse)反向生成实体类及对象/关系映射配置文件。先介绍第一种方法:

1、编写PO(Persist Object)类

    下图显示了本系统PO的类图。  

 

[转载]Sping+Hibernate+Struts开发应用实例

    如图所示,根据要实现的功能,系统的模型Model 实现类有四个: Category,News, NewsReview 和User,它们都是普通的JavaBean ,下面是这四个基本Persisent Object 类要实现的功能

    • News: 封装了一条消息。包括标题、内容、发布时间及发布人等。
    • Category: 封装了一个消息分类。
    • User: 封装了一个用户的信息。
    • NewsReview: 封装了一条消息评论。

    其中, News 有一个Category 类型的成员变量及User 类型的成员变量;NewsReview有一个News 类型的成员变量和一个User 类型的成员变量。为了能够使用双向关联(Hibernete 的映射功能), Category 有一个集合型成员变量,用于存放与这个Category 对象有关联的News 对象;同样News 也有一个集合型成员变量,用于存放与这个News 对象有关联的NewsReview 对象。实际上,持久化就是通过成员变量来映射关系数据库里的l-N 和N-N 的关系。

   需要指出的是,四个类都继承自BaseObject类。下面是BaseObject类的代码:

    //将父类声明为abstract 类

    package mxh.model;

    import java.io.Serializable;

    public abstract class BaseObject implements Serializable {

       public abstract String toString();
       public abstract boolean equals(Object o);
       public abstract int hanshCode(); 
    }
    父类BaseObject 是一个抽象类,定义了三个抽象方法toStringO ,equalsO 和hashCodeO,这三个方法是Hibernate 推荐持久化对象时重写的。

让其他类继承这个抽象类只是一个可选的写法,至少直接让其他类重写equalsO和hashCodeO方法来实现接口Serializable也是可以的。

推荐实现Serializable 接口时,最好重写equalsO 和hashCodeO 方法。

 


    下面具体来看News 类。
    package mxh.model;

    import java.util.Date;
    import java.util.Set;

    import org.apache.commons.lang.builder.ToStringBuilder;

    public class News extends BaseObject {

        private Long id;// pk,required

        private String title;// required

        private String content;// required

        private User poster;// fk,required

        private Date postDate;// required

        private Date lastModifyDate;// required

        private Category category;// required

        private Set newsReviews;

        public News() {
        }

        public Set getNewsReviews() {
           return newsReviews;
        }

        public void setNewsReviews(Set newsReviews) {
           this.newsReviews = newsReviews;
        }

 
        public Category getCategory() {
           return category;
        }

        public void setCategory(Category category) {
           this.category = category;
        }


        public Date getLastModifyDate() {
           return lastModifyDate;
        }

        public void setLastModifyDate(Date lastModifyDate) {
           this.lastModifyDate = lastModifyDate;
        }

        public Date getPostDate() {
           return postDate;
        }

        public void setPostDate(Date postDate) {
           this.postDate = postDate;
        }

        public User getPoster() {
           return poster;
        }

        public void setPoster(User poster) {
           this.poster = poster;
        }

        public String getContent() {
           return content;
        }

        public void setContent(String content) {
           this.content = content;
        }

        public Long getId() {
           return id;
        }

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

        public String getTitle() {
           return title;
        }

        public void setTitle(String title) {
           this.title = title;
        }

        public boolean equals(Object object) {
            if (!(object instanceof News)) {
                return false;
            }
            News rhs = (News) object;
            return this.poster.equals(rhs.getPoster()) && this.postDate.equals(rhs.getPostDate());
            //* return new EqualsBuilder().append(this.newsReviews, rhs.newsReviews)
            //* .append(this.title, rhs.title).append(this.category,
            //* rhs.category).append(this.content, rhs.content).append(
            //* this.postDate, rhs.postDate).append( this.lastModifyDate,
            //* rhs.lastModifyDate).append( this.id, rhs.id).append(this.poster,
            //* rhs.poster) .isEquals();
        }


        public String toString() {
           return new ToStringBuilder(this).append("id", this.id).append("title",this.title)

                .append("postDate", this.postDate).append("content",this.content)

                .append("lastModifyDate", this.lastModifyDate).append("poster", this.poster)

                .append("category", this.category).append("newsReviews",this.newsReviews)

                .toString();
        }

        public int hanshCode() {
           return this.poster.hashCode() + this.postDate.hashCode();
           //* return new HashCodeBuilder(1595611275, -1477459617).append(
           //* this.newsReviews).append(this.title).append(this.category)
           //* .append(this.content).append(this.postDate).append(
           //* this.lastModifyDate).append(this.id)
           //* .append(this.poster).toHashCode();
        }

   

各个属性的含义(后面的映射配置文件会有进一步探讨):每个News 对象就相当于数据库表里的一条记录,其中id 属性映射的是记录的主键: title 与content分别是消息的标题和内容;postDate 是发布时间;lastModifyDate 是最后评论时间;category则是News 关联(在数据库里通过外键关联)的Category对象; newsReviews则是News 对象关联的所有NewsReview 对象。

2、编写PO 的映射配置文件
    仅有一个POJO 是无法完成数据库的持久化操作的,还必须为POJO 增加映射文件。当POlO 增加映射文件后,可以完成O/R Mapping ,从而在某个特定对象的管理下完成数据库访问。
    因此必须为这个POlO 配上一个映射文件,通常将这个映射文件命名为:类名.hbm.xml ,并与这个类放置在同一目录下。News.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="mxh.model.News" table="news">

          <id name="id" column="id" unsaved-value="null">

            <generator class="increment">

            </generator>

          </id>

          <!-- N--1关联, category_id为Category表(主键)在News表中的外键名。如果在Category表中不引

             用News对象(Set--News对象列表),Hibernate称之为单向关系映射。很明显本系统的Category

             和News是双向关系映射(后面将列出Category的映射配置)。实际上,与单向映射的单向访问相

             比,双向映射只是在双方类定义中都设置了互访接口,无论单向还是双向,两个表之间的主外键

             关系总是单向的。如果非得设置双向的主外键关系,那只能通过增加连接表来设置,让连接表与

               双方建立外键关系,这就成为Hibernate所称的N--N关联 -->

          <many-to-one name="category" class="mxh.model.Category"column="category_id"

              not-null="true">

          </many-to-one>

          <property name="lastModifyDate" column="last_modify_date" not-null="true">

          </property>

          <property name="postDate" column="post_date" not-null="true">

          </property>

          <many-to-one name="poster" column="username" not-null="true">

          </many-to-one>

          <property name="content" column="content" length="3000" not-null="true">

          </property>

          <property name="title" column="title" length="50" not-null="true">

          </property>
          <!-- 与NewsReviews的1--N双向关系映射(NewsReviews配置文件要相应设置many-to-one映射)-->
          <set name="newsReviews" lazy="false" inverse="true" cascade="all-delete-orphan">
             <key>
               <column name="news_id" />
             </key>
             <one-to-many class="mxh.model.NewsReview" />
          </set>
       </class>

    </hibernate-mapping>

   

    下面是Category.hbm.xml映射文件的代码:

    ......

    <hibernate-mapping>

       <class name="mxh.model.Category" table="category">

          <id name="id" column="id" unsaved-value="null">

             <generator class="increment">

             </generator>

          </id>

          <property name="name" column="name" length="50" not-null="true">

          </property>
          <!-- 与News的双向关系映射 one-to-many association to News -->
          <set name="news" lazy="false" inverse="true" cascade="all-delete-orphan">
             <key>
                 <column name="category_id" />
             </key>
             <one-to-many class="mxh.model.News" />
          </set>
       </class>

    </hibernate-mapping>

    映射文件根元素hibernate-mapping 下面可以有多个class 子元素,即在一个映射文件里可以配置多个映射对象,但为了清晰起见,建议为每个类单独写一个映射配置文件。
    class 的name 属性就是要映射的对象类;table 是数据库里对应的表名;class 下面的子元素就是这个类的属性并与数据库里的宇段相对应。

    id 用于唯一标识该对象,称为标识属性。其中name 属性是类里面的属性名; column是数据库的对应宇段。另外, id 还有generator 子元素,用于指定主键生成方式,这里用的是自动增长生成(increment) 策略,其他方式请参照Hibernate 文档。

3、使用Hibernate配置数据库连接并创建和查询数据表
    完成后可以通过配置Hibernate 的属性(数据库连接驱动,数据库方言,登录用户名与密码等)自动生成数据库的表。首先来看hibernate.cfg.xml文件内容(为工程添加hibernate功能支持组件时自动生成配置框架)。

     <?xml version='1.0' encoding='UTF-8'?>
     <!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

     <!-- Generated by MyEclipse Hibernate Tools.                   -->
     <hibernate-configuration>

      <session-factory>
        <property name="connection.username">root</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/newsboard</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="myeclipse.connection.profile">newsboard</property>
        <property name="connection.password">root</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
       
        <mapping resource="mxh/model/User.hbm.xml"/>
        <mapping resource="mxh/model/Category.hbm.xml"/>
        <mapping resource="mxh/model/News.hbm.xml"/>
        <mapping resource="mxh/model/NewsReview.hbm.xml"/>
   
      </session-factory>

    </hibernate-configuration>

   

    注意:除了手工编写以外,开发者还有一些工具可以帮助生成映射配直文件。例如,Xdoclet 就可以用来生成Hibernate 和Struts 等配置文件,只需在编写源代码时在适当的地方加上注释,再用Xdoclet 生成即可。

    完成映射配置后,可以使用如下程序实现数据库表以及主外键约束关系的创建。

    package mxh.model;

    import org.hibernate.cfg.Configuration;
    import org.hibernate.tool.hbm2ddl.SchemaExport;

    public class SchemaToDb {

       public static void main(String[] args) {
          //使用src目录下的hibernate.cfg.xml默认配置文件,如果使用不同位置或不同的(数据库)连接配

          //置文件,请在configure("")中加载
          Configuration cfg = new Configuration().configure();
          SchemaExport export = new SchemaExport(cfg);
          export.create(true, true);
       }

    }

    运行该程序,可以在MySql数据库服务器中创建上述四个PO类所对应的四个数据库表及其主外键约束。由此,我们还可以创建利用Hibernate的SessionFactory创建会话的通用工具程序。其代码如下:
    package mxh.dataoperationutility;

    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;

    public class HibernateUtil {

       public static final SessionFactory sessionFactory;

       static
       {
         try
         {
            //采用默认的hibernate.cfg.xml来启动一个Configuration的实例
            Configuration configuration=new Configuration().configure();
            //由Configuration的实例来创建一个SessionFactory实例
            sessionFactory = configuration.buildSessionFactory();
         }
         catch (Throwable ex)
         {
             // Make sure you log the exception, as it might be swallowed
             System.err.println("Initial SessionFactory creation failed." + ex);
             throw new ExceptionInInitializerError(ex);
        }
     }

     //ThreadLocal并不是线程本地化的实现,而是线程局部变量。也就是说每个使用该变量的线程都必须为
     //该变量提供一个副本,每个线程改变该变量的值仅仅是改变该副本的值,而不会影响其他线程的该变量
     //的值.

     //ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要对线程同步   
     public static final ThreadLocal session = new ThreadLocal();

     public static Session currentSession() throws HibernateException
     {
         Session s = (Session) session.get();
         //如果该线程还没有Session,则创建一个新的Session
         if (s == null)
         {
            s = sessionFactory.openSession();
            //将获得的Session变量存储在ThreadLocal变量session里
            session.set(s);
         }
         return s;
     }

     public static void closeSession() throws HibernateException {
        Session s = (Session) session.get();
        if (s != null)
            s.close();
        session.set(null);
    
    }
    下面是利用该工具插入和查询数据的示例程序,Sql语句仅供参考:

    package mxh.dataoperationutility;

    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Iterator;
    import java.util.List;

    import org.hibernate.Session;
    import org.hibernate.Transaction;

    import mxh.model.*;

    public class HibernateSqlTest

    {

      public static void main(String[] args) throws Exception {  
        HibernateSqlTest mgr=new HibernateSqlTest();
        //mgr.insertData();
        mgr.queryData();
        HibernateUtil.sessionFactory.close();        
      
      private void queryData(){
        Session session = HibernateUtil.currentSession();
        // 通过命名参数解决汉字乱码问题,又可避免sql注入攻击
        List newsList=session.createQuery("from News nw where  nw.poster.username = 'j2ee' "

              +"and nw.category.name = :cname").setString("cname","社会新闻").list();
        for (Iterator it = newsList.iterator() ; it.hasNext(); )
        {
            News nw = ( News )it.next();
            System.out.println(nw.getContent());
        }
  
         List newsreList=session.createQuery("from NewsReview nr where  nr.news.category.name in

            (select ca.name from Category ca where ca.id < 3) ").list();
         for (Iterator it = newsreList.iterator() ; it.hasNext(); )
         {
            NewsReview nwr = ( NewsReview )it.next();
            System.out.println(nwr.getContent());
         }
      }
 
      private void insertData()throws Exception
       
        Session session = HibernateUtil.currentSession();
        Transaction tx = session.beginTransaction();
        User user1=new User();
        user1.setUsername("j2ee");
        user1.setPassword("123");
        User user2=new User();
        user2.setUsername("mysql");
        user2.setPassword("12345");
       
        //使用persist报错“detached entity passed to persist: null”,
        //原因可能是persist是把托管对象持久化(与数据表关联),不执行insert,
        //而新对象不生成标识属性,不能持久化,只能保存(执行insert),当然事务中
        //如果有错可以回滚。请注意save、persist、update、merge方法的区别。
        //session.persist(user1);
        session.save(user1);
        session.save(user2);
               
        Category category1=new Category();
        category1.setName("社会新闻");
        Category category2=new Category();
        category2.setName("军事新闻");
       
        session.save(category1);
        session.save(category2);
        
        News news1=new News();
        //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //Date d = sdf.parse("2004-10-03");
        news1.setCategory(category1);
        news1.setTitle("第一条新闻");
        news1.setContent("第一条新闻的内容");
        news1.setPoster(user1);
        news1.setPostDate(new Date());
        news1.setLastModifyDate(new Date());
       
        News news2=new News();
        news2.setCategory(category1);
        news2.setTitle("第二条新闻");
        news2.setContent("第二条新闻的内容");
        news2.setPoster(user1);
        news2.setPostDate(new Date());
        news2.setLastModifyDate(new Date());
       
        session.persist(news1);
        session.persist(news2);
               
        NewsReview newsreview1=new NewsReview();
        newsreview1.setNews(news1);
        newsreview1.setContent("第一条社会新闻的第一条评论");
        newsreview1.setPoster(user1);
        newsreview1.setPostDate(new Date());
        newsreview1.setLastModifyDate(new Date());
       
        NewsReview newsreview2=new NewsReview();
        newsreview2.setNews(news1);
        newsreview2.setContent("第一条社会新闻的第二条评论");
        newsreview2.setPoster(user1);
        newsreview2.setPostDate(new Date());
        newsreview2.setLastModifyDate(new Date());
       
        session.persist(newsreview1);
        session.persist(newsreview2);  
       
        tx.commit();
        HibernateUtil.closeSession();        
      }

    }

    前述各文件在MyEclipse中的路径如下图所示:

[转载]Sping+Hibernate+Struts开发应用实例

 

原创粉丝点击