Hibernate 一对多双向映射及乐观锁使用

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:
在“ Hibernate关联关系映射实例速查”一文中,通过myeclipse5.5,快速做出了Hibernate各种映射的示例。时隔快一年了,但是还是有博友向我索要工程源码,很遗憾的是已经找不到了。但找到一了一个测试代码:对双向关联和乐观锁的测试。其实映射类型很多,搞清楚一对多,基本上所有的映射就搞明白了,一对一也是一对多的特例而已,多对多也可以转换为一对多和多对一,并且实际中很少用到多对多。
 
还是老规矩,因为是测试,代码几乎全部是myeclipse生成的,我稍作了修改。并且应博友“阿飞”的留言,我做了详细的注释。
 
例子两部分:
1、一对多双向映射:模型是“班级-学生”模型。两个实体分别是Tclass和Student。
2、乐观锁的是使用,版本分别使用递增整数和时间戳。两个实体分别是Foo和Bar。
 
 
Tclass实体及其映射:
public  class Tclass  implements java.io.Serializable { 

     // Fields 

     private Long cid; 

     private String cname; 

     private Set students =  new HashSet(0); 

     // Constructors 
     // Property accessors 
    ....... 

     public String toString() { 
         return  "Tclass{" + 
                 "cid=" + cid + 
                 ", cname='" + cname + '\'' + 
                '}'; 
    } 
}
 
< hibernate-mapping > 
     < class  name ="stu.one2many.pojo.Tclass"  table ="tclass" > 
         < id  name ="cid"  type ="java.lang.Long" > 
             < column  name ="cid" /> 
             < generator  class ="native" /> 
         </ id > 
         < property  name ="cname"  type ="java.lang.String" > 
             < column  name ="cname"  length ="24"  not-null ="true" /> 
         </ property > 
        <!--  set元素属性说明: 
           name="students" 设置表示多个学生的变量名; 
           inverse="true" 关系控制反转,不掌握主控权,表示Tclass不控制与Student关联关系,而是将这种关联控制的权利转给Student。 
           cascade="all" 表示级联操作,操作班级的时候,对班级关联的学生也做同样的操作。 
           lazy="true" 查询班级的时候,延迟查询班级下的学生。 
        
--> 
         < set  name ="students"  inverse ="true"  cascade ="all"  lazy ="true" > 
             < key > 
                <!--  
                    name="fk_cid" 指定关联的外键列; 
                    not-null="true" 说明这个外间列不能为空,多余的。 
                
--> 
                 < column  name ="fk_cid"  not-null ="true" /> 
             </ key > 
            <!--  指定所关联的类 --> 
             < one-to-many  class ="stu.one2many.pojo.Student" /> 
         </ set > 
     </ class > 
</ hibernate-mapping >
 
Student实体及其映射
public  class Student  implements java.io.Serializable { 

     // Fields 

     private Long sid; 

     private Tclass tclass; 

     private String sname; 
  

     // Constructors 
     // Property accessors 
    ....... 

     public String toString() { 
         return  "Student{" + 
                 "sid=" + sid + 
                 ", sname='" + sname + '\'' + 
                '}'; 
    } 
}
 
< hibernate-mapping > 
     < class  name ="stu.one2many.pojo.Student"  table ="student" > 
         < id  name ="sid"  type ="java.lang.Long" > 
             < column  name ="sid"  /> 
             < generator  class ="native"  /> 
         </ id > 
        <!--  表示多个Student关联一个Tclass --> 
        <!--   
            name="tclass" 关联的成员变量名; 
            class="stu.one2many.pojo.Tclass" 表示所关联的类; 
            fetch="select" 查询策略,有两个选项select和join, 
          select表示通过外联接来进行查询,查询速度稍慢,但消耗资源少; 
          join表示通过内连接来查询,速度快,但消耗资源多. 
         
