• One to Many 映射关系
    • 多对一单向外键关联(XML/Annotation)
    • 一对多单向外键关联(XML/Annotation)
    • 懒加载和积极加载
    • 一对多双向外键关联(XML/Annotation)
  • Many to Many 映射关系
    • 多对多单向外键关联(XML/Annotation)
    • 多对多双向外键关联(XML/Annotation)
    • set的inverse元素详解
  • 问题小结
  • 关联关系的优缺点

    多对一单向外键关联关系

      注意多对一关联是多方持有一方的引用。看一个例子,去淘宝购物,那么一个淘宝用户可以对应多个购物订单,如图所示:

      多的一方是Orders,持有一方的引用,也就是Users,而在Users中无需作任何定义,从订单到用户的关系是单向多对一关联。对应数据库就是:

      还有比如说学生和班级的关系,多个学生可以属于同一个班级,这就是从学生到班级也是典型的单向多对一关系,看代码实现:

      

      基于注解的多对一单向外键关联:

      单向多对一关联中,多方需要持有一方的引用,那么多方(学生类)需要额外配置,需要对持有的一方引用使用注解@ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER),设置为级联操作和饥渴的抓取策略,@JoinColumn(name="cid"),而一方(教室类)无需做任何多方的定义。

      注意;多方必须保留一个不带参数的构造器!

    复制代码
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id; //班级类,在多对一关系中属于一的方,不持有其他多余的配置,反而是被多方持有
    @Entity
    public class ClassRoom {
    private int cid;//班级编号 private String cname;//班级名称 // 自动增长的主键
    @Id
    @GeneratedValue
    public int getCid() {
    return cid;
    } public void setCid(int cid) {
    this.cid = cid;
    } public String getCname() {
    return cname;
    } public void setCname(String cname) {
    this.cname = cname;
    }
    }

    一方——班级类无需做多余的定义,下面是多方——学生实体和配置:

    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne; //学生实体类,属于多对一的多方,持有班级(一方)的引用
    @Entity
    public class Students {
    private int sid; //编号 private String sname; //姓名 private ClassRoom classroom;//学生班级 //注意:多方一定要显式的定义不带参数的构造方法
    public Students() {
    } public Students(String sname)
    {
    this.sname = sname;
    }
    // 多方使用注解:@ManyToOne
    // fetch=FetchType.EAGER,急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
    // 全部级联操作,referencedColumnName显式设置数据库字段名cid,不写默认就是和name一样的。
    @ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
    @JoinColumn(name="cid",referencedColumnName="cid")
    public ClassRoom getClassroom() {
    return classroom;
    } public void setClassroom(ClassRoom classroom) {
    this.classroom = classroom;
    } // 自动增长主键
    @Id
    @GeneratedValue
    public int getSid() {
    return sid;
    } public void setSid(int sid) {
    this.sid = sid;
    } public String getSname() {
    return sname;
    } public void setSname(String sname) {
    this.sname = sname;
    }
    }

    下面测试:先生成数据库脚本,再进行学生对象的插入

    public class TestStudentsByAnno {
    private static SessionFactory sessionFactory; @Before
    public void setUp() throws Exception {
    System.out.println("setUp()...");
    sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
    } @After
    public void tearDown() throws Exception {
    System.out.println("tearDown()...");
    sessionFactory.close();
    } @Test
    public void testSave() {
    Session session = sessionFactory.getCurrentSession();
    Transaction tx = session.beginTransaction(); try {
    ClassRoom c = new ClassRoom();
    c.setCname("computer001"); Students s = new Students("zhangsan");
    s.setClassroom(c); session.save(s);
    tx.commit();
    } catch(Exception ex) {
    ex.printStackTrace();
    tx.rollback();
    }
    } @Test
    @Ignore
    public void testSchemaExport() {
    SchemaExport se = new SchemaExport(new AnnotationConfiguration().configure());
    se.create(true, true);
    }
    }

    反向创建表的数据库脚本如下:

    create table ClassRoom (cid integer not null auto_increment, cname varchar(255), primary key (cid))
    create table Students (sid integer not null auto_increment, sname varchar(255), cid integer, primary key (sid))

    插入一个学生对象,会自动生成如下语句:

     ClassRoom c = new ClassRoom();
    c.setCname("computer001"); Students s = new Students("zhangsan");
    s.setClassroom(c); session.save(s);
    tx.commit();

    Hibernate: insert into ClassRoom (cname) values (?)

    Hibernate: insert into Students (cid, sname) values (?, ?)

    插入成功:

      基于xml配置实现多对一单向外键关联

    <hibernate-mapping>
    <class name="net.nw.vo.fk.mto.ClassRoom" table="classroom">
    <id name="cid" column="cid" type="int">
    <generator class="native"/>
    </id> <property name="cname" column="cname" type="string"/>
    </class>
    </hibernate-mapping>

      一方(教室类)无需做任何多方的定义。只需要维护好自己的属性配置即可。而多方只需要加上<many-to-one name="" column=“"/>就ok。

    <hibernate-mapping>
    <class name="net.nw.vo.fk.mto.Students" table="students">
    <id name="sid" column="sid" type="int">
    <generator class="native"/>
    </id> <property name="sname" column="sname" type="string"/> <many-to-one name="classroom" column="cid"/>
    </class>
    </hibernate-mapping>

      hibernate.cfg.xml里加上

      <mapping resource="net/nw/vo/fk/mto/ClassRoom.hbm.xml" />
    <mapping resource="net/nw/vo/fk/mto/Students.hbm.xml" />

    注意:如果没有设置级联ALL,那么需要在保存的时候先保存班级,在保存学生,否则出错: object references an unsaved transient instance - save the transient instance before flushing: 

                ClassRoom classRoom = new ClassRoom();
    classRoom.setCname("CS"); Students students = new Students("111");
    students.setClassroom(classRoom); session.save(classRoom);
    session.save(students);
    tx.commit();

      小结:使用<many-to-one>元素进行多对一关联关系配置,name属性  指定类的属性名,column属性 指定库表字段名,class属性  指定类属性类型(加上姓,即包名),not-null属性 指定属性是否允许为空,cascade属性 指定是否级联保存和更新:save-update、delete、all、none。

      

      一对多单向外键关联

      当类与类建立了关联,程序能很方便的从一个对象导航到另一个或一组与之关联的对象,有了student对象,就可以通过student对象得到这个学生所属的班级的信息——students.getClassroom();,对于班级对象,如果想要得到某个学生的信息,怎么办呢?这时候可以反过来控制,一方控制多方,下面进行一对多单向外键关联。

      简单说就是和之前多对一相反,之前是多方持有一方的引用,而一对多关联关系是一方持有多方的集合的引用,注意区别:这里是持有多方的集合。

      基于注解的配置:

      @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY),@JoinColumn(name=""),除了级联之外,还要设置一方为懒加载模式。且外键还是加在了多方学生表里,只不过控制权变了,之前多对一关联是多方学生持有班级外键,控制班级,现在一对多关联,表里还是多方学生持有班级一方的外键,只不过控制权交给了班级,让班级控制学生。不要混淆。

    import javax.persistence.*;
    import java.util.Set; //班级类是一方,一方持有多方的引用
    @Entity
    public class ClassRoom {
    private int cid;//班级编号 private String cname;//班级名称 private Set<Students> stus ;//班级的学生集合是多方 // 现在是一方维护多方了,主控权交给了一方,设置级联,一方要设置懒加载,推荐!
    @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
    @JoinColumn(name="cid") // 设置一方的外键,这里是cid,因为实际上这个外键还是加在多方,只不过控制权变了。
    public Set<Students> getStus() {
    return stus;
    } public void setStus(Set<Students> stus) {
    this.stus = stus;
    } @Id
    @GeneratedValue
    public int getCid() {
    return cid;
    } public void setCid(int cid) {
    this.cid = cid;
    } public String getCname() {
    return cname;
    } public void setCname(String cname) {
    this.cname = cname;
    }
    }

      注意,不论多对一还是一对多,多方都要显式保留无参构造器。

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id; //学生实体类
    @Entity
    public class Students {
    private int sid; //编号 private String sname; //姓名 //注意:一定要保留这个默认不带参数的构造方法
    public Students(){ } public Students(String sname)
    {
    this.sname = sname;
    } @Id
    @GeneratedValue
    public int getSid() {
    return sid;
    } public void setSid(int sid) {
    this.sid = sid;
    } public String getSname() {
    return sname;
    } public void setSname(String sname) {
    this.sname = sname;
    }
    }

      执行数据库脚本,发现一方(主控方)还是和之前多对一的表结构一样,多方也是如此。

    create table ClassRoom (cid integer not null auto_increment, cname varchar(255), primary key (cid))
    create table Students (sid integer not null auto_increment, sname varchar(255), cid integer, primary key (sid))

      执行测试,保存学生,因为现在关系是一方维护,控制多方。肯定保存主控方——班级(和之前相反,之前多对一保存的是多方学生对象),但是本质上还是先保存的学生班级,再自动保存学生,这点和多对一本质一样。

                Set<Students> stus = new HashSet<>();
    stus.add(new Students("zhangsan"));
    stus.add(new Students("lisi"));
    stus.add(new Students("wangwu"));
    stus.add(new Students("zhaoliu"));
    stus.add(new Students("sunqi")); ClassRoom c = new ClassRoom();
    c.setCname("cs001");
    c.setStus(stus); session.save(c);
    tx.commit();

      生成的脚本如下:先插入外键的班级对象,在执行五个学生的插入操作,最后执行五个更新,为sid=1。。。5的学生,更新cid为2

    Hibernate: insert into ClassRoom (cname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: insert into Students (sname) values (?)
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?
    Hibernate: update Students set cid=? where sid=?

      

      总结:多对一时候,多方设置EAGER,一方设置LAZY,也就是说,如果是多对一,多方控制一方,那么多方设置积极加载,一方无需多余配置,反过来,如果是一对多关系,一方控制多方,那么一方设置懒加载,多方无需多余配置,但是不论哪种,多方都显式加上一个不带参数的构造器。

      

      一对多里的懒加载

      记得之前总结,get和load的查询方式源码的时候,就总结了一下懒加载load里的应用,之前说Hibernate中,当访问的数据量过大时,用缓存也不太合适, 因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,Hibernate用懒加载机制来弥补这种缺陷,但是这只是弥补而不是用了懒加载总体性能就提高了。懒加载也被称为延迟加载,它在查询的时候不会立刻访问数据库,而是返回代理对象,比如之前总结的load方式查询,当真正去使用对象的时候才会访问数据库。除了load查询默认使用懒加载,现在我们的一对多关联映射也使用了lazy加载,下面进行实际验证测试:

      public void testQuery()    {
    Session session = sessionFactory.getCurrentSession();
    Transaction tx = session.beginTransaction(); try {
    // 首先查询班级,cid=1的班级
    ClassRoom c =(ClassRoom) session.get(ClassRoom.class, 1); // 通过班级导航到学生,遍历学生得到名字
    for(Students s : c.getStus()) {
    System.out.println("姓名 :" + s.getSname());
    } tx.commit();
    } catch(Exception ex) {
    ex.printStackTrace();
    tx.rollback();
    }
    }

      执行之后,debug发现:在没有使用班级对象的时候,只有这样一条SQL语句:从classroom表查询,cid=1的班级,使用使用AS赋给列一个别名。

    Hibernate: select classroom0_.cid as cid0_0_, classroom0_.cname as cname0_0_ from ClassRoom classroom0_ where classroom0_.cid=?

      等执行到for了,才打印这一语句:

    Hibernate: select stus0_.cid as cid0_1_, stus0_.sid as sid1_, stus0_.sid as sid1_0_, stus0_.sname as sname1_0_ from Students stus0_ where stus0_.cid=?

      充分说明这是执行的懒加载模式。一方控制多方,一方设置懒加载,如果什么都不设置,会是什么情况?经过验证,发现和显式设置懒加载效果一样,也就是说,one-to-many(元素)的懒加载是默认的,这是必须的,是常用的策略。一对多的时候,查询主对象时默认是懒加载。即:查询主对象的时候不会把从对象查询出来,使用从对象的时候才加载从对象。

      如果人为设置为积极加载,则直接全部查询,@OneToMany(cascade={CascadeType.ALL},fetch=FetchType.EAGER),SQL语句为如下,进行了外连接的查询。一次性查了主对象和从对象出来。

    Hibernate: select classroom0_.cid as cid0_1_, classroom0_.cname as cname0_1_, stus1_.cid as cid0_3_, stus1_.sid as sid3_, stus1_.sid as sid1_0_, stus1_.sname as sname1_0_ from ClassRoom classroom0_ left outer join Students stus1_ on classroom0_.cid=stus1_.cid where classroom0_.cid=?

      基于xml文件配置

      一方作为主控方:

    <set name="" >
    
       <key column=""/>
    
       <one-to-many class= "" />
    
    </set>

      一方是班级,持有多方的集合,如下配置:

    <hibernate-mapping>
    <class name="net.nw.vo.fk.otm.ClassRoom" table="classroom">
    <id name="cid" column="cid" type="int">
    <generator class="native"/>
    </id> <property name="cname" column="cname" type="string"/> <set name="stus" >
    <!-- 外键还是班级cid -->
    <key column="cid"/>
    <one-to-many class="net.nw.vo.fk.otm.Students" />
    </set>
    </class>
    </hibernate-mapping>

      多方是学生,作为从对象,别忘了,保留无参构造器,如下配置:

    <hibernate-mapping>
    <class name="net.nw.vo.fk.otm.Students" table="students">
    <id name="sid" column="sid" type="int">
    <generator class="native"/>
    </id> <property name="sname" column="sname" type="string"/>
    </class>
    </hibernate-mapping>

      

      一对多双向外键关联

      其实类似之前的一对一双向外键关联,也是互相持有对方的引用,故也叫双向一对多自身关联。多方持有一方的引用,@ManyToOne(cascade={CascadeType.ALL}),@JoinColumn(name="")。反过来,一方也持有多方的集合,@OneToMany(cascade={CascadeType.ALL}),@JoinColumn(name="")。代码如下:

      基于注解的配置:

    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne; //学生实体类,属于多方,持有一方的引用
    @Entity
    public class Students {
    private int sid; //编号 private String sname; //姓名 private ClassRoom classroom;//学生班级属于一方 //注意:一定要在多方保留这个默认不带参数的构造方法
    public Students() { } public Students(String sname) {
    this.sname = sname;
    } // 多方是设置积极加载,全部级联
    @ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
    @JoinColumn(name="cid",referencedColumnName="cid") // 外键设置为班级id,cid
    public ClassRoom getClassroom() {
    return classroom;
    } public void setClassroom(ClassRoom classroom) {
    this.classroom = classroom;
    } @Id
    @GeneratedValue
    public int getSid() {
    return sid;
    } public void setSid(int sid) {
    this.sid = sid;
    } public String getSname() {
    return sname;
    } public void setSname(String sname) {
    this.sname = sname;
    }
    }

      关键是一方,也必须持有多方的集合,形成你中有我,我中有你的局面,互相控制。但是还是注意,本质上,数据库表里外键cid还是加在了学生表——多方的表里。

    import javax.persistence.*;
    import java.util.Set; //班级类
    @Entity
    public class ClassRoom {
    private int cid;//班级编号 private String cname;//班级名称 private Set<Students> stus; // 一方也持有了多方:学生的集合引用 // 一方也要控制多方,一方设置懒加载,外键还是cid,也就是外键还是加在多方——学生表。
    @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
    @JoinColumn(name="cid")
    public Set<Students> getStus() {
    return stus;
    } public void setStus(Set<Students> stus) {
    this.stus = stus;
    } @Id
    @GeneratedValue
    public int getCid() {
    return cid;
    } public void setCid(int cid) {
    this.cid = cid;
    } public String getCname() {
    return cname;
    } public void setCname(String cname) {
    this.cname = cname;
    }
    }

      测试脚本生成。和之前表一样,只不过控制权双方都有了:

    alter table Students drop foreign key FK73AC29B8559B6D03
    drop table if exists ClassRoom
    drop table if exists Students
    create table ClassRoom (cid integer not null auto_increment, cname varchar(255), primary key (cid))
    create table Students (sid integer not null auto_increment, sname varchar(255), cid integer, primary key (sid))
    alter table Students add index FK73AC29B8559B6D03 (cid), add constraint FK73AC29B8559B6D03 foreign key (cid) references ClassRoom (cid)

      此时先保存谁都可以!控制权是双方都有。

      基于xml配置

      多方:<many-to-one name="group" column="gid"></many-to-one>,一方:记住,一方是持有集合
    <set name="" >
    
       <key column=""></key>
    
       <one-to-many class=""/>
    
    </set>

      本例代码如下:

    <hibernate-mapping>
    <class name="net.nw.vo.bfk.mto.Students" table="students">
    <id name="sid" column="sid" type="int">
    <generator class="native"/>
    </id> <property name="sname" column="sname" type="string"/> <many-to-one name="classroom" column="cid"/>
    </class>
    </hibernate-mapping> ---------------------------------------------------------------------------------- <hibernate-mapping>
    <class name="net.nw.vo.bfk.mto.ClassRoom" table="classroom">
    <id name="cid" column="cid" type="int">
    <generator class="native"/>
    </id> <property name="cname" column="cname" type="string"/> <set name="stus" >
    <key column="cid"/>
    <one-to-many class="net.nw.vo.bfk.mto.Students" />
    </set>
    </class>
    </hibernate-mapping>

      小结:在关系模型中,只存在外键参照关系,而且是many方参照one方。

      多对多的关联关系映射

      现在有一个角色类,和一个特权类,前者保存了都有哪些人(角色)拥有哪些特权,后者保存的是一些特权,比如可以做什么,不可以做什么等让哪些人拥有。如图类关系:

      这就是一个多对多的例子,他们之间在数据库如何实现的关联呢?显然不能互相持有对方主键做外键,那么就需要用到一个新的表——映射表作为中间表:

      再举一个最熟悉的学生的例子,现实中,学生和教师就构成了多对多的关联关系。一个教师可以教很多学生,同时一个学生可以师从很多老师,拿这个例子说明。

      多对多单向外键关联

      其中一个多方持有另一个多方的集合对象,而且前面也分析了,还需要创建一个中间表,先看两个实体对象配置
      
      基于注解的多对多单向外键关系配置:
      一个多方持有另一个多方的集合对象,我让学生持有老师的集合对象引用,同样的另一个多方——老师不做多余配置,且多方要显式设置一个无参构造器,那么学生需要使用注解@ManyToMany和@JoinTable,设置级联全部cascade=CascadeType.ALL,代码如下:
    import javax.persistence.*;
    import java.util.Set; //学生实体类
    @Entity
    public class Students {
    private int sid; //编号 private String sname; //姓名 private Set<Teachers> teachers ; // 我设置学生这个多方去持有老师这个多方的集合,去控制老师 //注意:一定要保留这个默认不带参数的构造方法
    public Students() { } public Students(String sname)
    {
    this.sname = sname;
    } // 先设置多对多的关联,之后必须生成一个中间表,使用JoinTable注解
    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(
    // 设置中间表名
    name="teachers_students",
    // 指定当前对象的外键,本表在中间表的外键名称
    joinColumns={@JoinColumn(name="sid")},
    // 指定关联对象的外键,另一个表在中间表的外键名称。
    inverseJoinColumns={@JoinColumn(name="tid")}
    )
    public Set<Teachers> getTeachers() {
    return teachers;
    } public void setTeachers(Set<Teachers> teachers) {
    this.teachers = teachers;
    } @Id
    @GeneratedValue
    public int getSid() {
    return sid;
    } public void setSid(int sid) {
    this.sid = sid;
    } public String getSname() {
    return sname;
    } public void setSname(String sname) {
    this.sname = sname;
    }
    }

      另一个多方,老师不做多余配置:

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id; @Entity
    public class Teachers {
    private int tid;//教师的编号 private String tname;//教师姓名 public Teachers() { } public Teachers(String tname)
    {
    this.tname = tname;
    } @Id
    @GeneratedValue
    public int getTid() {
    return tid;
    } public void setTid(int tid) {
    this.tid = tid;
    } public String getTname() {
    return tname;
    } public void setTname(String tname) {
    this.tname = tname;
    }
    }

      生成的数据库脚本如下:

    create table Students (sid integer not null auto_increment, sname varchar(255), primary key (sid))
    create table Teachers (tid integer not null auto_increment, tname varchar(255), primary key (tid))
    create table teachers_students (sid integer not null, tid integer not null, primary key (sid, tid))

      主要关注中间表,sid和tid都是作为了中间表的联合主键,他们同时也是外键:

      下面进行插入数据的测试,因为学生持有老师集合引用,且设置了级联,故直接保存学生就ok:
                // 因为学生持有教师的集合,先设置教师
    Set<Teachers> teachers = new HashSet<>(); teachers.add(new Teachers("Wang"));
    teachers.add(new Teachers("Li"));
    teachers.add(new Teachers("Song"));
    teachers.add(new Teachers("Zhang")); Students s = new Students();
    s.setSname("zhangsan");
    s.setTeachers(teachers); session.save(s);
    tx.commit();

      基于xml的多对多单向外键关系配置:

      学生这个多方持有老师的集合,那么持有对方集合的学生映射文件配置如下:

    <hibernate-mapping>
    <class name="net.nw.vo.fk.mtm.Students" table="students">
    <id name="sid" column="sid" type="int">
    <generator class="native"/>
    </id> <property name="sname" column="sname" type="string"/> <!-- 学生表持有老师的集合,如下进行配置 -->
    <set name="teachers" table="students_teachers" cascade="all">
    <!-- table设置中间表,级联是all -->
    <!-- key设置本对象在中间表的外键sid -->
    <key column="sid"/>
    <!-- many-to-many 标签设置对方的表(老师)在中间表的外键tid -->
    <many-to-many class= "net.nw.vo.fk.mtm.Teachers" column="tid"/>
    </set>
    </class>
    </hibernate-mapping>

      老师表配置就简单了:

    <hibernate-mapping>
    <class name="net.nw.vo.fk.mtm.Teachers" table="teachers">
    <id name="tid" column="tid" type="int">
    <generator class="native"/>
    </id> <property name="tname" column="tname" type="string"/>
    </class>
    </hibernate-mapping>

      进行测试(删除之前的表,先删除中间表,在删除老师表,最后删除学生表):

                Set<Teachers> teachers = new HashSet<>();
    
                teachers.add(new Teachers("Teacher Wang"));
    teachers.add(new Teachers("Teacher Li"));
    teachers.add(new Teachers("Teacher Song"));
    teachers.add(new Teachers("Teacher Zhang")); Students s = new Students();
    s.setSname("zhangsan");
    s.setTeachers(teachers); session.save(s);
    tx.commit();

      多对多双向外键关联

      和之前的类似,是互相持有对方的集合,双方持有对方的集合对象,其中一方设置@ManyToMany(mappedBy=""),另一方:

    @ManyToMany
    
      @JoinTable(
    
      name="",
    
      joinColumns={@JoinColumn(name="")},
    
      inverseJoinColumns={@JoinColumn(name="")}
    
    )

      基于注解的配置,看具体代码:

    import javax.persistence.*;
    import java.util.Set; //学生实体类
    @Entity
    public class Students {
    private int sid; //编号 private String sname; //姓名 private Set<Teachers> teachers ; //注意:一定要保留这个默认不带参数的构造方法
    public Students() {
    } public Students(String sname)
    {
    this.sname = sname;
    } @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(
    name="teachers_students",
    joinColumns={@JoinColumn(name="sid")},
    inverseJoinColumns={@JoinColumn(name="tid")}
    )
    public Set<Teachers> getTeachers() {
    return teachers;
    } public void setTeachers(Set<Teachers> teachers) {
    this.teachers = teachers;
    } @Id
    @GeneratedValue
    public int getSid() {
    return sid;
    } public void setSid(int sid) {
    this.sid = sid;
    } public String getSname() {
    return sname;
    } public void setSname(String sname) {
    this.sname = sname;
    }
    }

      关键是另一方的配置,前面总结了,双向关联不会真的是互相维持,只能交给一方去维护:

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.ManyToMany;
    import java.util.Set; @Entity
    public class Teachers {
    private int tid;//教师的编号 private String tname;//教师姓名 private Set<Students> stus ; public Teachers() {
    } public Teachers(String tname)
    {
    this.tname = tname;
    } // 把控制权交给student类——teachers集合引用
    @ManyToMany(mappedBy="teachers")
    public Set<Students> getStus() {
    return stus;
    } public void setStus(Set<Students> stus) {
    this.stus = stus;
    } @Id
    @GeneratedValue
    public int getTid() {
    return tid;
    } public void setTid(int tid) {
    this.tid = tid;
    } public String getTname() {
    return tname;
    } public void setTname(String tname) {
    this.tname = tname;
    }
    }

      生成数据库脚本:(和之前的单向多对多一样的表结构,关键看实体中控制权的变化)

    create table Students (sid integer not null auto_increment, sname varchar(255), primary key (sid))
    create table Teachers (tid integer not null auto_increment, tname varchar(255), primary key (tid))
    create table teachers_students (sid integer not null, tid integer not null, primary key (sid, tid))

      基于xml的配置:

      其中一方:
    <set name="teachers" table="students_teachers">
    
      <key column="sid"></key>
    
      <many-to-many class="net.nw.vo.Teachers" column="tid"/>
    
    </set>

      另一方:

    <set name="students" table="students_teachers">
    
      <key column="tid"></key>
    
      <many-to-many class="net.nw.vo.Students" column="sid"/>
    
    </set>

      具体代码:

    <hibernate-mapping>
    <class name="net.nw.vo.bfk.mtm.Students" table="students">
    <id name="sid" column="sid" type="int">
    <generator class="native"/>
    </id> <property name="sname" column="sname" type="string"/> <set name="teachers" table="students_teachers" cascade="all">
    <key column="sid"/>
    <many-to-many class= "net.nw.vo.bfk.mtm.Teachers" column="tid"/>
    </set>
    </class>
    </hibernate-mapping> --------------------------------------------------------------------- <hibernate-mapping>
    <class name="net.nw.vo.bfk.mtm.Teachers" table="teachers">
    <id name="tid" column="tid" type="int">
    <generator class="native"/>
    </id> <property name="tname" column="tname" type="string"/> <set name="stus" table="students_teachers" cascade="all">
    <key column="tid"/>
    <many-to-many class= "net.nw.vo.bfk.mtm.Students" column="sid"/>
    </set>
    </class>
    </hibernate-mapping>

      

      注意:set元素配置;

      属性name  指定类的属性名,table指定多对多关联关系中间表,cascade 级联操作属性:save-update、delete、all、none,一般all就ok,lazy属性可以指定是否是懒加载。set的子元素key元素——设定本表在中间表的外键名称。

        inverse属性设置:

      inverse是Hibernate中双向关联关系中的基本概念,在xml配置里,用来设置关系由哪一方来维护,inverse=true 表示被控方,false表示主控方,在多对一,一对一关联关系中,Hibernate默认设置多方的inverse=true,即多方为被控方,一方的inverse=false,即一方为主控方。在多对多关系中需要我们自己设置哪一方为被控方即设置inverse=true。上述例子没有设置,其实需要手动设置的,对应注解里的mappedBy属性。

      关联关系的优缺点

      使用关联关系,就可以直接操作内存中的对象,不用每次都查询数据库,会提高效率;而且域模型真实反映了客观世界的关系,但是缺点就是建立复杂的关联关系会给程序开发带来麻烦,当修改一个对象时,会牵连其它的对象,这也是为什么很多人说什么hibernate不好用……其实就是没学好,一般人掌握的不扎实,总出错,后来MyBatis横空出现,大家就去用它了,比起hibernate来,上手非常简单,使用原生SQL……本质不是一个真正的ORM架构模式的实现框架。当然Mybatis也有它的优点和缺点,以后再总结它。
      一句话:具体要建立对象之间的什么关联关系要根据具体的需求。具体使用什么框架,要听领导的,哈哈。、
     

      问题小结

      • 注意在多对一/一对多关系里:多方必须保留一个不带参数的构造器!
      • 如果没有设置级联ALL,那么需要在保存的时候先保存班级,在保存学生,否则出错: object references an unsaved transient instance - save the transient instance before flushing: 
      • 多对一时候,多方设置EAGER加载,一对多的时候,一方设置LAZY加载
      • 多对多关联,多方需要保留一个无参构造器。
      • 转载地址:http://www.cnblogs.com/kubixuesheng/p/5300437.html
      • 博主真是大神级啊100个赞!
      • 菜鸟不会改大神的样式,一点一点的粘的代码。。。。。

