SSH学习-Hibernate关联查询

与MyBatis类似,Hibernate也有关联查询,数据库中表与表之间的关联关系使用主外键的形式体现,实体对象之间的关联关系体现在对象与对象的引用。在Mybatis中,当涉及到复杂查询并且表之间存在关联关系时,查询的返回类型如果是resultType不能满足要求,需要使用ResultMap并在其中进行配置,当查询的结果为一个实体类时,使用association标签,当查询的结果是一个集合时,使用collection标签,具体参考自己写的博客。本文将关联查询user和book,来学习Hibernate关联查询。

关联查询步骤

(1)创建实体类user/book,建立两张表t_user和t_book

实体类user和book:

package Entity;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class user implements Serializable{

    private static final long serialVersionUID = 2697475450509781362L;
    private int id;
    private String name;
    private Set<book> books=new HashSet<book>();//一个用户对应多个书名
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public user() {
        super();
    }        
    
    public Set<book> getBooks() {
        return books;
    }
    public void setBooks(Set<book> books) {
        this.books = books;
    }    
    
    @Override
    public String toString() {
        return "user [id=" + id + ", name=" + name + ", books=" + books + "]";
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        user other = (user) obj;
        if (id != other.id)
            return false;
        return true;
    }
    
}
View Code
package Entity;

import java.io.Serializable;

public class book implements Serializable{

    private static final long serialVersionUID = -5148507992206736063L;

    private int id;
    private String name;
    private user user;//多个书名对应一个用户
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    public book() {
        super();
    }
        
    public user getUser() {
        return user;
    }
    public void setUser(user user) {
        this.user = user;
    }
        
    public book(int id, String name, Entity.user user) {
        super();
        this.id = id;
        this.name = name;
        this.user = user;
    }
        
    @Override
    public String toString() {
        return "book [id=" + id + ", name=" + name + ", user=" + user.getName() + "]";//切断死循环使用
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        book other = (book) obj;
        if (id != other.id)
            return false;
        return true;
    }    
        
}
View Code

(2)配置映射文件user.hbm.xml和book.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">    
<!-- 配置映射文件,名字一般用实体类类名.hbm.xml -->
<hibernate-mapping>

   <!-- 创建表和实体类的映射关系 -->
   <class name="Entity.user" table="t_user">
      <!-- 配置主键id -->
      <id name="id" column="id" type="int">
        <!-- MySql中设置了主键自增,使用identity生成策略 -->
        <generator class="identity"></generator>
      </id>
      <!-- 配置name属性 -->
      <property name="name" column="name" type="string"></property>
      <!-- 配置book属性 -->
      <!-- user和book的关系是一对多,采用one-to-many标签 -->
      <!-- lazy设置为false,代表不懒惰加载,默认是懒惰加载 -->
      <set name="books" lazy="false"><!-- 实体类对应属性名 -->
        <key column="user_id"></key><!-- 通过user_id关联查询,可以查到用户 -->
        <one-to-many class="Entity.book"></one-to-many>
      </set>
   
   </class>
</hibernate-mapping>  

配置user.hbm.xml,配置id和name很好理解,数据库和表中一一对应就好,但是当需要查询得到user的books属性时,就变得稍微复杂一点,因为user实体类对应的表t_user并没有books相关的字段,需要解决得到book就需要使用关联查询得到,并且关联查询的条件是t_user.id=t_book.user_id,因此配置中column属性就为user_id,代表用它关联查询得到book,里面需要使用one-to-many关键字,代表一个user可能对应多个book。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">    
<!-- 配置映射文件,名字一般用实体类类名.hbm.xml -->
<hibernate-mapping>

  <!-- 创建表和实体类的映射关系 -->
  <class name="Entity.book" table="t_book"><!-- 注意类名要写包名.类名 -->
      <!-- 配置主键id -->
      <id name="id" column="id" type="int">
         <!-- MySql中设置了主键自增,使用identity生成策略 -->
         <generator class="identity"></generator>
      </id>
      <!-- 配置name属性 -->
      <property name="name" column="name" type="string"></property>
      <!-- 配置user属性 -->
      <!-- user和book的关系是多对一的关系,使用many-to-one标签,此外要想得到user,需要使用表中的字段user_id关联 -->
      <!-- 如果返回的类型是一个实体类,使用class属性来指定 -->
      <many-to-one name="user" column="user_id" class="Entity.user"></many-to-one>
  </class>
 
