zoukankan      html  css  js  c++  java
  • Hibernate 之单向多对一映射及其衍生问题

      由于在数据表之间可以通过外键进行关联,在使用Hibernate操作映射到存在关联关系的数据表的对象时,需要将对象的关联关系与数据表的外键关联进行映射。

      首先建立hibernate.cfg.xml和会话工厂类HibernateUtil,然后添加两个待操作的实体类和相应的映射文件。

    HibernateUtil如下:

    package com.zzh.util;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.boot.registry.StandardServiceRegistry;
    import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
    import org.hibernate.cfg.Configuration;
    
    public class HibernateUtil {
        private static SessionFactory sessionFactory;
        private static Session session;
    
        static {
            // 创建Configuration对象,读取hibernate.cfg.xml文件,完成初始化
            Configuration config = new Configuration().configure();
            StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder()
                    .applySettings(config.getProperties());
            StandardServiceRegistry ssr=ssrb.build();
            sessionFactory=config.buildSessionFactory(ssr);
        }
        
        //获取SessionFactory
        public static SessionFactory getSessionFactory(){
            return sessionFactory;
        }
        
        //获取Session
        public static Session getSession(){
            session=sessionFactory.openSession();
            return session;
        }
        
        //关闭Session
        public static void closeSession(Session session){
            if(session!=null){
                session.close();
            }
        }
    }
    View Code

    两个实体类班级Grade和学生Student

    package com.zzh.entity;
    
    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.Set;
    
    public class Grade implements Serializable {
        private int gid;
        private String gname;
        private String gdesc;
    
        public int getGid() {
            return gid;
        }
    
        public void setGid(int gid) {
            this.gid = gid;
        }
    
        public String getGname() {
            return gname;
        }
    
        public void setGname(String gname) {
            this.gname = gname;
        }
    
        public String getGdesc() {
            return gdesc;
        }
    
        public void setGdesc(String gdesc) {
            this.gdesc = gdesc;
        }
    
        public Grade() {
            super();
        }
    
    
        public Grade(int gid, String gname, String gdesc) {
            super();
            this.gid = gid;
            this.gname = gname;
            this.gdesc = gdesc;
        }
    
        public Grade(String gname, String gdesc) {
            super();
            this.gname = gname;
            this.gdesc = gdesc;
        }
    
    }
    package com.zzh.entity;
    
    import java.io.Serializable;
    
    public class Student implements Serializable {
        private int sid;
        private String sname;
        private String sex;
        //在多方定义一个一方的引用
        private Grade grade;
    
        public Grade getGrade() {
            return grade;
        }
    
        public void setGrade(Grade grade) {
            this.grade = grade;
        }
    
        public int getSid() {
            return sid;
        }
    
        public void setSid(int sid) {
            this.sid = sid;
        }
    
        public String getSname() {
            return sname;
        }
    
        public void setSname(String sname) {
            this.sname = sname;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public Student() {
            super();
        }
    
        public Student(String sname, String sex) {
            super();
            this.sname = sname;
            this.sex = sex;
        }
    
    }

    可以看出我在Student类中使用Grade类声明了grade属性,并添加了getter和setter,以体现实体类Student对Grade的关联关系,在下面的映射表中只需要在“多”的一方配置。注意,Student类中添加的grade属性为Grade,它是一个持久化类Grade的对象属性,不是一个基本类型属性,因此不能用<property>元素来映射grade属性,又因为是多对一关联关系,要使用<many-to-one>元素。

    Grade.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">
    <!-- Generated 2016-8-31 11:19:40 by Hibernate Tools 3.5.0.Final -->
    <hibernate-mapping>
        <class name="com.zzh.entity.Grade" table="GRADE">
            <id name="gid" type="int">
                <column name="GID" />
                <generator class="increment"/>
            </id>
            <property name="gname" type="java.lang.String">
                <column name="GNAME" />
            </property>
            <property name="gdesc" type="java.lang.String">
                <column name="GDESC" />
            </property>
        </class>
    </hibernate-mapping>
    View Code

    Student.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">
    <!-- Generated 2016-8-31 11:19:40 by Hibernate Tools 3.5.0.Final -->
    <hibernate-mapping>
        <class name="com.zzh.entity.Student" table="STUDENT">
            <id name="sid" type="int">
                <column name="SID" />
                <generator class="increment" />
            </id>
            <property name="sname" type="java.lang.String">
                <column name="SNAME" />
            </property>
            <property name="sex" type="java.lang.String">
                <column name="SEX" />
            </property>
            <many-to-one name="grade" class="com.zzh.entity.Grade" fetch="join">
                <column name="GRADE" />
            </many-to-one>
        </class>
    </hibernate-mapping>

    <many-to-one>元素中的name指定Student类中关联类的属性名,column指定数据表关联的外键。换句话说,实体类Student对Grade的多对一关联在本质上是通过数据表student中的外键GRADE与数据表grade关联实现的,但Hibernate将表之间的关联通过<many-to-one>元素进行了封装

    2.编写与数据库交互的UserDAO接口和其实现类UserDAOImpl,将数据的添加,查找,修改,删除等方法封装其中。

    UserDAO:

    package com.zzh.dao;
    
    public interface UserDAO {
        void save(Object obj);
        Object findById(int id,Object obj);
        void delete(Object obj);
        void update(Object obj);
    }

    UserDAOImpl:

    package com.zzh.dao;
    
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    
    import com.zzh.util.*;
    
    public class UserDAOImpl implements UserDAO {
    
        @Override
        public void save(Object obj) {
            Session session = HibernateUtil.getSession();
            Transaction tx = session.beginTransaction();
            try {
                session.save(obj);
                tx.commit();
            } catch (Exception e) {
                e.printStackTrace();
                tx.rollback();
            } finally {
                HibernateUtil.closeSession(session);
            }
    
        }
    
        @Override
        public Object findById(int id, Object obj) {
    
            Session session = HibernateUtil.getSession();
            Transaction tx = session.beginTransaction();
            try {
                obj = session.get(obj.getClass(), id);
                tx.commit();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                HibernateUtil.closeSession(session);
            }
            return obj;
        }
    
        @Override
        public void delete(Object obj) {
            Session session = HibernateUtil.getSession();
            Transaction tx = session.beginTransaction();
            try {
                session.delete(obj);
                tx.commit();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                HibernateUtil.closeSession(session);
            }
    
        }
    
        @Override
        public void update(Object obj) {
            Session session = HibernateUtil.getSession();
            Transaction tx = session.beginTransaction();
            try {
                session.update(obj);
                tx.commit();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                HibernateUtil.closeSession(session);
            }
    
        }
    
    }

    注意:我的这个DAO使用的是Object对象,很多书上都是针对多个的对象建立多个DAO,也就是拆分为StudentDAOImpl和GradeDAOImpl,这样里面的Object就变为了相应的Student或者Grade。

    如果你要改写为我这样的通用DAO,尤其要注意里面的findById()方法,对于拆分写的DAO,这个方法很简单,只需要传入参数id然后Grade grade=(Grade) session.get(Grade.class, id);而我的方法因为是通用的,所有要传入id和对应的实体对象,obj = session.get(obj.getClass(), id),这里面运用了java的有关反射的知识,顺便拓展一下。

    1.利用对象调用getClass()方法获取该对象的Class实例。比如 Students s = new Students();  Class c = s.getClass();2.使用Class的静态方法forName(),用类的名字获取一个Class实例;比如 Class c = Class.forName("Students");3.运用.class的方式获取Class实例,对基本数据类型的封装类,还可以采用.TYPE来获取对应的基本数据类型的Class实例;Class c = Students.class; 所以对于我的例子而言,就是利用了obj.getClass()等价于Object.class这个特性来修改这个方法,大家可以自己试试。

    3.添加测试案例JUnit

      请注意看我上面给出的hbm.xml文件,主键生成机制是increment

                                                       

    现在问题又来了!!!当我用工具通过实体类生成hbm.xml文件时,主键生成机制是

    assigned表示对象标识符由应用程序产生,如果不指定<generator>节点,则默认使用该策略。此时数据表还没有建立,当我开始测试的时候,Hibernate根据cfg.xml中的数据库信息和相关映射信息建立表,问题来了,Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '0' for key 'PRIMARY'。 数据表grade中出现 “0 Java一班 Java软件开发一班 ”一条记录,数据表student中出现 “ 0 慕女神  女 0 ”一条记录,提示已经告诉我们主键重复,0主键已经存在,不能再进行添加了。assigned表示主键有程序代码负责,你要保证这个主键数据是唯一的。这时我在想,那我用identity生成策略会怎么样,identity表示对象标识符由底层数据库的自增主键生成机制产生。这样一搞 Caused by: java.sql.SQLException: Field 'SID' doesn't have a default value,马上反应过来自己没有在数据库设置AUTO_increment,需要去设置两个表的这个值。

    这样一搞,就可以正常添加了。

    当我设置好自动递增后,又想试试assigned,于是又改为了assigned,问题又来了Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 ;百度之后,有人这样解释:这个异常是由于主键设置为自增长,而在我们插入记录的时候设置了ID的值导致的。懵逼了,到现在我还在找怎样解决,如果你有答案请告诉我。

    之后我选用了increment生成策略,这个策略不需要在数据库中设置自动递增,感觉应付这种小例子很方便,不过他的局限性在于如果有多个应用实例向同一张表中插入数据时,则会出现重复的主键。需谨慎使用。

    4.总结

      这篇文章感觉挺凌乱的,一会是DAO修改一会又是反射,最后还自己搞了一堆BUG,自己还是需要慢慢积累知识才行,如果你觉得这篇文章有点用,请帮忙点个赞,谢谢观看。

      

  • 相关阅读:
    【Android游戏开发之八】游戏中添加音频详解MediaPlayer与SoundPool的利弊以及各个在游戏中的用途!
    【Android游戏开发之九】(细节处理)触屏事件中的Bug解决方案以及禁止横屏和竖屏切换!
    【Android游戏开发之七】(游戏开发中需要的样式)再次剖析游戏开发中对SurfaceView中添加组件方案!
    前端要给力之:URL应该有多长?
    【Android游戏开发之三】剖析 SurfaceView ! Callback以及SurfaceHolder!!
    charactersFound方法中的陷阱
    前端要给力之:分解对象构造过程new()
    结合UIImageView实现图片的移动和缩放
    【Android游戏开发之一】设置全屏以及绘画简单的图形
    扩展BaseAdapter实现在ListView中浏览文件
  • 原文地址:https://www.cnblogs.com/zhaozihan/p/5834161.html
Copyright © 2011-2022 走看看