你的位置:首页 > ASP.net教程

[ASP.net教程]Hibernate中的一对一关联和组件的映射


Hibernate提供了两种映射一对一映射关联关系的方式:

01.按照外键映射

02.按照主键映射

下面以员工账号表和员工档案表(员工账号和档案表之间是一对一的关系)为例,介绍这两种映射关系,并使用这两种 映射方式分别完成以下持久化操作

(1)保存员工档案的同时分配给员工一个账号

(2)加载员工档案的同时加载账号信息

一:按照外键映射

需要提示:

HibernateUtil工具类(用于获取session和关闭session)

package cn.zhang.util;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;public class HibernateUtil {  //初始化一个ThreadLocal对象,有get和set方法  private static final ThreadLocal<Session> sessionTL=new ThreadLocal<Session>();    private static Configuration configuration;    private final static SessionFactory sessionFactory;  static{        configuration=new Configuration().configure();    sessionFactory=configuration.buildSessionFactory();  }  //获得session对象  public static Session currentSession() {    //sessionTL的get方法根据当前线程返回其对应的线程内部变量,即Session对象,多线程情况下共享数据库连接是不安全的。    //ThreadLocal保证了每个线程都有自己的session对象    Session session=(Session)sessionTL.get();    if (session==null) {      session=sessionFactory.openSession();      sessionTL.set(session);    }        return session;  }  //关闭session对象  public static void closeSession() {    Session session=(Session)sessionTL.get();    sessionTL.set(null);    session.close();  }}

Resume1.java实体类

package cn.zhang.entity;public class Resume1 {    private Integer resid;  private String resname;  private String rescardno;  private Users1 users1;    public Resume1(String resname, String rescardno) {    super();    this.resname = resname;    this.rescardno = rescardno;  }  public Resume1() {  }  public Integer getResid() {    return resid;  }  public void setResid(Integer resid) {    this.resid = resid;  }  public String getResname() {    return resname;  }  public void setResname(String resname) {    this.resname = resname;  }  public String getRescardno() {    return rescardno;  }  public void setRescardno(String rescardno) {    this.rescardno = rescardno;  }  public Users1 getUsers1() {    return users1;  }  public void setUsers1(Users1 users1) {    this.users1 = users1;  }  }

Users1.java实体类
package cn.zhang.entity;public class Users1 {  private Integer userid;  private String username;  private String userpass;  private Resume1 resume1;      public Users1(String username, String userpass) {    super();    this.username = username;    this.userpass = userpass;  }  public Users1() {  }  public Integer getUserid() {    return userid;  }  public void setUserid(Integer userid) {    this.userid = userid;  }  public String getUsername() {    return username;  }  public void setUsername(String username) {    this.username = username;  }  public String getUserpass() {    return userpass;  }  public void setUserpass(String userpass) {    this.userpass = userpass;  }  public Resume1 getResume1() {    return resume1;  }  public void setResume1(Resume1 resume1) {    this.resume1 = resume1;  }  }

Users1.hbm.
<??><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="cn.zhang.entity">  <class name="Users1" table="USERS1">   <id name="userid" column="USERID" >    <generator class="sequence">        <param name="sequence">SQ_Num</param>    </generator>   </id>   <property name="username" column="USERNAME" type="string"></property>   <property name="userpass" column="USERPASS" type="string"></property>   <!-- 一对一关联 -->   <!-- property-ref属性为users1,表明通过Resume1的users1的属性建立从 Users1对象到Resume1对象的关联,如果不指定,它会默认两张表的主键联系,引发错误-->   <one-to-one name="resume1" class="Resume1" property-ref="users1"></one-to-one>  </class></hibernate-mapping>

Resume1.hbm.

<??><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="cn.zhang.entity">  <class name="Resume1" table="RESUME1">   <id name="resid" column="RESID" >    <generator class="sequence">        <param name="sequence">SQ_Num</param>    </generator>   </id>   <property name="resname" column="RESNAME" type="string"></property>   <property name="rescardno" column="RESCARDNO" type="string"></property>   <!-- 一对一配置 -->   <!--column="RESUSERID": Resume1对应Users1表的外键关系 -->   <!--unique="true":表明每个Resume1对象都有唯一的Users1对象,确保唯一性-->   <!--cascade="all":级联 -->   <many-to-one name="users1" class="Users1" cascade="all" column="RESUSERID" unique="true"></many-to-one>  </class></hibernate-mapping>

hibernate.cfg.

<??><!DOCTYPE hibernate-configuration PUBLIC    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"><hibernate-configuration>  <session-factory>    <!-- Database connection settings -->    <property name="connection.driver_class">oracle.jdbc.OracleDriver</property>    <property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>    <property name="connection.username">***</property>    <property name="connection.password">***</property>    <!-- SQL dialect (SQL 方言)-->    <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>      <!-- Drop and re-create the database schema on startup -->     <property name="hbm2ddl.auto">create</property>     <!-- Echo all executed SQL to stdout 在控制台打印后台的SQL语句-->    <property name="show_sql">true</property>        <!-- 格式化显示SQL -->    <property name="format_sql">true</property>          <!-- JDBC connection pool (use the built-in) -->    <!-- <property name="connection.pool_size">1</property> -->        <!-- Enable Hibernate's automatic session context management 指定当前session范围和上下文-->    <!-- <property name="current_session_context_class">thread</property> -->        <!-- Disable the second-level cache -->    <!-- <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>-->    <mapping resource="cn/zhang/entity/Resume1.hbm. />    <mapping resource="cn/zhang/entity/Users1.hbm. />      </session-factory></hibernate-configuration>

测试类:

/**   * 一对一关联测试   */  @Test   public void testOne(){      Session session = HibernateUtil.currentSession();      Transaction tx=session.beginTransaction();      //创建一个用户对象      Users1 u1=new Users1("ZhangZong","521");      //创建一个档案对象      Resume1 r1=new Resume1("精英档案","好");      u1.setResume1(r1);      r1.setUsers1(u1);      //保存r1自动保存u1      session.save(r1);//以为Resume1设置了级联,保存r1,u1也会保存      tx.commit();      HibernateUtil.closeSession();      System.out.println("成功");   }

结果展示:

01.控制台

02.数据库

 users1表

resume1表

 

二:按照主键映射

Users2表的userid字段是主键,同时作为外键参照Resume2表的主键,即Users2表与Resume2表共享主键(Users2中的主键值是根据Resume2生成的主键值取值的)

需要提示:

 

Resume2实体类:

package cn.zhang.entity;public class Resume2 {  private Integer resid;  private String resname;  private String rescardno;  private Users2 users2;    public Resume2(String resname, String rescardno) {    super();    this.resname = resname;    this.rescardno = rescardno;  }  public Resume2() {  }  public Integer getResid() {    return resid;  }  public void setResid(Integer resid) {    this.resid = resid;  }  public String getResname() {    return resname;  }  public void setResname(String resname) {    this.resname = resname;  }  public String getRescardno() {    return rescardno;  }  public void setRescardno(String rescardno) {    this.rescardno = rescardno;  }  public Users2 getUsers2() {    return users2;  }  public void setUsers2(Users2 users2) {    this.users2 = users2;  }  }

Users2.java实体类

package cn.zhang.entity;public class Users2 {  private Integer userid;  private String username;  private String userpass;  private Resume2 resume2;      public Users2(String username, String userpass) {    super();    this.username = username;    this.userpass = userpass;  }  public Users2() {  }  public Integer getUserid() {    return userid;  }  public void setUserid(Integer userid) {    this.userid = userid;  }  public String getUsername() {    return username;  }  public void setUsername(String username) {    this.username = username;  }  public String getUserpass() {    return userpass;  }  public void setUserpass(String userpass) {    this.userpass = userpass;  }  public Resume2 getResume2() {    return resume2;  }  public void setResume2(Resume2 resume2) {    this.resume2 = resume2;  }  }

Resume2.hbm.

<??><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="cn.zhang.entity">  <class name="Resume2" table="RESUME2">  <!-- 主键的生成策略,Users2表中的主键userid会根据此来取值 -->   <id name="resid" column="RESID" >    <generator class="sequence">        <param name="sequence">SQ_Num</param>    </generator>   </id>   <property name="resname" column="RESNAME" type="string"></property>   <property name="rescardno" column="RESCARDNO" type="string"></property>   <!-- 一对一配置 -->   <one-to-one name="users2" class="Users2" cascade="all"></one-to-one>  </class></hibernate-mapping>

Users2.hbm.

<??><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="cn.zhang.entity">  <class name="Users2" table="USERS2">  <!-- foreign生成策略,根据Resume2的主键生成策略 -->   <id name="userid" column="USERID" >     <generator class="foreign">     <param name="property">resume2</param>    </generator>   </id>   <property name="username" column="USERNAME" type="string"></property>   <property name="userpass" column="USERPASS" type="string"></property>   <!-- 一对一关联 -->   <!--constrained="true":表明 Users2表的userid主键同时作为外键参照Resume2表的主键-->   <one-to-one name="resume2" class="Resume2" constrained="true"></one-to-one>  </class></hibernate-mapping>

测试:

/**   * 一对一关联测试:按照主键映射   */  @Test   public void testOne(){      Session session = HibernateUtil.currentSession();      Transaction tx=session.beginTransaction();      //创建一个用户对象      Users2 u1=new Users2("ZhangZong","521");      //创建一个档案对象      Resume2 r1=new Resume2("精英档案","好");      u1.setResume2(r1);      r1.setUsers2(u1);      //保存r1自动保存u1      session.save(r1);      tx.commit();      HibernateUtil.closeSession();      System.out.println("成功");   }

结果展示:

01.控制台

 

02.数据库

Resume2表

Users2表

 三:组件的映射

建立关系数据模型的一个重要原则是在不会导致数据冗余的前提下,尽可能减少数据库表中的数目及表之间的外键参照关系。以员工信息为例,员工信息中有员工的家庭地址信息,如果把地址信息单独放在一张表中,然后建立员工信息表和地址信息表之间的外键关系,当每次查询员工信息时,都需要建立这两个表的连接。建立表的连接是很耗时的操作,为了提高数据库运行性能,可以把这两张表的信息整合在一张员工信息表Empinfo中

 

需要提示:

 

EmpHomeAddress.java实体类

package cn.zhang.entity;//员工地址信息实体类public class EmpHomeAddress {  private String ehomestreet;  private String ehomecity;  private String ehomeprovince;  private String ehomezipcode;  private EmpInfo empinfo;  public String getEhomestreet() {    return ehomestreet;  }  public void setEhomestreet(String ehomestreet) {    this.ehomestreet = ehomestreet;  }  public String getEhomecity() {    return ehomecity;  }  public void setEhomecity(String ehomecity) {    this.ehomecity = ehomecity;  }  public String getEhomeprovince() {    return ehomeprovince;  }  public void setEhomeprovince(String ehomeprovince) {    this.ehomeprovince = ehomeprovince;  }  public String getEhomezipcode() {    return ehomezipcode;  }  public void setEhomezipcode(String ehomezipcode) {    this.ehomezipcode = ehomezipcode;  }  public EmpInfo getEmpinfo() {    return empinfo;  }  public void setEmpinfo(EmpInfo empinfo) {    this.empinfo = empinfo;  }  }

EmpInfo.java实体类

package cn.zhang.entity;//员工实体类public class EmpInfo {  private Integer eid;  private String ename;  private EmpHomeAddress ehome;//地址信息  public Integer getEid() {    return eid;  }  public void setEid(Integer eid) {    this.eid = eid;  }  public String getEname() {    return ename;  }  public void setEname(String ename) {    this.ename = ename;  }  public EmpHomeAddress getEhome() {    return ehome;  }  public void setEhome(EmpHomeAddress ehome) {    this.ehome = ehome;  }  }

EmpInfo.hbm.

<??><!DOCTYPE hibernate-mapping PUBLIC   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="cn.zhang.entity">  <class name="EmpInfo" table="EMPINFO">    <id name="eid" column="EID">    <generator class="native"></generator>    </id>    <property name="ename" column="ENAME" type="string"></property>    <!--组件的映射配置 -->    <!--component元素表明 ehome属性是EmpInfo类的一个组成部分      name设定被映射的持久化类的属性名       -->       <component name="ehome" class="EmpHomeAddress">    <!-- parent指定EmpHomeAddress类所属的整体类 -->     <parent name="empinfo"/>     <!-- 基本映射 -->     <property name="ehomestreet" column="EHOMESTREET" type="string"></property>     <property name="ehomecity" column="EHOMECITY" type="string"></property>     <property name="ehomeprovince" column="EHOMEPROVINCE" type="string"></property>     <property name="ehomezipcode" column="EHOMEZIPCODE" type="string"></property>    </component>  </class> </hibernate-mapping>

测试类:

/**   * 组件的映射测试   */  @Test   public void testOne(){      Session session = HibernateUtil.currentSession();      Transaction tx=session.beginTransaction();      //创建一个员工对象      EmpInfo emp=new EmpInfo();      emp.setEname("张总");           //创建一个员工地址对象      EmpHomeAddress address=new EmpHomeAddress();      address.setEhomecity("北京");      address.setEhomeprovince("北京");      address.setEhomestreet("五道口");      address.setEhomezipcode("100000");      address.setEmpinfo(emp);      emp.setEhome(address);      session.save(emp);      tx.commit();      HibernateUtil.closeSession();      System.out.println("成功");   }

测试结果展示:

01.控制台

 

02.数据库

EMPINFO表: