• 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属性中写上关联查询的关键字。

  • 相关阅读:
    IIS发布问题解决
    创建Core项目使用IdentityServer4
    通过数据库名称字符串 反射获取数据并刷新对应缓存
    MVC模式下unity配置,报错“No connection string named '**Context' could be found in the application config file”
    Docker巨轮的航行之路-基础知识篇
    C#之LINQ
    Js调试中不得不知的Console
    jQuery中是事件绑定方式--on、bind、live、delegate
    前端常用技术概述--Less、typescript与webpack
    ES6学习之变量的解构赋值
  • 原文地址:https://www.cnblogs.com/youngchaolin/p/10909766.html
Copyright © 2020-2023  润新知