Hibernate学习笔记(2)

1.   映射关联关系

1.1映射的种类

以客户(Customer)和订单(Order)为例子。

客户       订单

1-1         一对一单向

1-1         一对一双向

1-n         一对多单向

n-1         多对一单向

1-n         一对多双向[和多对一双向一样]

n-m        多对多单向[可以分解为一对多]

n-m        多对多双向

1.2映射多对一[单向]关联关系

属于n-1单向模式。即多个订单订单单向关联客户。建表时,订单表要引用客户表的id作为外键。

 

理解例子中的多对一单向关联关系:

多个订单[多方]对应一个客户[一方],可以从订单自身获取该订单的客户信息(即订单知道自己属于哪个客户),但从客户上获取不到自身的订单信息(即客户不知道自己有哪些订单)。

 

实体类:

publicclass Customer {

//客户[一方],多对一单向,找不到订单

      private Integer id; //ID

      private String name;//客户名字

}

publicclass Order {

//订单[多方],多对一单向,可以找到客户

      private Integer id; //ID

      private String oderNum;//订单编号

      private Customer customer;//客户

}

映射文件:

一方:

      <class name="Customer"table="CUSTOMERS">

           <id name="id"column="ID" type="integer">

                 <generator class="native"/>

           </id>

           <property name="name"column="NAME" type="string"/>

      </class>

多方:

      <class name="Order"table="ORDERS">

           <id name="id"column="ID" type="integer">

                 <generator class="native"/>

           </id>

           <property name="orderNum"column="ORDERNUM" type="string"/>

           <!-- 多对一映射

           name:多方域对象customer属性名

           class:多方域对象属性customer的类型

           column:多方域对象对应的表orders的外键 -->

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

      </class>

保存客户和订单:

           Customerc1 = new Customer("张三");

           Ordero1 = new Order("N001");

           Ordero2 = new Order("N002");

           //设置多对一单向关联

           o1.setCustomer(c1);

           o2.setCustomer(c1);

           /*

            * 先保存订单再保存客户,

            * 结果:执行5条sql语句,3条insert,2条update

           session.save(o1);

           session.save(o2);

           session.save(c1);*/

           /*  先保存客户再保存订单,

            * 结果:只执行3条insert语句

            * 结论:保存多对一单向关联对象时,先保存一方再保存多方

            * 效率会高点。

            */

           session.save(c1);

           session.save(o1);

           session.save(o2);

查询订单:

/*

            * 当订单单向关联客户时,查询订单时

            * 会将订单关联的客户一起查询出来

            * 即o1.getCustomer()不为空

            */

           Ordero1 =(Order) session.get(Order.class, 1); //查询id为1的订单

保存订单时级联保存关联的客户:

           Customerc1 = new Customer("张三");

           Ordero1 = new Order("N001");

           Ordero2 = new Order("N002");

           //设置多对一单向关联

           o1.setCustomer(c1);

           o2.setCustomer(c1);

                 /*

            *保存订单时级联保存客户

            * 需在Order.hbm.xml中的many-to-one节点配置

            * cascade="save-update"属性,如果没有设置该属性

            * 又没有手动执行session.save(c1)则出错(TransientObjectException)。因为订单

            * 和客户是多对一单向关联关系,订单表引用了客户表的

            * id作为外键,没有执行session.save(c1)即客户表没有记录

            * 此时订单是保存不了的。

            */

session.save(o1);

           session.save(o2);

<!-- 多对一映射

           name:多方域对象customer属性名

           class:多方域对象属性customer的类型

           column:多方域对象对应的表orders的外键

           cascade:多方域对象所关联的一切对象都会同步保存和更新-->

           <many-to-one name="customer"class="Customer" column="CID" cascade="save-update"/>

* 小结:

 * 1.多对一单向关联时,保存时先保存一方再保存多方,效率较高;

 * 2.查询多方时,会自动将其关联的一方查询出来;

 * 3.当在多方的配置文件中配置了cascade="save-update"属性时,

 * 保存或更新多方时,会级联保存或更新其关联的一方。

