精华内容
下载资源
问答
  • 算法学习——求一个集合有多少种等价关系(递归) 等价关系,举个栗子:集合A{1,2,3},求它的等价关系就是{{1},{2},{3}},{{1, 2}, {3}},{{1,3},{2}},{{2,3},{1}},{{1,2,3}},就是每个集合的并集为A...

    算法学习——求一个集合有多少种等价关系(递归)

    等价关系,举个栗子:集合A{1,2,3},求它的等价关系就是{{1},{2},{3}}{{1, 2}, {3}}{{1,3},{2}}{{2,3},{1}}{{1,2,3}},就是每个集合的并集为A,且每个集合彼此没有交集。

    以此计算集合B{1,2,3,4}就有15中等价关系。

    那么我们怎样求呢?

    思考这样一个方法,比如我们有4个苹果,我们要保证每个苹果都有地方落脚,一共有多少种落脚的情况呢?首先我们能看出最多我们要用到四个盘子(一个苹果一个盘子);四个盘子的情况下,我们可以先拿出来一号苹果,将它放入一个盘子里,让其他的苹果放到其余3个盘子里,这是一种情况,我们还可以将3个苹果放入4个盘子里,然后剩余的一个苹果就有了4种选择;这是外层循环,其余的苹果放到盘子中,也可以用这种方法来放,递归就形成了,那么问题来了,递归的出口在哪里?就像求全排列一样,当我们只剩下了一个盘子我们就没法再通过递归分了,所以出口就出现了!由于四个苹果放到四个盘子里的情况只有一种,也可以做出口。

    这是bell数,有兴趣的同学可以去搜一下了解、理解一下。

    #include<iostream>
    #include<string>
    using namespace std;
    int fun(int n,int m)
    {
        if(m==1||n==m)return 1;
        else
           return fun(n-1,m-1)+fun(n-1,m)*m;
    }
    int main()
    {
        int n;
        while(cin>>n&&n>=1)
        {
           int sum=0;
           for(int i=1;i<=n;i++)
            sum+=fun(n,i);
           cout<<sum<<endl;
        }
        return 0;
    }
    

    如有问题欢迎指正!

    wish you have a good day!

     

    展开全文
  • 在一个集合定义一个等价关系...Bell数没有通项公式,但我们一个递推公式: B(n+1)=C(0,n)B(0)+C(1,n)B(1)+...+C(n,n)B(n),C(k,n)就是在n个数里选k的数的选法个数. 这个很好证明:取第n+1个数,并考虑除了含有它的那...

    在一个集合定义一个等价关系相当于把这个集合划分成许多子集的集.(这里假如不懂请追问)
    于是求等价关系的数目,就是求划分的数目.
    这其实是个定理,这个数叫Bell数.
    Bell数没有通项公式,但我们有一个递推公式:
    B(n+1)=C(0,n)B(0)+C(1,n)B(1)+...+C(n,n)B(n),C(k,n)就是在n个数里选k的数的选法个数.
    这个很好证明:取第n+1个数,并考虑除了含有它的那个部分以外所有其他的部分.含有它的部分的元素个数从1到n+1都有可能,而剩下的数就是从n到0.而每次我们可以挑选剩下来的数,所以就有C(k,n).
    Bell数的前几项是:
    B(0)=1,B(1)=1,B(2)=2,B(3)=5,B(4)=15,B(5)=52,B(6)=203.
    从上面的递推公式我们还可以得到下面的表达式:(Dobinski公式)
    B(n)=(1/e)(1^n/n!+2^n/n!+3^n/n!...)(一直加到正无穷)
    这个其实就是泊松分布的第n个矩.
    这个问题太大了,很难短时间说清楚.

    展开全文
  • 种关系型数据库的介绍

    千次阅读 2019-05-07 07:30:00
    这是杂货铺的第471篇文章在我学生时代,当时流行的数据库就这么几,Oracle、SQL Server、DB2,像MySQL这些,都还很小众。现在的数据库技术日新月异,尤...

    这是杂货铺的第471篇文章


    在我学生时代,当时流行的数据库就这么几种,Oracle、SQL Server、DB2,像MySQL这些,都还很小众。


    现在的数据库技术日新月异,尤其是互联网的推动,各种开源数据库、分布式数据、时序数据库、内存数据库,层出不穷,下图是DB-ENGINES在5月的数据库流行度排行榜,列表很长,总计有300多种数据库,足以看出数据库的百花齐放,

    参考:https://db-engines.com/en/ranking

    640?wx_fmt=png


    下文是OceanBase大神总结的关于几种关系型数据库的架构和演进历史,对于理解和学习关系型数据库,有很好的借鉴,原文参考《一些关系数据库的架构总结》。


    640?wx_fmt=png

    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png


    640?wx_fmt=png

    640?wx_fmt=png

    640?wx_fmt=png

    640?wx_fmt=png


    640?wx_fmt=png

    640?wx_fmt=png

    展开全文
  • 今天我们一起来探讨一下有关Hibernate四种关系映射,大家可能会疑惑,关系映射不就是只有三(一对一,一对多,多对多)吗?这里我们再多说一个继承映射关系,虽然它使用频繁度没有前三个那么多,但是它在一些特定...

          今天我们一起来探讨一下有关Hibernate四种关系映射,大家可能会有疑惑,关系映射不就是只有三种(一对一,一对多,多对多)吗?这里我们再多说一个继承映射关系,虽然它使用频繁度没有前三个那么多,但是它在一些特定情况下,还是非常有用的,至于有什么用下面马上就要讲。这些关系映射实际上就是对应着关系型数据库中的几种关系的类型。我们都知道Hibernate的作用是将面向关系数据操作,转换成面向对象的操作,其中关系映射文件就是实现这个转换的核心。我们都知道如果我们仅仅使用SQL语句建表并且去维护表与表之间一对一,一对多,多对多关系,这样单纯SQL操作是很简单的。我们通过上篇博客就知道我们要实现的是写好Bean类,然后对应配置好相应的映射文件,运行测试类后就可以实现自动在数据库建立数据表。而我们现在就要实现,如何利用Bean类和映射文件自动建立多个表,并且还要体现出多个表之间的一对一,一对多,多对多的关系。

    说白的一点,就是要通过编写多个不同的Bean类(一个Bean类就是对应一张表),和配置映射文件,实现自动创建带有关系(一对一、一对多、多对多关系)的数据表。那我们开始第一个。

            实际上通过我们上篇博客我们不难发现,实现表的建立主要体现两个方面,一个是Bean类(两个Bean类之间首先要区分:主表类和从表类),另一个则是Bean类对应的映射文件(首先需要区分:主方和从方),所以接下来的每一种映射关系的讨论也将围绕着这几个方面来展开。

        第一种关系:如何去自动建立一个一对一的关系数据表:

                            注意:在建立一对一映射关系的数据表,实际上有两种方法来实现:一种是通过主键来映射关系,另一种则是通过外键来映射关系。

                               第一种实现方式:通过主键的方式映射关系:实际上就是通过主表的主键来生成从表的主键,这就是所谓主键生成方式,也就是从表的主键生成机制是有限制,必须要参照主表的中的主键,不可能出现主表中不存在的主键。然后通过主表的主键来寻找从表的主键,由于主键是不可能重复,所以也就形成通过主键的映射方式来实现和维护了两个表之间的一对一的关系。

                                                             1、在Bean类中的体现:假如有Person类和Card类两个Bean类,来自动创建两张具有一对一关系的数据表。Person类为主方,Card类为从方

    这里面还分为单向维护和双向维护,单向维护就是在主方类中保存一个从方类中的引用或者在从方类中保存一个主方类的引用。为了讲解全面点,我们全都以双向维护为例。

    可能有人会有疑问,什么是单向维护什么又是双向维护,单向维护可以通过维护的主动方来找到被维护的一方;而双向维护则是双方都能互相找到。

     Person类

    package com.mikyou.day05.one2one;
    /**
     * @Mikyou
     * @Person主方类
     * 注意:
     * 1、在编写Person类时,不要把从方类的引用写入到构造器中
     * 2、两个构造器:一个无参数构造器,另一个则是有参数的构造器
     * 3、在重写toString方法的时候,注意不要把从方表引用写进去。
     * */
    public class Person {
    	private Long id;
    	private String name;
    	private Card card;//保存着从方类Card的一个引用
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Card getCard() {
    		return card;
    	}
    	public void setCard(Card card) {
    		this.card = card;
    	}
    	public Person(Long id, String name) {
    		super();
    		this.id = id;
    		this.name = name;
    	}
    	public Person() {
    		// TODO Auto-generated constructor stub
    	}
    	@Override
    	public String toString() {
    		return "Person [id=" + id + ", name=" + name + ", card=" + card + "]";
    	}
    	
    }
    
    Card类:

    package com.mikyou.day05.one2one;
    /**
     * @Mikyou
     * @Card从方类
     * 注意:
     * 1、在编写Card类时,不要把主方类的引用写入到构造器中
     * 2、两个构造器:一个无参数构造器,另一个则是有参数的构造器
     * 3、在重写toString方法的时候,注意不要把主方表引用写进去。
     * */
    public class Card {
    	private Long id;
    	private String num;//身份证号
    	private String address;//地址
    	private Person person;//保存主方类的一个引用
    
    	public Card(Long id, String num, String address) {
    		super();
    		this.id = id;
    		this.num = num;
    		this.address = address;
    	}
    	public Card() {
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getNum() {
    		return num;
    	}
    	public void setNum(String num) {
    		this.num = num;
    	}
    	public String getAddress() {
    		return address;
    	}
    	public void setAddress(String address) {
    		this.address = address;
    	}
    	public Person getPerson() {
    		return person;
    	}
    	public void setPerson(Person person) {
    		this.person = person;
    	}
    
    }
    
                              2、在映射文件中的体现:

                       主方表映射文件person.hbm.xml:

                                          

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.one2one">
    
    	<class name="Person" table="tbl_person">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		
    		<property name="name" />
    		<!-- 在主方一对一 配置one-to-one-->
    		<one-to-one name="card" class="Card" />
    	
    	</class>
    
    </hibernate-mapping> 

         从方类的映射文件card.hbm.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.one2one">
    
    	<class name="Card" table="tbl_card">
    		<id name="id" column="id" >
    			<generator class="foreign"><!-- 以外键的形式来产生主键 -->
    				<param name="property">person</param>
    			</generator>
    		</id>
    
    		<property name="num" />
    		<property name="address" />
    		<one-to-one name="person" class="Person"  constrained="true"></one-to-one><!-- constrained="true"表示从表中的主键必须依赖主表中的主键 -->
    	</class>
    
    </hibernate-mapping>



    注意:在配置主键关系来维护一对一的关系的时候,在映射文件中的表现,首先双方都通过<one-to-one>来维护关系。在主方person.hbm.xml映射文件中
    配置<one-to-one name="card" class="Card" />其中的name对应着Person类中保存的card引用,然后对应card引用的Class,Card类。在从方card.hbm.xml映射文件中也是配置

    <one-to-one>但是需要加上constrained="true"属性,表示从表card主键必须依赖主表person的主键,此外在card.hbm.xml中它的主键生成方式不再是increment自增的方式了,而是 <id name="id" column="id" ><generator class="foreign"><!-- 以外键的形式来产生主键 --><param name="property">person</param><!-- --></generator></id>

    从表中的主键引用了主表中的主键,所以需要在主键生成方式中设置成foreign,并将参数传进去,告诉hibernate应该使用哪一个主键。在配置上,双方都为one-to-one


    第二种实现方式:通过外键来映射关系:(注意:只要是通过外键来映射关系的,必须在主表和从表中都要有体现)

    这第二种方式可以说是维护一对一关系常用的方式,第一种通过主键来映射的方式使用的情况不是很多。通过外键来映射关系主要是通过映射文件来体现的,它的Bean类表现形式和第一种情况一致。它的映射文件主要怎么体现呢?它是在主方表中配置一个<one-to-one>并且要添加一个属性<propery-ref>,在从表中配置一个<many-to-one>,为什么要配置一个<many-to-one>这不是一对多吗?我们知道一对一实际上就是一对多的一个特例,所以我们可以将一个一对多的情况转换成一个一对一的情况只需要在从表中的<many-to-one>中配置一个unique属性设为true即可并且还得指定一个属性作为外键通过column属性来指定,但这种方式下的主键生成方式没有要求就采取自增方式即可。

    person.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.one2one">
    
    	<class name="Person" table="tbl_person">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		
    		<property name="name" />
    		<!-- 在主方一对一,因为在主表和从表都要有体现,使用property-ref="person"  因为Card类中参考的是person属性,通过从表中的外键去匹配主表中主键-->
    		<one-to-one name="card" class="Card" property-ref="person" />
    	
    	</class>
    
    </hibernate-mapping>


    card.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.one2one">
    	<class name="Card" table="tbl_card">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="num" />
    		<property name="address" />
    			<!--  配置特殊的多对一,实际上是一对一-->
    		<many-to-one name="person" class="Person" unique="true" column="p_id"></many-to-one>
    	</class>
    </hibernate-mapping>
    注意:注意:
      unique="true" 是在使用hibernate的自动建表的时候才用到,用来指定表中的外键唯一,即给表中的外键添加唯一性约束,保证是一对一的关系
      property-ref="" 用来指定关联类的属性名,这个属性将会和本类中的主键相对应,如果没有指定,默认使用关联类的主键和本类中的主键相对应。
      在一对一关系中,如果没有在<one-to-one>中指定property-ref="person",当从主方对象寻找从方对象的时候将会使用自身的主键和关联类的主键匹配,这样就会出问题!如果指定后,将会用自身的主键和关联类的外键匹配。property-ref=""中的值为属性。
     第二种关系:如何去自动建立一个一对多的关系数据表(以一个顾客对应多个订单为例):

                        1、在Bean类中的表现:

       在主类Customer中的表现:在Customer中保存一个Set<Oder>集合表示一对多在从类Order中保存一个Customer类的引用

                                    Customer类:

    package com.mikyou.day05.one2many;
    
    import java.io.Serializable;
    import java.util.Date;
    import java.util.HashSet;
    import java.util.Set;
    
    public class Customer implements Serializable{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private Long id;
    	private String name;
    	private String telephone;
    	private String address;
    	private String pwd;
    	private Set<Order> orders=new HashSet<Order>();//维护多个订单。注意这里一定要初始化,因为集合中数据为空和
    	//集合不存在是两个概念,集合中数据为空表示此时顾客中没有订单,当下次来了订单直接放入到集合中即可
    	//集合不存在就不行,如果下次只有来了订单,而此时集合不存在,会造成集合中不能放入订单对象,造成空指针
    
    
    	public Customer(Long id, String name,String pwd, String telephone, String address) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.telephone = telephone;
    		this.address = address;
    		this.pwd = pwd;
    
    	}
    	
    	public String getPwd() {
    		return pwd;
    	}
    
    	public void setPwd(String pwd) {
    		this.pwd = pwd;
    	}
    
    	public Customer() {
    		// TODO Auto-generated constructor stub
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getTelephone() {
    		return telephone;
    	}
    	public void setTelephone(String telephone) {
    		this.telephone = telephone;
    	}
    	public String getAddress() {
    		return address;
    	}
    	public void setAddress(String address) {
    		this.address = address;
    	}
    	public Set<Order> getOrders() {
    		return orders;
    	}
    	public void setOrders(Set<Order> orders) {
    		this.orders = orders;
    	}
    	
    }
    

    Order类:

    package com.mikyou.day05.one2many;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class Order  implements Serializable{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private Long id;
    	private String total;
    	private Date orderDate;
    	private Customer customer;//维护主表一方的引用
    	public Order(Long id, String total, Date orderDate) {
    		super();
    		this.id = id;
    		this.total = total;
    		this.orderDate = orderDate;
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getTotal() {
    		return total;
    	}
    	public void setTotal(String total) {
    		this.total = total;
    	}
    	public Date getOrderDate() {
    		return orderDate;
    	}
    	public void setOrderDate(Date orderDate) {
    		this.orderDate = orderDate;
    	}
    	public Customer getCustomer() {
    		return customer;
    	}
    	public void setCustomer(Customer customer) {
    		this.customer = customer;
    	}
    	public static long getSerialversionuid() {
    		return serialVersionUID;
    	}
    	public Order() {
    		super();
    	}
    	@Override
    	public String toString() {
    		return "Order [id=" + id + ", total=" + total + ", orderDate=" + orderDate + "]";
    	}
    	
    	
    }
    

        2、在映射文件中的表现:

    customer.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.one2many">
    	<class name="Customer" table="tbl_customer">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="name" />
    		<property name="telephone" />
    		<property name="pwd"></property>
    		<property name="address" />
    		<!-- 关联映射 -->
    		<set name="orders"  lazy="true" cascade="delete"><!-- lazy:true延迟加载 默认       cascade="delete"级联删除-->
    		    <key column="c_id"></key><!-- 在Order多的一方配置外键 -->
    		    <one-to-many class="Order"/>
    		 </set>
    	</class>
    </hibernate-mapping>
    order.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.one2many">
    	<class name="Order" table="tbl_order">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="total" />
    		<property name="orderDate" />
    		<!-- 关联映射,column是与生成表中的一列 -->
    		<many-to-one name="customer" class="Customer" column="c_id"></many-to-one>
    	</class>
    </hibernate-mapping>

    映射一对多关联

    数据库表中的关系:一个顾客对应多个订单

    主表:d_customer(

              Id

              name

            );

     

    从表: d_order(

              Id

              name

              c_id references d_customer(id)

            );

    从表的主键引用主表的主键

    类中的关系: 双向维护

    主类 Customer{

            Id

            name

            Set<Order> orders = new HashSet<>()

          }

    一个顾客对应多个订单,所以在对象中需要用一个集合来维护,后期可以通过getter方法获取属于该顾客的所有订单

    从类:Order{

              Id

              name

              Customer customer

          }

    一个订单只能属于一个顾客,所以需要维护一个顾客对象,后期通过getter方法获取该订单的所属者。

    映射关系上:

    主方: customer.hbm.xml

    <set name="orders">

      <key column="c_id"></key>

      <one-to-many class="Order"/>

    </set>

    从方:order.hbm.xml

    <many-to-one name="customer" class="Customer" column="c_id" >

    注意:

       在映射文件中,双方都维护了外键。主方通过<set>集合中的<key>来维护,从方通过

    <many-to-one> 中的column来维护。

      


     第三种关系:如何去自动建立一个多对多的关系数据表(以多个学生选择多门课程为例):

     1、在Bean类中的表现:

    在各自类中互相保存着对方一个集合类的空对象,而不是引用,一定要初始化。因为当下次有个学生要选课了,此时课程集合没有new操作,就直接调用集合的add方法,自然就抛出空指针异常,但是如果new操作得到一个空集合对象,下次只要有学生选课了,直接调用add方法就会把课程这个对象加入到集合中去。

    Student类:

    package com.mikyou.day05.many2many;
    
    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.Set;
    
    public class Student implements Serializable{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private Long id; 
    	private String name;
    	private Integer age;
    	private Set<Course> courses=new HashSet<Course>();
    	public Student(Long id, String name, Integer age) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.age = age;
    	}
    	public Student() {
    		super();
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Integer getAge() {
    		return age;
    	}
    	public void setAge(Integer age) {
    		this.age = age;
    	}
    	public Set<Course> getCourses() {
    		return courses;
    	}
    	public void setCourses(Set<Course> courses) {
    		this.courses = courses;
    	}
    	@Override
    	public String toString() {
    		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
    	}
    
    }
    

    Course类:

    package com.mikyou.day05.many2many;
    
    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.Set;
    
    public class Course implements Serializable{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private Long id;
    	private String name;
    	private Set<Student> students=new HashSet<Student>();
    	private Integer credit;
    	public Course() {
    		super();
    	}
    	public Course(Long id, String name, Integer credit) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.credit = credit;
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Set<Student> getStudents() {
    		return students;
    	}
    	public void setStudents(Set<Student> students) {
    		this.students = students;
    	}
    	public Integer getCredit() {
    		return credit;
    	}
    	public void setCredit(Integer credit) {
    		this.credit = credit;
    	}
    	@Override
    	public String toString() {
    		return "Course [id=" + id + ", name=" + name + ", credit=" + credit + "]";
    	}
    	
    }
    

     2、在映射关系中的表现:

    student.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.many2many">
    	<class name="Student" table="tbl_student">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="name" />
    		<property name="age" />
    		<set name="courses" table="tbl_student_course">
    			<key column="s_id"></key><!--tbl_student_course表中的外键s_id参考着本表的tbl_student中的主键id -->
    			<many-to-many class="Course" column="c_id"></many-to-many>
    		</set>
    	</class>
    </hibernate-mapping>

    course.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.many2many">
    	<class name="Course" table="tbl_course">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="name" />
    		<!-- 配置多对多 -->
    		<set name="students" table="tbl_student_course">
    			<key column="c_id"></key><!--tbl_student_course表中的外键c_id参考着本表的tbl_course中的主键id -->
    			<many-to-many class="Student" column="s_id"></many-to-many>
    		</set>
    	</class>
    </hibernate-mapping>

    映射多对多关联

    数据库表中的关系:一个学生选择多门课程,一门课程也可以有很多学生来选择

    主表:d_student(

              Id

              name

              age

            );

     

    从表: d_course(

              Id

              name

            );

     

    桥表:d_student_course(

              student_id references d_student(id),

              course_id references d_course(id).

              unique(student_id,course_id)

           );

      多对多的关系是维护在桥表上的,桥表中维护了两个外键,分别指向两个表的主键,并且这两个外键为联合主键。

    类中的关系: 双向维护

    主类 Student{

            Id

            name

            age

            Set<User> courses= new HashSet<>()

          }

    一个学生对应多门课程,用一个集合来维护

    从类:Course{

              Id

              name

              Set<Student> students=new hashSet<>()

          }

    一个门课程对应多个学生,需要用一个集合来维护

    映射关系上:

    主方:student.hbm.xm

    l<set name="courses" table="tbl_student_course">

    <key column="s_id"></key>

    <many-to-manyclass="Course" column="c_id"></many-to-many>

    </set>

    从方:course.hbm.xml

    <set name="students" table="tbl_student_course">

    <key column="c_id"></key>

    <many-to-many class="Student"

     column="s_id"></many-to-many>

    </set>

    注意:

      在映射元素上反应的是关联关系,由于多对多需要使用桥表来维护关联关系,所以需要在set元素中添加table元素用来指定桥表,key元素中的column属性表示本类在桥表中的外键。many-to-many表示对应的另外一方,其中的column属性表示对应的类在桥表中的外键。



     第三种关系:多对多关系中的另一种常用形式:将一个多对多形式问题分解成两个一对多的形式的问题

    这种转换后的好处就是我们控制桥表字段,也就是说我可以任意定制桥表;因为如果使用的是上一种方式的处理的话,桥表是系统自动产生的,并且这样的桥表只能有两个字段分别是两个表的主键,也就是桥表的两个外键。通常这样的桥表不能满足现实需求的,就比如上面的学生选课来说,就会自动产生一个选课关系表,现实需求中通常会在选课关系中添加一个分数或者老师评语,也就是某某选了某门课成绩多少,老师评语是什么?这样的一个需求很常见,但是如果使用系统自动产生通过many-to-many方式来生成表的话是无法满足现实需求,所以就出现这种将一个多对多的形式转换成两个一对多的形式的问题。这样的操作就需要我们自己手动的去创建桥表。学生选课关系就变成了:一个学生对应多种选课关系,一门课程对应多种选课关系。

     1、在Bean类中的表现:

    我们这里需要三个Bean类:一个是Student类,一个是Course类,另一个就是StuCou类(选课关系类)

    在Student类中保存着一个Set<StuCou>集合,有多个选课关系,在Course类中也保存一个Set<StuCou>集合,有多个选课关系。在StuCou类中的分别保存着Student和Course的引用。

    Student类:

    package com.mikyou.day05.double_one2many;
    
    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.Set;
    
    public class Student implements Serializable{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private Long id; 
    	private String name;
    	private Integer age;
    	private Set<StuCou> stuCous=new HashSet<StuCou>();//保存着多个选课关系
    	public Student(Long id, String name, Integer age) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.age = age;
    	}
    	public Student() {
    		super();
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Integer getAge() {
    		return age;
    	}
    	public void setAge(Integer age) {
    		this.age = age;
    	}
    
    	public Set<StuCou> getStuCous() {
    		return stuCous;
    	}
    	public void setStuCous(Set<StuCou> stuCous) {
    		this.stuCous = stuCous;
    	}
    	@Override
    	public String toString() {
    		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
    	}
    
    }
    

    Course类:

    package com.mikyou.day05.double_one2many;
    
    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.Set;
    
    public class Course implements Serializable{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private Long id;
    	private String name;
    	private Set<StuCou> stuCous=new HashSet<StuCou>();//保存着多个选课关系
    	private Integer credit;
    	public Course() {
    		super();
    	}
    	public Course(Long id, String name, Integer credit) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.credit = credit;
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public Set<StuCou> getStuCous() {
    		return stuCous;
    	}
    	public void setStuCous(Set<StuCou> stuCous) {
    		this.stuCous = stuCous;
    	}
    	public Integer getCredit() {
    		return credit;
    	}
    	public void setCredit(Integer credit) {
    		this.credit = credit;
    	}
    	@Override
    	public String toString() {
    		return "Course [id=" + id + ", name=" + name + ", credit=" + credit + "]";
    	}
    	
    }
    
    StuCou类:

    package com.mikyou.day05.double_one2many;
    //学生选课关系,抽取一个选课关系,让原来的一个多对多的关系直接转换成两个一对多的关系
    public class StuCou {
    	private Long id;
    	private Double grade;//新增扩展属性grade分数
    	private String comment;//新增扩展属性comment评语
    	
    	//
    	private Student student;
    	private Course course;
    	public StuCou(Long id, Double grade, String comment) {
    		super();
    		this.id = id;
    		this.grade = grade;
    		this.comment = comment;
    	}
    	public StuCou() {
    		// TODO Auto-generated constructor stub
    	}
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public Double getGrade() {
    		return grade;
    	}
    	public void setGrade(Double grade) {
    		this.grade = grade;
    	}
    	public String getComment() {
    		return comment;
    	}
    	public void setComment(String comment) {
    		this.comment = comment;
    	}
    	public Student getStudent() {
    		return student;
    	}
    	public void setStudent(Student student) {
    		this.student = student;
    	}
    	public Course getCourse() {
    		return course;
    	}
    	public void setCourse(Course course) {
    		this.course = course;
    	}
    
    }
    
     2、在映射文件中的表现:

    在映射文件配置实际上和一对多配置是一样的,只不过这里是配置两个一对多的关系,就是需要在桥表StuCou中的配置两个<many-to-one>

    student.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.double_one2many">
    	<class name="Student" table="tbl_student">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="name" />
    		<property name="age" />
    		<set name="stuCous">
    			<key column="s_id"></key><!--tbl_student_course表中的外键s_id参考着本表的tbl_student中的主键id -->
    			<one-to-many class="StuCou" />
    		</set>
    	</class>
    </hibernate-mapping>

    course.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.double_one2many">
    	<class name="Course" table="tbl_course">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="name" />
    		<property name="credit" />
    		<!-- 配置多对多 -->
    		<set name="stuCous">
    			<key column="c_id"></key><!--tbl_student_course表中的外键c_id参考着本表的tbl_course中的主键id -->
    			<one-to-many class="StuCou" />
    		</set>
    	</class>
    </hibernate-mapping>

    桥表映射文件stucou.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.mikyou.day05.double_one2many">
    	<class name="StuCou" table="tbl_student_course">
    		<id name="id" column="id">
    			<generator class="increment" />
    		</id>
    		<property name="grade" />
    		<property name="comment" />
    		<!-- 配置多对多 -->
    		<many-to-one name="student" class="Student" column="s_id"></many-to-one>
    		<many-to-one name="course" class="Course" column="c_id"></many-to-one>
    	</class>
    </hibernate-mapping>
    注意:实际上这种方式在现实需求用的更多一些,所以这种情况使用就是当你的桥表中需要新增适应需求的属性这时候就需要考虑将一个多对多的关系转换成两个一对多的关系

         第四种关系:如何去自动建立一个具有继承关系数据表(基类:Animal 两个子类:Dog、Bird)

       所谓继承映射实际上对应着就是面向对象中的继承机制,这种继承关系映射在现实需求其实用处也蛮大的:就比如说我们设想一个需求,在一个项目中或者多个项目的我们可能多次都需要用到上传附件或者上传图片的功能,实际上这操作是和项目没有多大关系,可以说它是通用的。那既然这样的话为什么不能抽取一个这样的操作单独用一个表处理,然后需要使用这些操作的表只需要继承这个基类表不就行了吗?这样就大大重构了我们的项目代码。

     1、在Bean类中的表现:

    先写一个Animal类,然后写一个Dog类和一个Bird类分别都去继承这个Animal类

    Animal

    package com.mikyou.day05.inherit;
    
    import java.io.Serializable;
    
    public class Animal implements Serializable{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private Long id;
    	private String name;
    	public Long getId() {
    		return id;
    	}
    	public void setId(Long id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Animal(Long id, String name) {
    		super();
    		this.id = id;
    		this.name = name;
    	}
    	public Animal() {
    		super();
    	}
    	@Override
    	public String toString() {
    		return "Animal [id=" + id + ", name=" + name + "]";
    	}
    
    }
    
    Dog类:

    package com.mikyou.day05.inherit;
    
    public class Dog extends Animal{
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private String color;
    
    	public String getColor() {
    		return color;
    	}
    
    	public void setColor(String color) {
    		this.color = color;
    	}
    
    	@Override
    	public String toString() {
    		return "Dog [color=" + color + "]";
    	}
    
    }
    
    Bird类:

    package com.mikyou.day05.inherit;
    
    public class Bird extends Animal{
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private String type;//品种
    
    	public String getType() {
    		return type;
    	}
    
    	public void setType(String type) {
    		this.type = type;
    	}
    }
    

    映射文件:

    映射文件生成表有两种:一种是将所有的子父类放在一张表中,另一种就是一个类就是一张表(个人认为这种方式更好)

    第一种是将所有的子父类放在一张表中:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
        
    <hibernate-mapping package="com.briup.day5.inherit">
        <class name="Animal" table="tbl_animal">
            <id name="id" column="id">
                <generator class="uuid.hex"/>
            </id>
            <property name="name"/>
    		<joined-subclass name="Dog" table="tbl_dog">
    			<key column="id"/>
    			<property name="color"/>
    		</joined-subclass>      
    		<joined-subclass name="Bird" table="tbl_bird">
    			<key column="id"/>
    			<property name="type"/>
    		</joined-subclass>         
        </class>
    </hibernate-mapping>
    
    

    第二种就是一个类就是一张表:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
        
    <hibernate-mapping package="com.briup.day5.inherit">
        <class name="Animal" abstract="true">
            <id name="id" column="id">
                <generator class="uuid.hex"/>
            </id>
            <property name="name"/>
    		<union-subclass name="Dog" table="tbl_dog">
    			<property name="color"/>
    		</union-subclass>
    		<union-subclass name="Bird" table="tbl_bird">
    			<property name="type"/>
    		</union-subclass>
        </class>
    </hibernate-mapping>
    

    继承映射

    1每个类一张表

    在表中的关系--子类的主键引用父类的主键,保证父子类的id一致

    父类表--动物

    animal(

      id,

      name

    );

    子类表---

    Dog(

      id references animal(id),

      type

    );

    子类表---

    Bird(

      id references animal(id),

      type

    );

    在类中的关系

    Animal{

      id,

      name

    }

    Dog extends Animal{

       type

    }

    Bird extends Animal{

       color

    }

    在映射文件中的关系,配置在同一个映射文件中

    <class name="Animal" table="d_animal">

      <id name="id" column="id">

    <generator class="increment"/>

      </id>

      <property name="name"/>

    <!-- 以上是配置父类的基本属性,需要指定父类使用的表名-->

    <!-- 以上是配置子类的配置,需要指定子类使用的表名,使用joined-subclass来和父类关联,key值继承父类的,column指定子类在表中的主键;property为子类特有的属性-->

      <joined-subclass name="Dog" table="h_dog">

    <key column="id"></key>

    <property name="type"/>

      </joined-subclass>

      <joined-subclass name="Bird" table="h_bird">

    <key column="id"></key>

    <property name="color"></property>

      </joined-subclass>

    </class>

     

    2一个类域一张表

    在表中的关系--父子类共用一张表,这张表中包含了父子类的所有字段

    Allclass(

      id

      name

      type

      color

      a_value --用来表示该条记录是属于哪个类的

    );

    在类中的关系

    Animal{

      id,

      name

    }

    Dog extends Animal{

       type

    }

    Bird extends Animal{

       color

    }

    在映射文件中的关系,配置在同一个映射文件中

    <class name="Animal" table="h_allclass">

    <id name="id" column="id">

    <generator class="increment">

    </generator>

    </id>

    <!-- discriminator用来指定标识列 -->

    <discriminator column="a_value"/>

    <property name="name"/>

    <!-- discriminator-value用来指定标识列中应该填入的值,用这个值来区分这条记录是属于哪个类的,property用来指明子类特有的属性 -->

        <subclass name="Dog" discriminator-value="dog">

    <property name="type"/>

    </subclass>

    <subclass name="Bird" discriminator-value="bird">

    <property name="color"/>

    </subclass>

    </class>

     

     

     

     

     

     

     

     

     

     

     

    3每个子类一张表

    在表中的关系--父子类共用一张表,这张表中包含了父子类的所有字段

     

     

    在类中的关系

    Animal{

      id,

      name

    }

    Dog extends Animal{

       type

    }

    Bird extends Animal{

       color

    }

    在映射文件中的关系,配置在同一个映射文件中

    <class name="Animal">

            <id name="id">

                <generator class="native"/>

            </id>

            <property name="name"/>

            <property name="color"/>

            <union-subclass name="Dog" table="dog">

                <property name="callType"></property>

            </union-subclass>

            <union-subclass name="Bird" table="bird">

             <property name="type"></property>

            </union-subclass>

    </class>

    主键生成方式不能使用Increment,因为Animal类并没有对应的表,可以使用序列

     到这里Hibernate中表的映射关系就讲这么多了。

     

     

     



     

     



      

     

     

     

     

     

     




    展开全文
  • UML类图几种关系的总结

    万次阅读 2016-09-27 16:03:34
    在UML类图中,常见的以下几种关系: 泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)  1. 泛化(Generalization)  ...
  • 类与类之间的几种关系

    万次阅读 2017-03-14 08:30:22
    类与类之间的几种关系 一、继承关系 继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。在Java中继承关系通过关键字extends明确标识...
  • 数据库表的三种关系

    千次阅读 2013-04-14 09:51:33
    数据库表的三种关系 1. 一对一的关系(1:1): 在一表中一条记录,则在另一张表中一条记录相匹配。一般是看主表每一个字段对应另一张表的匹配记录条数。    上图中学生实体与政治面貌实体是1:1的关系,一...
  • UML九种关系

    千次阅读 2014-12-11 09:17:16
    UML中包括九图:用例图、类图、对象图、状态图、时序图、协作图、活动图、组件图、配置图。   1)用例图(Use Case Diagram) 它是UML中最简单也是最复杂的一图。说它简单是因为它采用了面向对象...
  • UML里类之间的几种关系

    千次阅读 2016-10-12 19:03:01
    UML类图:类图时锻炼OOA(OO analysis)和OOD(OO design)思想的重要工具,助于OOA、OOD思想的提升。 重点:理解类图中类与类之间的关系关系以及这种关系在代码中...类与类之间存在泛化,关联,依赖,和聚合四种关系
  • UML常用图的几种关系的总结

    千次阅读 2017-09-08 11:08:46
    类图中,常见的以下几种关系: 泛化(Generalization), 实现(Realization), 关联(Association), 聚合(Aggregation), 组合(Composition), 依赖(Dependency) 1. 泛化(Generalization) ...
  • 递推算法-五典型的递推关系

    千次阅读 多人点赞 2020-01-26 00:03:56
    递推法是一重要的数学方法,在数学的各个领域中都广泛的运用,也是计算机用于数值计算的一个重要算法。这种算法特点是:一个问题的求解需一系列的计算,在已知条件和所求问题之间总存在着某种相互联系的关系,在...
  • UML类图中类之间的6种关系与区别

    千次阅读 2018-05-15 10:35:11
    原文:https://blog.csdn.net/it_zjyang/article/details/51355062类与类之间的关系类与类之间的关系可以根据关系的强度依次分为以下五:依赖关系...--泛化(Generalization)依赖关系依赖关系是五种关系中...
  • 企业组织结构中的几种关系

    千次阅读 2014-03-16 13:11:29
    本文章参考 博客园 博主“心内求法” 的文章 这属于业务领域的问题,同时也是一个系统最常用最基本的数据之一 1--组织单元间的包含关系 ...尽管通常绘制成上下级关系,但组织单元间通常为一“包含”关
  • n个节点的二叉树有多少种形态

    千次阅读 2018-04-03 20:12:31
    【n个节点的二叉树有多少种形态(Catalan数)】分析过程: (1)先考虑只有一个节点的情形,设此时的形态f(1)种,那么很明显f(1)=1(2)如果两个节点呢?我们很自然想到,应该在f(1)的基础上考虑递推关系。那么...
  • 矩阵链乘法 有多少种加括号方案

    千次阅读 2015-09-19 14:42:25
    矩阵链乘法 有多少种加括号方案flyfish 2015-9-19答案与Catalan数列有关递归式的解释 更改下字母就是《算法导论》上的式子 **答案就是C(n−1)C_(n-1) 例如4个矩阵相乘5种加括号方案 5个矩阵相乘14种加括号...
  • 具有n个节点的二叉树有多少种形态

    千次阅读 2018-11-02 21:05:37
    【n个节点的二叉树有多少种形态(Catalan数)】 分析过程: (1)先考虑只有一个节点的情形,设此时的形态f(1)种,那么很明显f(1)=1 (2)如果两个节点呢?我们很自然想到,应该在f(1)的基础上考虑递推关系。...
  • UML类图的几种关系和序列图总结

    千次阅读 2016-11-09 23:39:51
    转自 UML类图几种关系的总结 http://blog.csdn.net/tianhai110/article/details/6339565 转自 UML序列图总结 ... 在UML的类图中,常见的以下几种关系: 泛化(Generalization), 实现(Realization
  • N个节点的二叉树有多少种形态

    千次阅读 2017-10-13 14:36:15
    先考虑只有一个节点的情形,设此时的形态f(1),那么很明显f(1)=1 如果两个节点呢?我们很自然想到,应该在f(1)的基础上考虑递推关系。那么,如果固定一个节点后,情况,一是左子树还剩一个节点,此刻...
  • 一、 特殊关系 、 二、 集合上的特殊关系 、 三、 整除关系 、 四、 大小关系
  • 【n个节点的二叉树有多少种形态(Catalan数)】 分析过程:  (1)先考虑只有一个节点的情形,设此时的形态f(1)种,那么很明显f(1)=1 (2)如果两个节点呢?我们很自然想到,应该在f(1)的基础上考虑递推关系...
  • UML中类图的四种关系及其代码实现

    千次阅读 热门讨论 2014-09-29 17:35:40
    在uml的学习中最重要的就是类之间的关系,以及如何实现,在本文将详细的为你介绍这几种关系,并说明他们是如何代码中实现的。看完不问以后你就会队UML中的类图的组合进一步的了解,在你学习设计模式,或者看别人给...
  • 微信公众号:javafirstMySQL中日志文件,分别是:重做日志(redo log)、回滚日志(undo log)、二...其中重做日志和回滚日志与事务操作息息相关,二进制日志也与事务操作一定的关系,这三日志,对理解My...
  • 数据库关系代数详解

    千次阅读 多人点赞 2021-02-26 16:35:55
    传统的关系运算起源于数学的集合论,下面几: 笛卡尔积运算 差运算 交运算 并运算 1.2 专门的关系运算 选择 投影 连接 除运算 1.2.1 关系运算中的基础概念 在学习关系代数的时候,我们脑海中要时刻拥有一张...
  • 典型的递推关系 1.Fibonacci数列 在所有的递推关系中,Fibonacci数列应该是最为大家所熟悉的。在最基础的程序设计语言Logo语言中,就很多这类的题目。而在较为复杂的Basic、Pascal、C语言中,Fibonacci数列类...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 612,221
精华内容 244,888
关键字:

关系有多少种