--> 
         < many-to-one  name ="tclass"  
             class ="stu.one2many.pojo.Tclass"  
             fetch ="select" > 
            <!--  指定关联的外键列 --> 
             < column  name ="fk_cid"  not-null ="true"  /> 
         </ many-to-one > 
         < property  name ="sname"  type ="java.lang.String" > 
             < column  name ="sname"  length ="24"  not-null ="true"  /> 
         </ property > 
     </ class > 
</ hibernate-mapping >
 
测试班级学生模型:
public  class Test { 

     /** 
     * @param args 
     */
 
     public  static  void main(String[] args) { 
        testSave(); 
//        testDeleteTclass(); 

    } 

     public  static  void testSave() { 
        Tclass c =  new Tclass(); 
        c.setCname( "某班级"); 
        Student s1 =  new Student(); 
        Student s2 =  new Student(); 
        s1.setSname( "张三"); 
        s1.setTclass(c); 
        s2.setSname( "李四"); 
        s2.setTclass(c); 
        c.getStudents().add(s1); 
        c.getStudents().add(s2); 

        Session session = HibernateSessionFactory.getSession(); 
        Transaction tx = session.beginTransaction(); 
        session.save(c); 
        tx.commit(); 
        session.close(); 
    } 

     public  static  void testUpdateClass() { 
        System.out.println( "----------------正在调用testUpdateClass()----------------"); 
        Session session = HibernateSessionFactory.getSession(); 
        Tclass c = (Tclass) session.load(Tclass. class, Long.valueOf(1L)); 
        System.out.println(c); 
        c.setCname( "班级更名"); 
        session.beginTransaction().commit(); 
    } 

     public  static  void testUpdateStudent() { 
        System.out.println( "----------------正在调用testUpdateStudent()----------------"); 
        Session session = HibernateSessionFactory.getSession(); 
        Tclass c = (Tclass) session.load(Tclass. class, Long.valueOf(3L)); 
        Student s = (Student) session.load(Student. class, Long.valueOf(2L)); 
        s.setSname( "学生改名换姓-王八"); 
        s.setTclass(c); 
        System.out.println(c); 
        System.out.println(s); 
        session.beginTransaction().commit(); 
        System.out.println(s); 
        System.out.println(s.getTclass()); 
    } 

     public  static  void testDeleteStudent() { 
        System.out.println( "----------------正在调用testDelete()----------------"); 
        Session session = HibernateSessionFactory.getSession(); 
        Student s = (Student) session.load(Student. class, Long.valueOf(5L)); 
        System.out.println(s); 
        System.out.println(s.getTclass()); 
        session.delete(s); 
        session.beginTransaction().commit(); 
    } 

     public  static  void testDeleteTclass() { 
        System.out.println( "----------------正在调用testDelete()----------------"); 
        Session session = HibernateSessionFactory.getSession(); 
        Tclass c = (Tclass) session.load(Tclass. class, Long.valueOf(3L)); 
        System.out.println(c); 
        session.delete(c); 
        session.beginTransaction().commit(); 
    } 

     public  static  void testQueryClass() { 
        System.out.println( "----------------正在调用testQueryClass()----------------"); 
        Session session = HibernateSessionFactory.getSession(); 
        Tclass c = (Tclass) session.load(Tclass. classnew Long( "1")); 
        System.out.println(c); 
        System.out.println(c.getStudents()); 

    } 

     public  static  void testQueryStudent() { 
        System.out.println( "----------------正在调用testQueryStudent()----------------"); 
        Session session = HibernateSessionFactory.getSession(); 
        Student s = (Student) session.load(Student. classnew Long( "1")); 
        System.out.println(s); 
        System.out.println(s.getTclass()); 
    } 
}
 
 
下面是乐观锁的使用:
1、基于整数的版本控制
 
Foo实体和映射文件
public  class Foo  implements java.io.Serializable { 

     // Fields 

     private Long pid; 

     private Integer version; 

     private String name; 