1.3映射一对多[双向]关联关系

没有多对一双向的说法,因为它和一对多双向是同一个意思,但是有多对一单向。

 

理解例子中的一对多双向关联关系:

一个客户[一方]可以对应多个订单[多方],每个订单对应一个客户,即可以从客户自身获取其订单信息(即客户知道自己有哪些订单),也可以从订单自身获取该订单的客户信息(即订单知道自己属于哪个客户)。

 

实体类:

publicclass Customer {

//客户[一方],一对多双向,可以找到订单

      private Integer id; //ID

      private String name;//客户名字

      private Set<Order> orders = newHashSet<Order>();//多个订单

}

publicclass Order {

//订单[多方],一对多双向,可以找到客户

      private Integer id; //ID

      private String orderNum;//订单编号

      private Customer customer;//客户

}

映射文件:

多方:

<class name="Order" table="ORDERS">

           <id name="id"column="ID" type="integer">

                 <generator class="native"/>

           </id>

           <property name="orderNum"column="ORDERNUM" type="string"/>

           <!-- 多对一映射

           name:多方域对象customer属性名

           class:多方域对象属性customer的类型

           column:多方域对象对应的表orders的外键

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

 

一方:

<class name="Customer" table="CUSTOMERS">

           <id name="id"column="ID" type="integer">

                 <generator class="native"/>

           </id>

           <property name="name"column="NAME" type="string"/>

           <!-- 一对多双向[一方]

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

           name:一方的关联属性orders,是一个set集合

           table:多方域对象对应的表名

           column:多方域对象对应表的外键

           class:多方域对象的类名 -->

           <set name="orders"table="ORDERS" >

                 <key column="CID"/>

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

           </set>

      </class>

 

保存客户和订单:

           Customerc1 = new Customer("张三");

           Ordero1 = new Order("N001");

           Ordero2 = new Order("N002");

           //设置一对多双向关联

           c1.getOrders().add(o1);

           c1.getOrders().add(o2);

           o1.setCustomer(c1);

           o2.setCustomer(c1);

           /*先保存订单再保存客户

           7条sql,3条insert,4条update

           session.save(o1);

           session.save(o2);

           session.save(c1);

            */

           /*先保存客户再保存订单

           5条sql,3条insert,2条update

            效率较高,优先选择保存一方

           */

           session.save(c1);

           session.save(o1);

           session.save(o2);

 

查询客户或订单:

/*

           查询id为1的客户,会将其级联的订单查出来

           Customerc1 =(Customer) session.get(Customer.class, 1);

           Set<Order>orders = c1.getOrders();

           for(Order order : orders) {

                 System.out.println(order.getOrderNum());

           }*/

           /*

            * 查询id为1的订单,会将其级联的客户查出来

            */

           Ordero1 =(Order) session.get(Order.class, 1);

           System.out.println(o1.getCustomer().getName());

级联保存客户或订单:

           Customerc1 = new Customer("张三");

           Ordero1 = new Order("N001");

           Ordero2 = new Order("N002");

           //设置一对多双向关联

           c1.getOrders().add(o1);

           c1.getOrders().add(o2);

           o1.setCustomer(c1);

           o2.setCustomer(c1);

 

           /*

            *保存订单时级联保存客户

            * 需在Order.hbm.xml中的many-to-one节点配置

            * cascade="save-update"属性

           session.save(o1);

           session.save(o2);

           */

           /*

            *保存客户时级联保存订单

            * 需在Customer.hbm.xml中的set节点配置

            * cascade="save-update"属性

            */

           session.save(c1);

更新客户和订单:

有三种方式:

1.手工更新session.update(c1)和sesison.update(o1)

2.session.update(c1)要在Order.hbm.xml中配置属性cascade="save-update"

