美文网首页
hibernate多对多映射之增删改查

hibernate多对多映射之增删改查

作者: nextliving | 来源:发表于2018-04-22 14:49 被阅读329次

    本文是hibernate多对多双向映射的增删改查实践总结笔记,全部基于注解。文中的多对多关系指的是老师有多个学生,学生也有多个老师。

    环境

    • JDK1.8.92

    • Idea 2016.1

    • Hibernate 5.2.5

    • Junit4

    实体类

    Student.java:

    
    package com.engchen.pojo;
    
    import javax.persistence.*;
    
    import java.util.Set;
    
    /**
    
     * Created by chenxin on 09/02/2017.
    
     */
    
    @Entity
    
    @Table(name="student")
    
    public class Student {
    
     @Id
    
     @GeneratedValue(strategy = GenerationType.AUTO)
    
     private int sid;
    
     @Column(name="name")
    
     private String name;
    
     @ManyToMany(cascade = CascadeType.ALL)
    
     @JoinTable(
    
     name="teas_stus",
    
     joinColumns = {@JoinColumn(name = "sid")},
    
     inverseJoinColumns = {@JoinColumn(name = "tid")}
    
     )
    
     private Set<Teacher> teas;
    
     public Student() {
    
     }
    
     public Student(String name) {
    
     this.name = name;
    
     }
    
     public Set<Teacher> getTeas() {
    
     return teas;
    
     }
    
     public void setTeas(Set<Teacher> teas) {
    
     this.teas = teas;
    
     }
    
     public String getName() {
    
     return name;
    
     }
    
     public void setName(String name) {
    
     this.name = name;
    
     }
    
     public int getSid() {
    
     return sid;
    
     }
    
     public void setSid(int sid) {
    
     this.sid = sid;
    
     }
    
    }
    
    

    Teacher.java:

    
    package com.engchen.pojo;
    
    import javax.persistence.*;
    
    import java.util.Set;
    
    /**
    
     * Created by chenxin on 10/02/2017.
    
     */
    
    @Entity
    
    @Table(name="teacher")
    
    public class Teacher {
    
     @Id
    
     @GeneratedValue(strategy = GenerationType.AUTO)
    
     private int tid;
    
     private String name;
    
     public Teacher(){
    
     }
    
     public Teacher(String name) {
    
     this.name = name;
    
     }
    
     @ManyToMany(mappedBy = "teas")
    
     private Set<Student> stus;
    
     public int getTid() {
    
     return tid;
    
     }
    
     public void setTid(int tid) {
    
     this.tid = tid;
    
     }
    
     public String getName() {
    
     return name;
    
     }
    
     public void setName(String name) {
    
     this.name = name;
    
     }
    
     public Set<Student> getStus() {
    
     return stus;
    
     }
    
     public void setStus(Set<Student> stus) {
    
     this.stus = stus;
    
     }
    
    }
    
    

    关于一些注解的说明:

    • @Entity,@Table,@Id,@ManyToMany,@JoinTable等都是JPA2.1规范中提供的标准注解。

    • 在多对多双向映射中,实际上关系也是由其中一方负责维护的,则该方为主控方(owner)同时另一方为被控方(inverse)。

    • 多对多主控方的Set属性上一般用@ManyToMany和@JoinTable同时注解。被控方的Set属性一般用@ManyToMany(mappedBy="")注解。

    • mappedBy:双向的关联关系中必须在其中一方使用,表示放弃关系的控制权,一般用在被控方,比如多对一中的@OneToMany.双向的关联必须设置mappedBy属性。也就是说双向关联只能交给一方去控制,不可能在双方都设置外键保存关联关系,否则都无法保存。比如在学生和省份证的一对一双向关联关系中,学生表包含身份证id,同时身份证表也包含学生id,互为对方的外键。如果互为主控方,那么先保存谁呢?如果要保存学生,则应该先保存身份证,而身份证也是主控方,要保存身份证必须先保存学生……死循环发生。

    • 多对多关系实际执行上要拆分成2个一对多关系,共需要3张表,student,teacher,teas_stus。第三张表(中间表)一般只需要前2张表的主键sid和tid。学生和老师表都是一方,对应多个teas_stus.

    • @JoinTable用于生成第三张表,配置表名和需要加入表中的字段,joinColumns用于配置主控方(Student)需要加入到中间表的字段,示例中只加入了sid,也可以加入Student的其它字段。inverseJoinColumns用于配置被控方(Teacher)需要加入到中间表的字段,示例中只加入了tid,也可以加入Teacher的其它字段。

    • 非注解方式添加第三张表是在xml文件的<set table=""></set>标签的table属性指定表名,对应注解的@JoinTable,具体参考这篇文章:Hibernate多对多删除问题的解决

    • @JoinColumn表示加入外键(在多对多关系中一般不用@JoinColumn,因为多对多关系中不靠其中一方加入一个外键维护和另一方的关系,而是靠第三张表),一般用在需要维护关系的一方(比如一对一中的任意一方,多对一中通常是多方),该方是主控方(owner)。与此同时,mappedBy一般用在被控方(inverse).更多关于@JoinColumn和mappedBy的疑惑请参考stackoverflow上的这个问答: JPA JoinColumn vs mappedBy

    • 关于单向和双向:Student中有个teas的Set集合指向Teacher,同时Teacher中也有个stus的Set集合指向Student,这就是双向映射。如果Teacher中没有stus的Set集合指向Student,这就是单向。

    工具类

    HibernateUtil.java:

    
    package com.engchen.util;
    
    import org.hibernate.Session;
    
    import org.hibernate.SessionFactory;
    
    import org.hibernate.cfg.Configuration;
    
    public class HibernateUtil {
    
     private static SessionFactory factory;
    
     static {
    
     factory=new Configuration().configure().buildSessionFactory();
    
     }
    
     public static Session openSession() {
    
     return factory.openSession();
    
     }
    
    }
    
    

    hibernate.cfg.xml配置

    classpath路径下的hibernate.cfg.xml配置如下:

    
    <?xml version="1.0" encoding="UTF-8"?>
    
    <!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="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    
     <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate?autoReconnect=true&amp;useUnicode=true&amp;createDatabaseIfNotExist=true&amp;characterEncoding=utf8</property>
    
     <property name="hibernate.connection.username">root</property>
    
     <property name="hibernate.connection.password">123</property>
    
     <!-- 其他的配置 -->
    
     <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
    
     <property name="hibernate.hbm2ddl.auto">update</property>
    
     <property name="hibernate.show_sql">true</property>
    
     <!-- 加载映射文件 -->
    
     <mapping class="com.engchen.pojo.Student"></mapping>
    
     <mapping class="com.engchen.pojo.Teacher"></mapping>
    
     </session-factory>
    
    </hibernate-configuration>
    
    

    测试添加数据

    新建测试类TestHibernate.java,添加以下测试方法:

    
     @Test
    
     public void add(){
    
     Session session = HibernateUtil.openSession();
    
     Transaction tx = session.beginTransaction();
    
     Teacher tea1=new Teacher("wang");
    
     Teacher tea2=new Teacher("zhang");
    
     Set<Teacher> teas=new HashSet<>();
    
     teas.add(tea1);
    
     teas.add(tea2);
    
     Student stu1=new Student("aa");
    
     Student stu2=new Student("bb");
    
     Set<Student> stus=new HashSet<>();
    
     stus.add(stu1);
    
     stus.add(stu2);
    
     tea1.setStus(stus);
    
     tea2.setStus(stus);
    
     stu1.setTeas(teas);
    
     stu2.setTeas(teas);
    
     session.save(stu1);
    
     session.save(stu2);
    
     tx.commit();
    
     session.close();
    
     }
    
    

    使用navicat连接数据库可以看到新生成的3张表:student,teacher,teas_stus.

    测试检索数据

    hql检索student表中的全部学生:

    
     @Test
    
     public void hqlQuery(){
    
     Session s=HibernateUtil.openSession();
    
     String hql="from Student";
    
     Query query=s.createQuery(hql);
    
     List<Student> stus=query.list();
    
     for (Student stu:stus
    
     ) {
    
     System.out.println(stu.getName());
    
     }
    
     s.close();
    
     }
    
    

    使用hql检索name为wang的老师的全部学生:

    这种类型的检索涉及多表联结查询。

    
     @Test
    
     public void multiHqlQuery(){
    
     Session s = HibernateUtil.openSession();
    
     String hql="select s from Student as s,Teacher as t where t.name='wang' and s.sid in elements(t.stus) ";
    
     Query query = s.createQuery(hql);
    
     List<Student> stus = query.list();
    
     for (Student stu :
    
     stus) {
    
     System.out.println(stu.getName()+" 学号:"+stu.getSid());
    
     }
    
     s.close();
    
     }
    
    

    更新关系

    现在要把1号学生和名字为wang的老师解除关系,对应方法如下:

    
     @Test
    
     public void update(){
    
     Session session = HibernateUtil.openSession();
    
     Transaction tx = session.beginTransaction();
    
     Student stu=(Student)session.load(Student.class,1);
    
     Set<Teacher> teas=stu.getTeas();
    
     CopyOnWriteArraySet<Teacher> cs=new CopyOnWriteArraySet(teas);
    
     for (Teacher tea :
    
     cs) {
    
     if (tea.getName().equals("wang")) {
    
     cs.remove(tea);
    
     }
    
     }
    
     stu.setTeas(cs);
    
     session.update(stu);
    
     tx.commit();
    
     session.close();
    
     }
    
    

    关于CopyOnWriteArraySet的说明:普通的HashSet不支持边遍历边删除,详情参考:Java删除List和Set集合中元素

    参考

    相关文章

      网友评论

          本文标题:hibernate多对多映射之增删改查

          本文链接:https://www.haomeiwen.com/subject/tvcclftx.html