     // Constructors 
     // Property accessors 
    ....... 

     public String toString() { 
         return  "Foo{" + 
                 "pid=" + pid + 
                 ", version=" + version + 
                 ", name='" + name + '\'' + 
                '}'; 
    } 
}
 
< hibernate-mapping > 
     < class  name ="stu.one2many.pojo.Foo"  table ="foo"  
            optimistic-lock ="version" > 
         < id  name ="pid"  type ="java.lang.Long" > 
             < column  name ="pid"  /> 
             < generator  class ="native"  /> 
         </ id > 
        <!--  版本控制字段必须在id后配置 --> 
         < version  name ="version"  type ="java.lang.Integer" > 
             < column  name ="version"  /> 
         </ version > 
         < property  name ="name"  type ="java.lang.String" > 
             < column  name ="name"  length ="24"  not-null ="true"  /> 
         </ property > 
     </ class > 
</ hibernate-mapping >
 
测试:
public  class TestFoo { 

     /** 
  * @param args 
  */
 
     public  static  void main(String[] args) { 
  testSave(); 
   
    } 
     public  static  void testSave(){ 
  Foo foo1 =  new Foo( "foo1"); 
   
  Session session = HibernateSessionFactory.getSession(); 
  session.save(foo1); 
  session.beginTransaction().commit(); 
        session.close(); 
    } 
}
 
2、基于时间戳的版本控制
public  class Bar  implements java.io.Serializable, Comparable { 

     // Fields     

     private Long id; 
     private Date timestamp; 
     private String name; 

     // Constructors 
     // Property accessors 
    ....... 

     public String toString() { 
         return  "Bar{" + 
                 "id=" + id + 
                 ", timestamp=" + timestamp + 
                 ", name='" + name + '\'' + 
                '}'; 
    } 

     /** 
     * 排序接口方法实现,为了能对查询结果按照id的大小进行排序 
     * @param o 排序对象 
     * @return 比较值 
     */
 
     public  int compareTo(Object o) { 
        Bar bar = (Bar) o; 
        Long res =  this.id - bar.getId(); 
         return res.intValue(); 
    } 
}
 
< hibernate-mapping > 
     < class  name ="stu.one2many.pojo.Bar"  table ="bar"  optimistic-lock ="version" > 
         < id  name ="id"  type ="java.lang.Long" > 
             < column  name ="id"  /> 
             < generator  class ="native"  /> 
         </ id > 
         < version  name ="timestamp"  type ="java.util.Date" > 
             < column  name ="timestamp"  length ="0"  not-null ="true"  /> 
         </ version > 
         < property  name ="name"  type ="java.lang.String" > 
             < column  name ="name"  length ="24"  not-null ="true"  /> 
         </ property > 
     </ class > 
</ hibernate-mapping >
 
public  class TestBar { 
     public  static  void main(String args[]) { 
        testUpdateBar(); 
        testQueryBar(); 
    } 

     public  static  void testSaveBar() { 
        Bar bar =  new Bar( "bar"); 
        Session session = HibernateSessionFactory.getSession(); 
        session.save(bar); 
        session.beginTransaction().commit(); 
        session.close(); 
    } 

     public  static  void testQueryBar() { 
        Session session = HibernateSessionFactory.getSession(); 
        String hql =  "from Bar"
        Query query = session.createQuery(hql); 
        List<Bar> barList = query.list(); 
        Collections.sort(barList); 
         for (Bar bar : barList) { 
            System.out.println(bar.getId() +  ":\t" + bar.getTimestamp().getTime()); 
        } 
        session.close(); 
    } 

     public  static  void testUpdateBar() { 
        Session session = HibernateSessionFactory.getSession(); 
        String hql =  "from Bar"
        Query query = session.createQuery(hql); 
        List<Bar> barList = query.list(); 
         for (Bar bar : barList) { 
            bar.setName( "newBar"); 
        } 
        session.beginTransaction().commit(); 
        session.close(); 
    } 
}
 