3.不使用任何session.update()语句,直接更新,原理是原因session一级缓存有快照的功能(快照只适合于更新操作).

           Customerc1 = (Customer) session.get(Customer.class, 1); //查询id为1的客户

           Ordero4 = (Order) session.get(Order.class, 4);

           c1.getOrders().add(o4);//客户变化了

           o4.setCustomer(c1);//订单变化了

           /*方式一:手工更新,同时调用update方法

           session.update(c1);

           session.update(o4);*/

           //方式二:级联更新,调用update,同时传入一方对象,并在该方配置文件设置cascade="save-update"属性

//        session.update(c1);

//        session.update(o4);

//        方式三:使用session的快照功能,不调用任何update方法

           t.commit();

           session.close();

当在双向关联时,应该在一方使用inverse=true属性,由多方来负责管理维护双向关系,这样可以减少update语句,如果是多方管理话,有且只有在多方发生引用变化时,hibernate才负责做update操作;如果是多方管理话,多方没有发生引用变化时,即使是一方发生变化,那么hibernate依然不负责update操作;如果单向操作,不要使用inverse属性。

* 小结:

 * 1.一对多双向关联时,保存时先保存一方再保存多方,效率较高;

 * 2.不管查询哪方都会将其级联的一方查询出来,因为是双向关联;

 * 3.当双方都配置了cascade="save-update"属性时,可以级联保存(即保存一方时另一方自动保存)

 *4.session有快照功能,当关联发生变化时,可自动更新数据库数据,与cascade="save-update"属性是否设置无关

 */

1.4映射一对一[双向]关联关系

实体类:

publicclass Person {

//人,一对一双向一方,可以找到身份证

      private Integer id;

      private String name;

      private Card card;

}

 

publicclass Card {

//身份证,一对一双向一方,可以找到人

      private Integer id;

      private String cardNO; //身份证号码

      private String location;

      private Person person;

}

映射文件:

有外键一方,即Person.hbm.xml:

<!-- 在一对一双向中,有外键一方用many-to-one标签加上unique="true"属性表示name:Person类中card属性名

                 class:card属性的类型

                 column:Person类所对应的表的外键

                 unique:对外键加上唯一约束,true表示唯一,false表示不唯一

            -->

           <many-to-one name="card"class="Card"  column="CID"unique="true"/>

无外键一方,即Card.hbm.xml:

           <!--  在一对一外键双向关联中,无外健一方使用one-to-one标签

               name:Card类的person属性

               class:Card类的person属性类型

               property-ref:Card类在对方的关联属性,即card属性-->

           <one-to-one name="person"class="Person" property-ref="card"/>

1.5映射多对多[双向]关联关系

多对多双向关联可以拆分为多对一单向关联,同过一个中间表(middle),middle表有两个字段,如:tid,sid,分别引用老师表和学生表的id作为外键。而tid和sid有是联合主键。

此时在这种关联状态下,如果由双方管理关联关系,middle会出现主键冲突,所以只能由其中一方来管理。如让老师来管理,则在学生的映射文件中设置数据inverse=“true”。叫由对方管理关联关系。

 

实体类:

publicclass Student {

//学生,多对多双向的多方,可以找到老师

      private Integer id;

      private String name;

private Set<Teacher>teacherSet = newHashSet<Teacher>(); //多个老师

}

publicclass Teacher {

//老师,多对多双向的多方,可以找到学生

      private Integer id;

      private String name;

private Set<Student>studentSet = newHashSet<Student>();//多个学生

}

映射文件:

Student.hbm.xml:

           <!-- 在多对多双向关联中,多方使用set标签和many-to-many标签

                name:Student的集合属性

                table:中间表名

                key-column:中间表引用Student表的外键

                class:Student类对应的另一个多方

                many-to-many-column:中间表引用Teacher表的外键

           -->

           <set name="teacherSet"table="MIDDLES" inverse="true">

                 <key column="SID"/>

                 <many-to-many

                      class="Teacher" column="TID"/>

           </set>

