Hibernate多表映射

Posted 海盗屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate多表映射相关的知识,希望对你有一定的参考价值。

一对多|多对一

  一个分类对应多个商品,一个商品只属于一个分类

  创建分类表 products用set装,set特点值不能够重复

package com.hibernate.domain;

import java.util.HashSet;
import java.util.Set;

public class Category {
    private Integer cid;
    private String cname;

    private Set<Product> products = new HashSet<Product>();
    
    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    public Set<Product> getProducts() {
        return products;
    }

    public void setProducts(Set<Product> products) {
        this.products = products;
    }

}

  创建商品表 category表示所属分类

package com.hibernate.domain;

public class Product {
    private Integer pid;
    private String pname;
    private Category category;

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

}

配置映射关系

Category.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.hibernate.domain">
    <class name="Category" table="Category">
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <property name="cname"/>
        <!--配置一对多关系
        set表示所有products
        name 实体类中 商品集合属性
        -->
        <set name="products">
            <!--column 外键名 -->
            <key column="cpid"></key>
            <one-to-many class="Product"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

Product.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.hibernate.domain">
    <class name="Product" table="Product">
        <id name="pid">
            <generator class="native"/>
        </id>
        <property name="pname"/>

        <!--配置多对一 所属分类
        name 分类属性
        class 分类全路径
        column 外键名称 要与 一对多设置的外键一样
        -->
        <many-to-one name="category" class="Category" column="cpid"></many-to-one>
    </class>
</hibernate-mapping>

配置全局映射hibernate.cfg.xml

<mapping resource="com/hibernate/domain/Category.hbm.xml"></mapping>
<mapping resource="com/hibernate/domain/Product.hbm.xml"></mapping>

插入

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            Category category = new Category();
            category.setCname("产品分类");

            Product p1 = new Product();
            p1.setPname("产品1");
            p1.setCategory(category);

            Product p2 = new Product();
            p2.setPname("产品2");
            p2.setCategory(category);

            category.getProducts().add(p1);
            category.getProducts().add(p2);

            session.save(category);
            session.save(p1);
            session.save(p2);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

为已有分类追加商品

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            //获得已有分类对象
            Category category = session.get(Category.class,1);

            Product p = new Product();
            p.setCategory(category);
            p.setPname("小米电脑");

            category.getProducts().add(p);

            session.save(p);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

移除分类下的商品 (将商品的外键设置为null 并没有删除)

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            //获得已有分类对象
            Category category = session.get(Category.class,1);

            Product p = session.get(Product.class,2);

            category.getProducts().remove(p);

            p.setCategory(null);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

 

cascade级联操作

级联保存更新 只保存分类 自动把产品也保存

首先配置category.hbm.xml

<hibernate-mapping package="com.hibernate.domain">
    <class name="Category" table="Category">
       ...
        <!--级联操作:cascade
            save-update:级联保存更新
            delete:级联删除
            all:save-update+delete -->
        <set name="products" cascade="save-update">
            <key column="cpid"></key>
            <one-to-many class="Product"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

编辑代码 只需要save 分类即可

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            Category category = new Category();
            category.setCname("产品分类1");

            Product p1 = new Product();
            p1.setPname("产品11");
            p1.setCategory(category);

            Product p2 = new Product();
            p2.setPname("产品22");
            p2.setCategory(category);

            category.getProducts().add(p1);
            category.getProducts().add(p2);

            session.save(category);
            //session.save(p1);
            //session.save(p2);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

级联删除 配置hbm.xml 为delete

<hibernate-mapping package="com.hibernate.domain">
    <class name="Category" table="Category">
        。。。
        <!--级联操作:cascade
            save-update:级联保存更新
            delete:级联删除
            all:save-update+delete-->
        <set name="products" cascade="delete">
            <key column="cpid"></key>
            <one-to-many class="Product"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

编写删除代码 该分类下的产品都将被删除

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            Category category = session.get(Category.class,4);
            session.delete(category);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

cascade="all" 就是 添加 修改 删除时 不用save 商品 直接操作分类即可

在产品的配置文件中 也可以设置 many-to-one  cascade="save-update" 操作时 就是不用操作分类表了

作用:简化操作,如果一定要用就用save-update,不要用delete。

 

inverse属性

关系维护,在保存时,两方都会维护外键关系,存在多余的维护关系语句。

上面保存分类和商品时 生成的sql语句如下:

Hibernate:     insert     into        Category        (cname)     values        (?)
Hibernate:     insert     into        Product        (pname, cpid)     values        (?, ?)
Hibernate:     insert     into        Product        (pname, cpid)     values        (?, ?)
Hibernate:     update        Product     set        cpid=?     where        pid=?
Hibernate:     update        Product     set        cpid=?     where        pid=?

配置Category.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.hibernate.domain">
    <class name="Category" table="Category">
        。。。
        <!-- inverse:配置关系是否维护
            true:不维护
            false:维护(默认值)-->
        <set name="products" inverse="true">
            <key column="cpid"></key>
            <one-to-many class="Product"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

修改配置文件后 重新运行程序 sql语句如下:

Hibernate:     insert     into        Category        (cname)     values        (?)
Hibernate:     insert     into        Product        (pname, cpid)     values        (?, ?)
Hibernate:     insert     into        Product        (pname, cpid)     values        (?, ?)

inverse的作用就是 优化性能,此配置只能设置一的一方放弃维护关系,多的一方不可以放弃维护关系。

 

多对多

  一个用户可以有很多角色,一个角色可以有很多用户。

  使用中间表,至少两列,都是外键,分别引用其他两张表的主键