Hibernate—— 一对多 和 多对多关联关系映射(xml和注解)总结(转载)的更多相关文章

  1. Hibernate(6)—— 一对多 和 多对多关联关系映射(xml和注解)总结

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下: One to Many 映射关系 多对一单向外键关联(XML/Annotation) 一对多单向外键关联(XM ...

  2. (转)Hibernate框架基础——多对多关联关系映射

    http://blog.csdn.net/yerenyuan_pku/article/details/52756536 多对多关联关系映射 多对多的实体关系模型也是很常见的,比如学生和课程的关系.一个 ...

  3. Hibernate框架之双向多对多关系映射

    昨天跟大家分享了Hibernate中单向的一对多.单向多对一.双向一对多的映射关系,今天跟大家分享下在Hibernate中双向的多对多的映射关系 这次我们以项目和员工举个栗子,因为大家可以想象得到,在 ...

  4. Hibernate 一对多,多对多,多对一检索策略

    一.概述 我们先来谈谈检索数据时的两个问题: 1.不浪费内存   2.更好的检索效率 以上说的问题都是我们想要避免的,接下来就引出了我们要讨论的话题---------------hibernate检索 ...

  5. Java基础-SSM之mybatis一对多和多对一关系映射

    Java基础-SSM之mybatis一对多和多对一关系映射 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.准备测试环境(创建数据库表)  1>.创建customers表: ...

  6. Java进阶知识10 Hibernate一对多_多对一双向关联(Annotation+XML实现)

    本文知识点(目录): 1.Annotation 注解版(只是测试建表)    2.XML版 的实现(只是测试建表)    3.附录(Annotation 注解版CRUD操作)[注解版有个问题:插入值时 ...

  7. mybatis多对多关联关系映射

    mybatis多对多关联关系映射 多对多关系在java类实体中表示为,一个类中包含了集合为另一个类的属性.而这连个实体都需要包含对方的集合类的属性. 例如:订单和商品,一个订单包含多个商品,一个商品又 ...

  8. Hibernate自身一对多和多对多关系映射

    一对多关系映射大家都明白,关系双方都一个含有对方多个引用,但自身一对多很多同学都不明白什么意思,那么首先我就说明一下什么是自身一对多,其实也很好理解,自身一对多就是自身含有本身的多个引用,例如新闻类别 ...

  9. hibernate 2 一对多、多对一 双向映射

    多对一或一对多中,在多的一方维护关系效率高 一:java实体类 1.Classes.java package cn.gs.ly.school.entity; import java.util.Set; ...

随机推荐

  1. 01 Linux入门介绍

    一.Linux 初步介绍 Linux的优点 免费的,开源的 支持多线程,多用户 安全性好 对内存和文件管理优越 系统稳定 消耗资源少 Linux的缺点 操作相对困难 一些专业软件以及游戏支持度不足 L ...

  2. 提供VR定制开发、AR定制开发(VR游戏定制、应用定制)

    设置输出路径 添加烘培输出的贴图类型 添加“LightingMap”类型 设置烘培贴图大小和目标贴图位置为“自发光” 设置烘培材质,选择“输出到源” 点击“渲染”即可 24.标准材质贴图的烘培光影处理 ...

  3. 5. Longest Palindromic Substring

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  4. Hadoop总结篇之一------开篇

    从今天开始新的系列:Hadoop总结篇 之前的hadoop学习篇由于是学习过程中随手记下来的一些内容,不具有系统性.所以在这个系列中,将凭着这段时间的研究心得,来记录一些自认为比较重要的东西. 本系列 ...

  5. Eclipse插件安装方式及使用说明

    拷贝安装方式 1.通过ECLIPSE_HOME\plugins安装 在eclipse的主目录ECLIPSE_HOME, 比如在我的机器上安装的目录是:ECLIPSE_HOME有一个plugins的目录 ...

  6. 说说Web.Config与App.Config

    说到web.config和app.config大家都很熟悉,我们都叫他们配置文件,平时用的多,注意的少.两个有啥区别呢,很简单,一句话:如果是web程序,如webform项目类型和mvc项目类型就是w ...

  7. 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...

  8. [SQL]收缩数据库日志

    SELECT NAME, recovery_model_desc FROM sys.databases --查看数据库的模式 ALTER DATABASE TOMS SET RECOVERY SIMP ...

  9. 【解决】AWS服务控制台中上传文件失败

    使用IE 11,在 AWS Services Console 中不管是 S3 还是 Elastic Beanstalk 的页面中上传页面都会失败,提示信息如下: A problem occurred ...

  10. sqlmap用户手册

    http://192.168.136.131/sqlmap/mysql/get_int.php?id=1 当给sqlmap这么一个url的时候,它会: 1.判断可注入的参数2.判断可以用那种SQL注入 ...