Teacher.hbm.xml:

                      <!-- 在多对多双向关联中,多方使用set标签和many-to-many标签

                name:Teacher类的集合属性

                table:中间表

                key-column:中间表引用Teacher表的外健

                class:Teacher类对应的另一个多方

                many-to-many-column:中间表引用Student表的外健

                inverse="false"表示双向关联由Teacher来管理

           -->

           <set name="studentSet"

                 table="MIDDLES"

                 cascade="delete">

                 <key column="TID"/>

                 <many-to-many

                      class="Student" column="SID"/>

           </set>

 

1.6继承映射关系(垂直关系)

有三种方案:

A每个类对应[一]张表

比较节约空间,但要有区分字段。使用<subclass>配置,每个类都要加上区分值

discriminator-value=xxx。

B每个类对应[每]张表,每张表独立,无外健关联

使用<union-subclass>配置

C每个类对应[每]张表,子类对应的表通过外健关联(推荐)

使用<joined-subclass>配置

 

实体类:

publicclass Employee {

      private Integer id;

      private String name;

}

publicclass HourEmployee extends Employee {

      private Double rate;

}

publicclass SalaryEmployee extends Employee {

      private Double salary;

}

方案一:

配置文件:

<class name="Employee" table="EMPLOYEES"discriminator-value="E">

           <id name="id"column="ID" type="integer">

                 <generator class="native"/>

           </id>

           <!-- 区分字段,只能id后面 -->

           <discriminator  column="ETYPE"type="string"/>

           <property name="name"column="NAME" type="string"/>

           <!-- 配置子类 discriminator-value:区分字段的值 -->

           <subclass name="HourEmployee"discriminator-value="HE">

                 <property name="rate"column="RATE" type="double"/>

           </subclass>

           <!-- 配置子类 -->

           <subclass name="SalaryEmployee"discriminator-value="SE">

                 <property name="salary"column="SALARY" type="double"/>

           </subclass>

      </class>

           Employeee = new Employee("员工");

           HourEmployeehe = new HourEmployee("时工", 13.5D);

           SalaryEmployeese = new SalaryEmployee("月工", 5000D);

           session.save(e);

           session.save(he);

           session.save(se);

方案二:

配置文件:

<class name="Employee" table="EMPLOYEES">

           <id name="id"column="ID" type="integer">

                 <generator class="increment"/><!-- 第二种方案,id生成策略不能用native,因为要保持三张表id的连续性 -->

           </id>

           <property name="name"column="NAME" type="string"/>

           <union-subclass name="HourEmployee"table="HOUREMPLOYEES">

                 <property name="rate"column="RATE" type="double"/>

           </union-subclass>

           <union-subclass name="SalaryEmployee"table="SALARYEMPLOYEES">

                 <property name="salary"column="SALARY" type="double"/>

           </union-subclass>

      </class>

方案三:

配置文件:

<class name="Employee" table="EMPLOYEES">

           <id name="id"column="ID" type="integer">

                 <generator class="native"/>

           </id>

           <property name="name"column="NAME" type="string"/>

           <joined-subclass name="HourEmployee"table="HOUREMPLOYEES">

                 <key column="HID"/><!-- 主/外键 -->

                 <property name="rate"column="RATE" type="double"/>

           </joined-subclass>

           <joined-subclass name="SalaryEmployee"table="SALARYEMPLOYEES">

                 <key column="SID"/>    

                 <property name="salary"column="SALARY" type="double"/>

           </joined-subclass>

      </class>

1.7组件映射

publicclass Customer {

//一对多双向 一方

      private Integer id;

      private String name;

      private Address address; //客户地址,组件

      private Set<Order> orderSet = newHashSet<Order>();

}

Customer.hbm.xml文件:

           <!-- 组件 映射address属性-->

           <component name="address"class="Address">

                 <property name="province"column="PROVINCE" type="string"/>

                 <property name="city"column="CITY" type="string"/>

                 <property name="area"column="AREA" type="string"/>

           </component>

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值