创建user表 set装role集合

package com.hibernate.domain;

import java.util.HashSet;
import java.util.Set;

public class User {
    private Integer uid;
    private String uname;
    private Set<Role> roles = new HashSet<Role>();

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

创建role表 set装user

package com.hibernate.domain;

import java.util.HashSet;
import java.util.Set;

public class Role {
    private Integer rid;
    private String rname;
    private Set<User> users = new HashSet<User>();

    public Integer getRid() {
        return rid;
    }

    public void setRid(Integer rid) {
        this.rid = rid;
    }

    public String getRname() {
        return rname;
    }

    public void setRname(String rname) {
        this.rname = rname;
    }

    public Set<User> getUsers() {
        return users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }

}

编写 User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.hibernate.domain">
    <class name="User" table="User">
        <id name="uid">
            <generator class="native"/>
        </id>
        <property name="uname"/>
        <!--多对多关系
            table:中间表名
        -->
        <set name="roles" table="User_Role">
            <!--column 别人引入我的外键 uid -->
            <key column="uid"></key>
            <!--column 另外一方的外键 rid -->
            <many-to-many class="Role" column="rid"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

编写role.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.hibernate.domain">
    <class name="Role" table="Role">
        <id name="rid">
            <generator class="native"/>
        </id>
        <property name="rname"/>
        <set name="users" table="User_Role">
            <key column="rid"></key>
            <many-to-many column="uid" class="User"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

最后写入全局配置中

<mapping resource="com/hibernate/domain/User.hbm.xml"></mapping>
<mapping resource="com/hibernate/domain/Role.hbm.xml"></mapping>

添加操作

 

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            User u1 = new User();
            u1.setUname("武大郎");

            User u2 = new User();
            u2.setUname("武松");

            Role r1 = new Role();
            r1.setRname("卖烧饼");

            Role r2 = new Role();
            r2.setRname("武都头");

            u1.getRoles().add(r1);
            u2.getRoles().add(r2);
            //武松帮大哥卖卖烧饼
            u2.getRoles().add(r2);

            r1.getUsers().add(u1);
            r1.getUsers().add(u2);
            r2.getUsers().add(u2);

            session.save(u1);
            session.save(u2);
            session.save(r1);
            session.save(r2);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

虽然会插入数据成功 但是运行后会报错:could not execute statement(插入主键报错),是因为双方都维护关系导致。

解决办法1: 去掉一方的维护关系

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            User u1 = new User();
            u1.setUname("武大郎");

            User u2 = new User();
            u2.setUname("武松");

            Role r1 = new Role();
            r1.setRname("卖烧饼");

            Role r2 = new Role();
            r2.setRname("武都头");

            u1.getRoles().add(r1);
            u2.getRoles().add(r2);
            //武松帮大哥卖卖烧饼
            u2.getRoles().add(r2);

//            r1.getUsers().add(u1);
//            r1.getUsers().add(u2);
//            r2.getUsers().add(u2);

            session.save(u1);
            session.save(u2);
            session.save(r1);
            session.save(r2);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

解决办法2:

修改配置文件,选择一方不维护关系 加入节点inverse=true 

<hibernate-mapping package="com.hibernate.domain">
<class name="User" table="User">
    <id name="uid">
        <generator class="native"/>
    </id>
    <property name="uname"/>
    <!--user一方 放弃维护 -->
    <set name="roles" table="User_Role" inverse="true">
        <key column="uid"></key>
        <many-to-many class="Role" column="rid"></many-to-many>
    </set>
</class>
</hibernate-mapping>

然后把注释放开 ,运行 也不会报错了。

 

级联操作cascade

编辑user.hbm.xml配置文件

<hibernate-mapping package="com.hibernate.domain">
<class name="User" table="User">
    <id name="uid">
        <generator class="native"/>
    </id>
    <property name="uname"/>
    <!--user一方 放弃维护 -->
    <set name="roles" table="User_Role" inverse="true" cascade="save-update">
        <key column="uid"></key>
        <many-to-many class="Role" column="rid"></many-to-many>
    </set>
</class>
</hibernate-mapping>

剩下联行save而已

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            User u1 = new User();
            u1.setUname("武大郎");

            User u2 = new User();
            u2.setUname("武松");

            Role r1 = new Role();
            r1.setRname("卖烧饼");

            Role r2 = new Role();
            r2.setRname("武都头");

            u1.getRoles().add(r1);
            u2.getRoles().add(r2);
            //武松帮大哥卖卖烧饼
            u2.getRoles().add(r2);

            r1.getUsers().add(u1);
            r1.getUsers().add(u2);
            r2.getUsers().add(u2);


            session.save(u1);
            session.save(u2);
            //session.save(r1);
            //session.save(r2);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

只save一方,另一方自动save。

不建议使用 如果非要用就用save-update。

 

解除权限关系

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtils.getCurrentSession();
        Transaction trans = session.beginTransaction();

        try {
            User u = session.get(User.class,2);
            //武松不想卖烧饼了
            Role r = session.get(Role.class,1);

            r.getUsers().remove(u);

            trans.commit();
        } catch (Exception ex){
            System.out.println(ex);
            trans.rollback();
        }
    }
}

 

以上是关于Hibernate多表映射的主要内容,如果未能解决你的问题,请参考以下文章

hibernate中多表映射关系配置

hibernate中多表映射关系配置

Hibernate的HQL多表查询

Hibernate多表关系配置——多对多对关系映射

Hibernate多表关系配置——多对一关系映射

Hibernate多表关系配置——一对一关系映射