public  class TestStack { 
     public  static  void main(String args[]){ 
        test(); 
    } 
     public  static  void  test(){ 
        Stack stack =  new Stack(); 
        String s1=  "1"
        String s2= "2"
        String s3=  "3"
        String s4=  "4"
        stack.push(s1); 
        stack.push(s2); 
        stack.push(s3); 
        stack.push(s4); 

         for(;!stack.isEmpty();){ 
            System.out.println(stack.pop()); 
        } 

         //for语句先判断是否符合条件,然后确定是否执行循环 
         for( int i=0;i>10;i--){ 
            System.out.println( ">>> "+i); 
        } 
    } 
}
 
下面是SessionFactory工具和hibernate配置文件:
import org.hibernate.HibernateException; 
import org.hibernate.Session; 
import org.hibernate.cfg.Configuration; 

/** 
* Configures and provides access to Hibernate sessions, tied to the 
* current thread of execution.  Follows the Thread Local Session 
* pattern, see {@link [url]http://hibernate.org/42.html[/url] }. 
*/
 
public  class HibernateSessionFactory { 

     /**  
     * Location of hibernate.cfg.xml file. 
     * Location should be on the classpath as Hibernate uses   
     * #resourceAsStream style lookup for its configuration file.  
     * The default classpath location of the hibernate config file is  
     * in the default package. Use #setConfigFile() to update  
     * the location of the configuration file for the current session.    
     */
 
     private  static String CONFIG_FILE_LOCATION =  "/hibernate.cfg.xml"
     private  static  final ThreadLocal<Session> threadLocal =  new ThreadLocal<Session>(); 
     private   static Configuration configuration =  new Configuration(); 
     private  static org.hibernate.SessionFactory sessionFactory; 
     private  static String configFile = CONFIG_FILE_LOCATION; 

     static { 
         try { 
      configuration.configure(configFile); 
      sessionFactory = configuration.buildSessionFactory(); 
  }  catch (Exception e) { 
      System.err 
        .println( "%%%% Error Creating SessionFactory %%%%"); 
      e.printStackTrace(); 
  } 
    } 
     private HibernateSessionFactory() { 
    } 
  
     /** 
     * Returns the ThreadLocal Session instance.  Lazy initialize 
     * the <code>SessionFactory</code> if needed. 
     * 
     *  @return Session 
     *  @throws HibernateException 
     */
 
     public  static Session getSession()  throws HibernateException { 
        Session session = (Session) threadLocal.get(); 

   if (session ==  null || !session.isOpen()) { 
       if (sessionFactory ==  null) { 
    rebuildSessionFactory(); 
      } 
      session = (sessionFactory !=  null) ? sessionFactory.openSession() 
        :  null
      threadLocal.set(session); 
  } 

         return session; 
    } 

     /** 
     *  Rebuild hibernate session factory 
     * 
     */
 
     public  static  void rebuildSessionFactory() { 
   try { 
      configuration.configure(configFile); 
      sessionFactory = configuration.buildSessionFactory(); 
  }  catch (Exception e) { 
      System.err 
        .println( "%%%% Error Creating SessionFactory %%%%"); 
      e.printStackTrace(); 
  } 
    } 

     /** 
     *  Close the single hibernate session instance. 
     * 
     *  @throws HibernateException 
     */
 
     public  static  void closeSession()  throws HibernateException { 
        Session session = (Session) threadLocal.get(); 
        threadLocal.set( null); 

         if (session !=  null) { 
            session.close(); 
        } 
    } 

     /** 
     *  return session factory 
     * 
     */
 
     public  static org.hibernate.SessionFactory getSessionFactory() { 
   return sessionFactory; 
    } 

     /** 
     *  return session factory 
     * 
     *    session factory will be rebuilded in the next call 
     */
 
     public  static  void setConfigFile(String configFile) { 
  HibernateSessionFactory.configFile = configFile; 
  sessionFactory =  null
    } 