</hibernate-mapping>  

配置book.hbm.xml,与上面类似,配置id和name容易理解,配置user属性时,也需要使用user_id来关联查询。

hibernate.cfg.xml配置:

<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!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>
       
       <!-- 指定数据库方言,用这个可以切换连接不同的数据库 -->
       <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
       <!-- 测试连接oracle -->
       <!--  <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property> -->
       <!-- 连接数据库 -->      
       <!-- 驱动,url,用户名和密码 -->
         
       <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
       <property name="connection.url">jdbc:mysql://localhost:3306/ssh</property>
       <property name="connection.username">root</property>
       <property name="connection.password">2688</property> 
       
       <!-- 连接oracle测试 -->
       <!--  
       <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
       <property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
       <property name="connection.username">SCOTT</property>
       <property name="connection.password">Yang2688</property>-->
       
       
       <!-- 显示Hibernate持久层操作所使用的SQL -->
       <property name="show_sql">true</property>
       <!-- 将脚本格式化后再进行输出,sql developer中的ctrl+F7就是快速格式化 -->
       <property name="hibernate.format_sql">true</property>
       
       
       <!-- 设置Mapping映射资源文件位置 -->
       <mapping resource="user.hbm.xml"/>
       <mapping resource="book.hbm.xml"/>


   </session-factory>
</hibernate-configuration>
View Code

(3)实现测试方法,查询id=1的user用户数据

package TestCase;

import java.util.List;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import Entity.book;
import Entity.user;

/**
 * 测试Hibernate关联查询
 * @author yangchaolin
 *
 */
public class testHibernate {
    
      public static Session getSession() {
          //读取hibernate.cfg.xml配置文件
          Configuration cfg=new Configuration();
          cfg.configure("hibernate.cfg.xml");
          //创建sessionfactory
          SessionFactory factory=cfg.buildSessionFactory();
          //创建session
          Session session=factory.openSession();
          return session;
      }

      //测试获取user id为1的用户的信息
      @Test
      public void test() {
          //读取hibernate.cfg.xml配置文件
          Configuration cfg=new Configuration();
          cfg.configure("hibernate.cfg.xml");
          //创建sessionfactory
          SessionFactory factory=cfg.buildSessionFactory();
          //创建session
          Session session=factory.openSession();
          //创建一个事务并开启事务
          Transaction trans=session.getTransaction();
          trans.begin();
          //开始执行持久层操作
          user user=(user) session.get(user.class, 1);//1代表user的id属性值
          System.out.println(user.getId());
          System.out.println(user.getName());
          Set<book> books=user.getBooks();
          for(book book:books) {
              System.out.println(book);
              /**
               * 这里输出book信息时,会报StackOverFlowError,为内存溢出,主要原因为陷入了死循环。
               * (1)输出book,会调用book类的toString方法,发现toString方法里需要输出user
               * (2)为了输出user,会调用user类的toString方法,发现需要输出books,其中books是一个集合
               * (3)为了输出集合books,会调用集合类底层的toString方法,其中集合底层是用ArrayList保存数据,调用ArrayList的toString方法还是会调用book
               * (4)为了输出底层集合的book,又会继续跳回到第一步,如此反复执行造成栈溢出。
               * 解决办法:切断死循环
               * 
               */
          }
          //关闭session
          session.close();
      }
           
}

在查询打印出book的时候,刚开始报警栈溢出,原因为陷入了死循环,打印book时调用其toString方法需要输出user,然后继续调用user类的toString方法,user类的toString方法里要输出books集合,集合底层使用的是ArrayList保存对象,输出集合的时候也会调用其下面的toString方法,输出单个的book对象,然后又陷入了需要打印user老路,因此陷入了死循环造成栈溢出。需要切换死循环只需要任意切断其中一个线路,本例就打印user时只打印user的name来切断。

测试结果

 

可以看出来是一次性查询出所有结果,这是因为将与懒惰加载相关的lazy属性设置了false,因此没有懒惰加载一次性查询出所有数据。

结论

与MyBatis类似,Hibernate中也有关联查询,只是使用的关键字不太一样,Hibernate中常用就是one-to-many和many-to-one标签,column属性中写上关联查询的关键字。

原文地址:https://www.cnblogs.com/youngchaolin/p/10909766.html