• Hibernatel框架关联映射


    Hibernatel框架关联映射

    Hibernate程序执行流程:

    1.集合映射

    需求:网络购物时,用户购买商品,填写地址
    每个用户会有不确定的地址数目,或者只有一个或者有很多。这个时候不能把每条地址分别放在一个字段,这样的话会非常的麻烦。

    数据库设计:创建两个表,一个是保存用户信息,一个表保用户的地址。使用外键引用。javaBean对象中的地址信息使用Set集合方式。配置映射使集合能够映射到数据库

    1.1.集合映射的配置和保存

    集合分为Set,List,Map集合,也有数组可用,使用方法大体上是相同的,区别只是在映射配置的时候有细微的区别。下面是javaBean对象和集合映射的配置写法,以及测试类。

    JavaBean对象:

    
    /**
     * User对象
     */
    public class User {
        private int userId;
        private String userName;
        //一个用户,对应的多个地址
        private Set<String> address;
        private List<String> addressList = new ArrayList<String>();
    //    private String[] addressArray;//映射方式和List一样,配置的名字换成<array name=""/>
        private Map<String,String> addressMap = new HashMap<String, String>();
        
        //set方法和get方法省略
    }
    
    

    映射文件的写法:
    映射文件我在主文件配置了自动创建表,所以不用单独创建表了

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
    	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping package="a_collection">
    
    	<!--javabean和表的映射-->
    	<class name="User" table="t_user">
    		<!--1.主键映射-->
    		<id name="userId" column="id">
    			<generator class="native"/>
    		</id>
    		<!--2.普通字段映射-->
    		<property name="userName"/>
    		<!--
    		   3.set集合属性映射
    			 name:指定要映射的set集合的属性
    			 table:集合属性要映射到的表
    			 key:指定集合表(t_address)的外键字段
    			 element:指定集合表的其他字段
    			 type:元素类型,一定要指定
    		-->
    		<set name="address" table="t_address">
    			<key column="uid"/>
    			<element column="address" type="string"/>
    		</set>
    
            <!--
            List集合映射
                list-index:指定的是拍序列名称,因为要保证list集合的有序
            -->
            <list name="addressList" table="t_addressList">
                <key column="uid"/>
                <list-index column="idx"/>
                <element column="address" type="string"/>
            </list>
    
            <!--
               Map集合的映射
                key:外键字段
                map-key:指定Map的key
                element:指定map的value
            -->
            <map name="addressMap" table="t_addressMap">
                <key column="uid"/>
                <map-key type="string" column="shortName"/>
                <element column="address" type="string"/>
            </map>
    
    
    	</class>
    </hibernate-mapping>
    
    

    测试类:

    package a_collection;
    
    
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    import org.junit.Test;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     *集合映射的测试类
     */
    public class App {
        private static SessionFactory sf;
        static {
            sf = new Configuration().configure().buildSessionFactory();
        }
    
        //保存Set
        @Test
        public void testSaveSet() throws Exception{
            Session session = sf.openSession();
            session.beginTransaction();
    
            //--保存adress
            Set<String> addressSet = new HashSet<String>();
            addressSet.add("广州");
            addressSet.add("深圳");
            //用户对象
            User user = new User();
            user.setUserName("Jack");
            user.setAddress(addressSet);
    
            //保存到数据库
            session.save(user);
    
    
            session.getTransaction().commit();
            session.close();
        }
    
        //保存List/Map
        @Test
        public void testSaveList() throws Exception{
            Session session = sf.openSession();
            session.beginTransaction();
    
            //用户对象
            User user = new User();
            user.setUserName("Jack");
            user.getAddressList().add("广州");
            user.getAddressList().add("深圳");
    
            //保存到数据库
            session.save(user);
    
            session.getTransaction().commit();
            session.close();
        }
    
        //保存Map
        @Test
        public void testSaveMap() throws Exception{
            Session session = sf.openSession();
            session.beginTransaction();
    
            //用户对象
            User user = new User();
            user.setUserName("Tom");
            user.getAddressMap().put("A001","广州");
            user.getAddressMap().put("A002","深圳");
    
            //保存到数据库
            session.save(user);
    
            session.getTransaction().commit();
            session.close();
        }
    
    
    }
    
    

    1.2.集合数据的获取

    /**
     * 集合数据的获取
     */
    public class App02 {
        private static SessionFactory sf;
        static {
            sf = new Configuration().configure().buildSessionFactory();
        }
    
        //获取
        @Test
        public void testGet() throws Exception{
            Session session = sf.openSession();
            session.beginTransaction();
    
            //获取user对象,因为User中有set,list,map属性,所有会全部查询出来
            User user = (User)session.get(User.class, 3);//及时加载
    
            //查询用户,同时可以获取用	户关联的set,list,map集合的数据,因为有正确映射
            //当使用到集合数据的时候,才向数据库发送执行的sql语句(懒加载)
            System.out.println(user);
    
            session.getTransaction().commit();
            session.close();
        }
    }
    
    

    集合映射中,映射的集合元素,都是普通的类型,但是能不能为对象呢?这个时候就可以使用一对多或者多对一。

    2.一对多或多对一映射(重点)

    2.1.逻辑分析

    需求:部门和员工的关系:
    一个部门有多个员工--一对多
    多个员工,属于同一个部门--多对一

    数据库:有两个表,一个是员工表(id,姓名,薪水,部门外键),一个是部门表(id,部门名称),员工表中有部门表的外键。

    JavaBean设计:要有两个对象,一个员工一个部门,是两个对象。

    映射:
    为了将数据库和JavaBean映射起来,映射配置中如果是配置一方,就只能通过一方维护到另外一方,如果要两方都能维护到另外一方,需要两方都能配置。

    2.2.代码实现

    创建两个JavaBean对象,一个是员工,一个是部门

    /**
     * 员工方对象
     */
    public class Employee {
        private int empId;
        private String empName;
        private double salary;
        //员工与部门[多对一]
        private Dept dept;
        //省略get和set
        }
    
    /**
     * 部门方对象
     */
    public class Dept {
        private int depId;
        private String deptName;
        //部门对应多个员工[一对多]
        private Set<Employee> emps = new HashSet<Employee>();
            //省略get和set
        }
    

    对两个JavaBean对象分别进行映射:
    Employee对象的映射:Employee.hbm.xml

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
    	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="b_oneToMany">
    	<class name="Employee" table="t_employee">
    		<id name="empId">
    			<generator class="native"/>
    		</id>
    		<property name="empName" length="20"/>
    		<property name="salary" type="double"/>
    
    		<!--
    		多对一关联映射配置(员工管理到部门)
    		1.映射的部门属性:name="dept"
    		2.映射的部门对象,对应的外键字段:column="dept_id"
    		3.部门的类型:class="Dept"
    		-->
    		<many-to-one name="dept" column="dept_id" class="Dept"/>
    		
    	</class>
    </hibernate-mapping>
    

    Dept的映射:Dept.hbm.xml

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
    	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="b_oneToMany">
    	<class name="Dept" table="t_dept">
    		<id name="depId">
    			<generator class="native"/>
    		</id>
    		<property name="deptName" length="20"/>
    
    		<!--
    		一对多关联映射配置(通过部门管理到员工)
    		1.指定映射的集合属性:name="emps"
    		2.集合属性对应的集合表:table="t_employee"
    		3.集合表的外键字段:<key column="deptId"/>
    		4.集合元素的类型:<one-to-many class="Employee"/>
    		-->
    		<set name="emps" table="t_employee">
                <key column="dept_id"/>
                <one-to-many class="Employee"/>
            </set>
    		
    	</class>
    </hibernate-mapping>
    

    测试类,分别从一的一方(Dept)来操作,使可以同时更新Employee;也测试从多的一方(Employee)来操作,使可以同时更新Dept

    package b_oneToMany;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    import org.junit.Test;
    
    /**
     * 测试类,分别从两个对象来维护表
     */
    public class App {
        private static SessionFactory sf;
        static {
            sf = new Configuration().configure().buildSessionFactory();
        }
        //保存,部门方维护(一的一方操作)
        @Test
        public void save(){
            Session session = sf.openSession();
            session.beginTransaction();
            //部门对象
            Dept dept = new Dept();
            dept.setDeptName("应用开发部");
            //员工对象
            Employee emp_zs = new Employee();
            emp_zs.setEmpName("张三");
            Employee emp_ls = new Employee();
            emp_ls.setEmpName("李四");
    
            //处理关系
            dept.getEmps().add(emp_zs);
            dept.getEmps().add(emp_ls);
    
            session.save(emp_zs);
            session.save(emp_ls);
            session.save(dept);//保存部门,部门下所有的员工
            session.getTransaction().commit();
            session.close();
    
        }
    
        //【推荐】保存,员工方维护(多的一方操作)
        @Test
        public void save2(){
            Session session = sf.openSession();
            session.beginTransaction();
            //部门对象
            Dept dept = new Dept();
            dept.setDeptName("人事部");
            //员工对象
            Employee emp_zs = new Employee();
            emp_zs.setEmpName("张三");
            Employee emp_ls = new Employee();
            emp_ls.setEmpName("李四");
    
            //处理关系
            emp_zs.setDept(dept);
            emp_ls.setDept(dept);
    
    
            session.save(dept);
            session.save(emp_zs);
            session.save(emp_ls);
            session.getTransaction().commit();
            session.close();
    
        }
    }
    
    

    数据的获取:

    package b_oneToMany;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    import org.junit.Test;
    
    /**
     * 获取数据
     */
    public class appGet {
        private static SessionFactory sf;
        static {
            sf = new Configuration().configure().buildSessionFactory();
        }
        //保存,部门方维护(一的一方操作)
        @Test
        public void get(){
            Session session = sf.openSession();
            session.beginTransaction();
    
            //通过部门方,获取另外一方
            Dept dept = (Dept)session.get(Dept.class, 1);
            System.out.println(dept.getDeptName());
            System.out.println(dept.getEmps());
    
    //        //通过员工方,获取另一方
            Employee emp = (Employee) session.get(Employee.class, 1);
            System.out.println(emp.getEmpName());
            System.out.println(emp.getDept().getDeptName());
    
            session.getTransaction().commit();
            session.close();
    
        }
    }
    
    

    总结:
    在一对多与多对一的关联关系中,保存数据最好的是通过多的一方来维护关系,这样可以减少update语句的生成,从而提高Hibernate的执行效率!

    配置一对多与多对一,这种叫:双向关联
    只配置一对多,叫单项一对多
    只配置多对一,叫单项多对一。

    注意:
    配置了哪一方,哪一方才有维护关联关系的权限!

    3.inverse/cascade

    3.1.Inverse属性

    Inverse属性是在维护表之间的关联关系的时候起作用的,表示控制权是否转移,在一的一方起作用。比如在一对多的两张表中,如果更改了一个表中的主键或者删除了一个数据,那么另外一张表该如何处理呢。

    Inverse的英文意思就是翻转
    Inverse=false:不转账,当前方有控制权
    Inverse=true:控制反转,当前方没有控制权

    Inverse维护表之间的关联关系,主要从以下几个方面来维护:
    1.保存数据
    有影响。如果设置控制反转,即Inverse=true,然后通过部门方(一的一方)维护关联关系。在保存部门的时候,同时保存员工,数据会保存,但是关联关系不会维护,即员工表中的部门外键不会更新,为NULL。

    2.获取数据
    没有影响。

    3.解除关联关系
    有影响
    Inverse=false:可以解除关联
    Inverse=true:当前方(部门)没有控制权,不能接触关联

    4.删除数据对关联关系的影响
    有影响
    Inverse=false:有控制权,可以删除。先清空外键作用,再删除数据
    Inverse=true:没有控制权,如果删除的记录有被外键引用,会报错,违反主键引用约束!如果删除的记录没有被引用,可以直接删除。

    3.2.CasCade属性

    CasCade表示级联操作,可以设置到一的一方或多的一方。
    CasCade的参数:
    none:不级联操作,默认值
    save-update:级联保存或更新
    delete:级联删除
    save-update,delete.级联保存,更新,删除
    all :同上,级联保存,更新,删除

    4.多对多映射

    4.1.多对多映射配置

    需求:项目(Project)与开发人员(Developer)
    一个项目,有多个开发人员
    一个开发人员,参与多个项目!--多对多

    例子:
    项目1:电商系统,参与的开发人员:曹吉,王春;
    项目2:OA系统,王春,老张
    例子中有两个项目,开发人员王春同时参与了两个项目。
    这个例子中,项目与开发人员就是多对多的关系。

    数据库设计:

    数据库设计如上,分析:
    DB设计:数据库有项目表(t_project),包含id,项目名,日期字段。员工表(t_developer),包含id,员工姓名。这两个表是多对多的关系,一般会有个中间表(t_relation),分别填写两张表的外键,使其联系起来。中间表跟其他两个表之间的关系是一对多的关系

    JavaBean:两个对象,分别是项目和开发者。项目中要有开发者的集合对象。开发者中也要有项目的集合对象。

    映射:映射的配置要注意一些关键点

    代码实现
    首先是JavaBean对象,有两个,项目(Project)和开发者员工(Developer)

    /**
     * 项目
     */
    public class Project {
        private int prj_id;
        private String prj_name;
        //项目下的多个员工
        private Set<Developer> developers = new HashSet<Developer>();
        //省略get和set
        }
    
    /**
     * 开发人员
     */
    public class Developer {
        private int d_id;
        private String d_name;
        //开发者参数的多个项目
        private Set<Project> projects = new HashSet<Project>();
            //省略get和set
        }
    

    然后是映射的配置,因为有两个JavaBean对象,所有也需要有两个映射文件
    Project.hbm.xml

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
    	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="c_many2many">
    	<class name="Project" table="t_project">
    		<id name="prj_id">
    			<generator class="native"/>
    		</id>
    		<property name="prj_name" length="20"/>
    
            <!--
              多对多映射:
              1.映射的集合属性:name="developers"
              2.集合属性,对应的中间表:table="t_relation"
              3.在中间表中的外键字段:<key column="prjId"/>
              4.3中的外键字段对应的中间表字段:column="did"
              5.集合属性元素的类型:class="Developer"
            -->
            <set name="developers" table="t_relation">
                <key column="prjId"/>
                <many-to-many column="did" class="Developer"/>
            </set>
    
    	</class>
    </hibernate-mapping>
    

    Developer.hbm.xml

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
    	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="c_many2many">
    	<class name="Developer" table="t_developer">
    		<id name="d_id">
    			<generator class="native"/>
    		</id>
    		<property name="d_name" length="20"/>
    
    		<!--
              多对多映射:员工方
              name:指定映射的集合属性
              table:集合属性对应的中间表
              key:指定中间表的外键字段(引用当前表t_developer主键的外键字段)
              many-to-many:
                column:指定外键字段对应的项目字段
                class:集合元素的类型
            -->
    		<set name="projects" table="t_relation">
    			<key column="did"/>
    			<many-to-many column="prjId" class="Project"/>
    		</set>
    
    
    	</class>
    </hibernate-mapping>
    

    测试类:

    package c_many2many;
    
    import b_oneToMany.Dept;
    import b_oneToMany.Employee;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    import org.junit.Test;
    
    /**
     * 测试类
     */
    public class App {
        private static SessionFactory sf;
        static {
            sf = new Configuration().configure().buildSessionFactory();
        }
    
        //多对多数据保存
        @Test
        public void save(){
            Session session = sf.openSession();
            session.beginTransaction();
            /*
              模拟数据:
              电商系统(曹吉,王春)
              OA系统(王春,老张)
            */
            //创建项目对象
            Project prj_ds = new Project();
            prj_ds.setPrj_name("电商系统");
            Project prj_oa = new Project();
            prj_oa.setPrj_name("OA系统");
    
            //创建开发者对象
            Developer dev_cj = new Developer();
            dev_cj.setD_name("曹吉");
            Developer dev_wc = new Developer();
            dev_wc.setD_name("王春");
            Developer dev_lz = new Developer();
            dev_lz.setD_name("老张");
    
            //关系【项目方操作】
            prj_ds.getDevelopers().add(dev_cj);
            prj_ds.getDevelopers().add(dev_wc);//电商系统:曹吉,王春
    
            prj_oa.getDevelopers().add(dev_wc);
            prj_oa.getDevelopers().add(dev_lz);//OA系统:王春,老张
    
    //        //关系【开发者方】
    //        //注意可以在项目方维护员工方,也可以通过员工方维护项目方,不用在两方同时维护,只需要维护一方就可以了
    //        dev_cj.getProjects().add(prj_ds);
    //        dev_wc.getProjects().add(prj_ds);
    //        dev_wc.getProjects().add(prj_oa);
    //        dev_lz.getProjects().add(prj_oa);
    
    
            //保存
            session.save(prj_ds);
            session.save(prj_oa);
    
            //使用级联映射保存,在项目方维护时,员工方就不需要再写保存
            //级联必须设置:cascade="save-update"
    //        session.save(dev_cj);
    //        session.save(dev_wc);
    //        session.save(dev_lz);
    
    
            session.getTransaction().commit();
            session.close();
        }
    
        @Test
        public void bak(){
            Session session = sf.openSession();
            session.beginTransaction();
    
            session.getTransaction().commit();
            session.close();
        }
    }
    
    

    4.2.多对多关联关系的维护

    关联关系一般只需要在一方进行维护,不用在两方都设置。
    设置Inverse属性,在多对多中维护关联关系的影响?
    其实多对多中和一对多和多对一的功作用是相同的。
    1.保存数据
    有影响
    Inverse=false,有控制权,可以维护关联关系,保存数据的时候会把对象关系插入中间表
    Inverse=true 没有控制权,不会往中间表插入数据

    2.获取数据
    无影响

    3.解除关系
    有影响:
    Inverse=false:有控制权,解除关系就是删除中间表的数据
    Inverse=true:没有控制权,不能解除关系

    4.删除数据
    有影响:
    Inverse=false:有控制权,先删除中间表数据,再删除自身
    Inverse=true:没有控制权,如果删除的数据有被引用,会报错!否则,才可以删除

  • 相关阅读:
    csrf攻击 xss区别
    同源策略
    JavaScript中数组的排序——sort()
    箭头函数
    bind(),call(),apply()
    异步操作
    slice(), splice(),split(),indexOf(),join(),replace()
    04-Linux系统编程-第01天(文件IO、阻塞非阻塞)
    03-Linux命令基础-第03天(makefile、静态库、动态库、gdb调试工具)
    02-Linux命令基础-第02天(压缩包管理、服务器搭建与使用、vim)
  • 原文地址:https://www.cnblogs.com/cenyu/p/6260162.html
Copyright © 2020-2023  润新知