     /** 
     *  return hibernate configuration 
     * 
     */
 
     public  static Configuration getConfiguration() { 
   return configuration; 
    } 

}

 
<? 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/testdb 
   </ property > 
   < property  name ="dialect" > 
      org.hibernate.dialect.MySQLDialect 
   </ property > 
   < property  name ="myeclipse.connection.profile" > 
      com.mysql.jdbc.Driver 
   </ property > 
   < property  name ="connection.password" >leizhimin </ property > 
   < property  name ="connection.driver_class" > 
      com.mysql.jdbc.Driver 
   </ property > 
   < property  name ="show_sql" >true </ property > 
  <!-- <property name="format_sql">true</property>--> 
         < property  name ="hbm2ddl.auto" >create </ property > 


         < mapping  resource ="stu/one2many/pojo/Tclass.hbm.xml"  /> 
   < mapping  resource ="stu/one2many/pojo/Student.hbm.xml"  /> 
   < mapping  resource ="stu/one2many/pojo/Foo.hbm.xml" > </ mapping > 
   < mapping  resource ="stu/one2many/pojo/Bar.hbm.xml"  /> 

     </ session-factory > 

</ hibernate-configuration >
 
数据库用的是mysql5,sql脚本我导出了一份如下:
/* 
SQLyog Enterprise - MySQL GUI v6.5 
MySQL - 5.0.45-community-nt : Database - testdb 
********************************************************************* 
*/
 


/*!40101 SET NAMES utf8 */

/*!40101 SET SQL_MODE=''*/

/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */

create  database  if  not  exists testdb; 

USE testdb; 

/*Table structure for table bar */ 

DROP  TABLE  IF  EXISTS bar; 

CREATE  TABLE bar ( 
  id  bigint(20)  NOT  NULL auto_increment, 
   timestamp  datetime  NOT  NULL
   name  varchar(24)  NOT  NULL
   PRIMARY  KEY  (id) 
) ENGINE=InnoDB  DEFAULT CHARSET=gbk; 

/*Table structure for table foo */ 

DROP  TABLE  IF  EXISTS foo; 

CREATE  TABLE foo ( 
  pid  bigint(20)  NOT  NULL auto_increment, 
  version  int(11)  NOT  NULL
   name  varchar(24)  NOT  NULL
   PRIMARY  KEY  (pid) 
) ENGINE=InnoDB  DEFAULT CHARSET=gbk; 

/*Table structure for table student */ 

DROP  TABLE  IF  EXISTS student; 

CREATE  TABLE student ( 
  sid  bigint(20)  NOT  NULL auto_increment, 
  fk_cid  bigint(20)  NOT  NULL
  sname  varchar(24)  NOT  NULL
   PRIMARY  KEY  (sid), 
   KEY FK8FFE823B3AA29689 (fk_cid), 
   CONSTRAINT FK8FFE823B3AA29689  FOREIGN  KEY (fk_cid)  REFERENCES tclass (cid) 
) ENGINE=InnoDB AUTO_INCREMENT=3  DEFAULT CHARSET=gbk; 

/*Table structure for table tclass */ 

DROP  TABLE  IF  EXISTS tclass; 

CREATE  TABLE tclass ( 
  cid  bigint(20)  NOT  NULL auto_increment, 
  cname  varchar(24)  NOT  NULL
   PRIMARY  KEY  (cid) 
) ENGINE=InnoDB AUTO_INCREMENT=2  DEFAULT CHARSET=gbk; 

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */
 
具体测试运行的结果运行下即可看到。
 
源码压缩包太大4M多,我删除了所有引用的包。使用的是hibernate3.1
 

本文转自 leizhimin 51CTO博客,原文链接:http://blog.51cto.com/lavasoft/78916,如需转载请自行联系原作者
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
Java 数据库连接 API
解锁高效开发秘籍:深入探究 Hibernate 如何优雅处理一对多与多对多关系,让数据映射再无烦恼!
【9月更文挑战第3天】Hibernate 是 Java 领域中最流行的 ORM 框架之一,广泛用于处理实体对象与数据库表之间的映射。尤其在处理复杂关系如一对多和多对多时,Hibernate 提供了丰富的 API 和配置选项。本文通过具体代码示例,展示如何使用 `@OneToMany`、`@JoinColumn`、`@ManyToMany` 和 `@JoinTable` 等注解优雅地实现这些关系,帮助开发者保持代码简洁的同时确保数据一致性。
64 4
|
4月前
|
Java 数据库连接 数据库
AI 时代风起云涌,Hibernate 实体映射引领数据库高效之路,最佳实践与陷阱全解析!
【8月更文挑战第31天】Hibernate 是一款强大的 Java 持久化框架,可将 Java 对象映射到关系数据库表中。本文通过代码示例详细介绍了 Hibernate 实体映射的最佳实践,包括合理使用关联映射(如 `@OneToMany` 和 `@ManyToOne`)以及正确处理继承关系(如单表继承)。此外,还探讨了常见陷阱,例如循环依赖可能导致的无限递归问题,并提供了使用 `@JsonIgnore` 等注解来避免此类问题的方法。通过遵循这些最佳实践,可以显著提升开发效率和数据库操作性能。
91 0
|
4月前
|
数据库 开发者 Java
Hibernate映射注解的魔力:实体类配置的革命,让你的代码量瞬间蒸发!
【8月更文挑战第31天】Hibernate 是一款出色的对象关系映射框架,简化了 Java 应用与数据库的交互。其映射注解让实体类配置变得直观简洁。本文深入剖析核心概念与使用技巧,通过示例展示如何简化配置。
55 0
|
6月前
|
Java 数据库连接 数据库
JPA和Hibernate的乐观锁与悲观锁
木头左讲解JPA和Hibernate中的乐观锁与悲观锁。乐观锁在并发更新少、冲突处理成本高、数据一致性要求不严的场景下适用,利用`@Version`注解防止冲突。而悲观锁适合并发更新频繁、处理冲突成本低、需高度数据一致性的场景,通过`@Lock`注解实现锁机制。选择合适的锁策略对提升数据库性能和保证数据一致性至关重要。
JPA和Hibernate的乐观锁与悲观锁
|
6月前
|
Java 数据库连接 API
解锁你的数据库:JPA和Hibernate的乐观锁与悲观锁
本文由木头左介绍JPA和Hibernate中的乐观锁与悲观锁。乐观锁假设无冲突,通过`@Version`注解防止并发更新,适用于更新不频繁、处理冲突成本高、数据一致性要求不高的场景。悲观锁假设有冲突,利用`@Lock`注解实现加锁,适用于并发更新频繁、处理冲突成本低、数据一致性要求高的情况。选择哪种锁取决于具体需求。
解锁你的数据库:JPA和Hibernate的乐观锁与悲观锁
|
SQL XML 存储
Hibernate框架【五】——基本映射——多对多映射
Hibernate框架【五】——基本映射——多对多映射
203 0
|
7月前
|
缓存 Java 数据库连接
Hibernate或MyBatis:ORM映射、缓存机制等知识讲解梳理
Hibernate或MyBatis:ORM映射、缓存机制等知识讲解梳理
133 0
|
7月前
|
Java 数据库连接 数据库
Hibernate5中实体映射命名策略
Hibernate5中实体映射命名策略
142 0
|
7月前
|
SQL 存储 Java
Hibernate - 继承关联关系映射
Hibernate - 继承关联关系映射
75 0
|
7月前
|
SQL Java 关系型数据库
Hibernate - Java 类型, Hibernate 映射类型及 SQL 类型之间的对应关系
Hibernate - Java 类型, Hibernate 映射类型及 SQL 类型之间的对应关系
72 0