hibernate——继承关系以及三个subclass标签的区别

。 inverse = ?
          inverse=false(default)
                      用于单向one-to-many关联
                      parent.getChildren().add(child) // insert child
                      parent.getChildren().delete(child) // delete child
           inverse=true
                      用于双向one-to-many关联
                      child.setParent(parent); session.save(child) // insert child
                       session.delete(child)
            在分层结构的体系中
             parentDao, childDao对于CRUD的封装导致往往直接通过session接口持久化对象,而很少通过关联对象可达性

二。 one-to-many关系
                单向关系还是双向关系?
                     parent.getChildren().add(child)对集合的触及操作会导致lazy的集合初始化,在没有对集合配置二级缓存的情况下,应避免此类操作
                   select * from child where parent_id = xxx;
          性能口诀:
                  1. 一般情况下避免使用单向关联,尽量使用双向关联
                  2. 使用双向关联,inverse=“true”
                  3. 在分层结构中通过DAO接口用session直接持久化对象,避免通过关联关系进行可达性持久化

三。many-to-one关系
         单向many-to-one表达了外键存储方
         灵活运用many-to-one可以避免一些不必要的性能问题
         many-to-one表达的含义是:0..n : 1,many可以是0,可以是1,也可以是n,也就是说many-to-one可以表达一对多,一对一,多对一关系
          因此可以配置双向many-to-one关系,例如:
                1.   一桌四人打麻将,麻将席位和打麻将的人是什么关系?是双向many-to-one的关系

四。one-to-one
            通过主键进行关联
            相当于把大表拆分为多个小表
            例如把大字段单独拆分出来,以提高数据库操作的性能
            Hibernate的one-to-one似乎无法lazy,必须通过bytecode enhancement

五。集合List/Bag/Set
            one-to-many
               1.    List需要维护index column,不能被用于双向关联,必须inverse=“false”,被谨慎的使用在某些稀有的场合

               2.      Bag/Set语义上没有区别
               3.       我个人比较喜欢使用Bag
           many-to-many
               1.      Bag和Set语义有区别
               2。   建议使用Set

六。集合的过滤
             1. children = session.createFilter(parent.getChildren(), “where this.age > 5 and   this.age < 10”).list()
         针对一对多关联当中的集合元素非常庞大的情况,特别适合于庞大集合的分页:
                   session.createFilter(parent.getChildren(),“”).setFirstResult(0).setMaxResults(10).list();
在hibernate 中用 super.getSession().createFilter( , )

七。继承关系当中的隐式多态
           HQL: from Object
             1.     把所有数据库表全部查询出来
              2.     polymorphism=“implicit”(default)将当前对象,和对象所有继承子类全部一次性取出
              3.      polymorphism=“explicit”,只取出当前查询对象

八。Hibernate二级缓存
              著名的n+1问题:from Child,然后在页面上面显示每个子类的父类信息,就会导致n条对parent表的查询:
                   select * from parent where id = ?
                   .......................
                   select * from parent where id = ?
              解决方案
                        1.      eager fetch
                         2.      二级缓存

九。inverse和二级缓存的关系
            当使用集合缓存的情况下:
                 1.     inverse=“false”,通过parent.getChildren()来操作,Hibernate维护集合缓存
                  2.    inverse=“true”,直接对child进行操作,未能维护集合缓存!导致缓存脏数据
                  3.    双向关联,inverse=“true”的情况下应避免使用集合缓存

十。Hibernate二级缓存是提升web应用性能的法宝
              OLTP类型的web应用,由于应用服务器端可以进行群集水平扩展,最终的系统瓶颈总是逃不开数据库访问;

           哪个框架能够最大限度减少数据库访问,降低数据库访问压力, 哪个框架提供的性能就更高;针对数据库的缓存策略:
                    1.        对象缓存:细颗粒度,针对表的记录级别,透明化访问,在不改变程序代码的情况下可以极大提升web应用的性能。对象缓存是ORM的制胜法宝。
                    2.       对象缓存的优劣取决于框架实现的水平,Hibernate是目前已知对象缓存最强大的开源ORM
                    3.        查询缓存:粗颗粒度,针对查询结果集,应用于数据实时化要求不高的场合

十一。应用场合决定了系统架构
一、是否需要ORM
Hibernate or iBATIS?
二、采用ORM决定了数据库设计
            Hibernate:
                    倾向于细颗粒度的设计,面向对象,将大表拆分为多个关联关系的小表,消除冗余column,通过二级缓存提升性能(DBA比较忌讳关联关系的出现,但是 ORM的缓存将突破关联关系的性能瓶颈);Hibernate的性能瓶颈不在于关联关系,而在于大表的操作
            iBATIS:
                    倾向于粗颗粒度设计,面向关系,尽量把表合并,通过表column冗余,消除关联关系。无有效缓存手段。iBATIS的性能瓶颈不在于大表操作,而在于关联关系。

总结:
     性能口诀
               1、使用双向一对多关联,不使用单向一对多
               2、灵活使用单向多对一关联
               3、不用一对一,用多对一取代
               4、配置对象缓存,不使用集合缓存
               5、一对多集合使用Bag,多对多集合使用Set
               6、继承类使用显式多态
               7、表字段要少,表关联不要怕多,有二级缓存撑腰

最近开始留意项目中的Hibernate的性能问题,希望可以抽出时间学习一下hiberante的性能优化。主要是对数据库连接池技术、hibernate二级缓存、hibernate的配置优化等问题进行学习!


1.关联关系:
普通的关联关系:是不包括一个连接表,也就是中间表如:
create table Person(personId bigint not null primary key,addressId bigint not null)
create table Address(addressId bigint not null primary key)
也就是不会还有一个关系表如:
create table Person(personId bigint not null primary key)
create table Address(addressId bigint not null primary key)
create table PersonAddress(personId bigint not null,ddressId bigint not null primary key)


单向many-to-one关联是最常见的,而单向one-to-many是不常见的


2. inner join (内连接)
left (outer) join (左外连接)
right (outer) join (右外连接)
full join (全连接,并不常用)


3.小技巧:
统计结果数目:
(Integer)session.iterator("select count(*) from ..").next()).intValue();
根据一个集合大小来排序:
select user.id,user.name
from User as user.name
    left join user.messages msg
group by user.id,user.name
having count(msg)>=1

在处理大数据量时,会有大量的数据缓冲保存在 Session 的一级缓存中,这缓存大太时会严重显示性能,所以在使用 Hibernate 处理大数据量的,可以使用 session.clear() 或者 session. Evict(Object) 在处理过程中,清除全部的缓存或者清除某个对象。
2) 对大数据量查询时,慎用 list() 或者 iterator() 返回查询结果,
1. 使用 List() 返回结果时, Hibernate 会所有查询结果初始化为持久化对象,结果集较大时,会占用很多的处理时间。
2. 而使用 iterator() 返回结果时,在每次调用 iterator.next() 返回对象并使用对象时, Hibernate 才调用查询将对应的对象初始化,对于大数据量时,每调用一次查询都会花费较多的时间。当结果集较大,但是含有较大量相同的数据,或者结果集不是全部都会使用时,使用 iterator() 才有优势。
3. 对于大数据量,使用 qry.scroll() 可以得到较好的处理速度以及性能。而且直接对结果集向前向后滚动。
3) 对于关联操作, Hibernate 虽然可以表达复杂的数据关系,但请慎用,使数据关系较为简单时会得到较好的效率,特别是较深层次的关联时,性能会很差。
4) 对含有关联的 PO (持久化对象)时,若 default-cascade="all" 或者 “save-update” ,新增 PO 时,请注意对 PO 中的集合的赋值操作,因为有可能使得多执行一次 update 操作。
5) 在一对多、多对一的关系中,使用延迟加载机制,会使不少的对象在使用时 才 会初始化,这样可使得节省内存空间以及减少数据库的负荷,而且若 PO 中的集合没有被使用时,就可减少互数据库的交互从而减少处理时间。

6) 对于大数据量新增、修改、删除操作或者是对大数据量的查询,与数据库的交互次数是决定处理时间的最重要因素,减少交互的次数是提升效率的最好途径,所以在开发过程中,请将 show_sql 设置为 true ,深入了解 Hibernate 的处理过程,尝试不同的方式,可以使得效率提升。

7) Hibernate 是以 JDBC 为基础,但是 Hibernate 是对 JDBC 的优化,其中使用 Hibernate 的缓冲机制会使性能提升,如使用二级缓存以及查询缓存,若命中率较高明,性能会是到大幅提升。
8) Hibernate 可以通过设置 hibernate.jdbc.fetch_size , hibernate.jdbc.batch_size 等属性,对 Hibernate 进行优化。 9) 不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发 insert 数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次 Insert 操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。因此,对于并发 Insert 要求较高的系统,推荐采用 uuid.hex 作为主键生成机制。 10) Dynamic Update 如果选定,则生成 Update SQL 时不包含未发生变动的字段属性,这样可以在一定程度上提升 SQL 执行效能 . Dynamic Insert 如果选定,则生成 Insert SQL 时不包含未发生变动的字段属性,这样可以在一定程度上提升 SQL 执行效能 11) 在编写代码的时候请,对将 POJO 的 getter/setter 方法设定为 public ,如果设定为 private , Hibernate 将无法对属性的存取进行优化,只能转而采用传统的反射机制进行操作,这将导致大量的性能开销(特别是在 1.4 之前的 Sun JDK 版本以及 IBM JDK 中,反射所带来的系统开销相当可观)。 12) 在 one-to-many 关系中,将 many 一方设为主动方( inverse=false )将有助性能的改善 13) 由于多对多关联的性能不佳(由于引入了中间表,一次读取操作需要反复数次查询),因此在设计中应该避免大量使用 .

14) Hibernate 支持两种锁机制:即通常所说的“悲观锁( Pessimistic Locking )”和“乐观锁( Optimistic Locking )”。 悲观锁带来 数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。乐观锁机制在一定程度上解决了这个问题.乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统整体性能表现。



---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Java类中有继承关系,相应的在hibernate中,也有继承关系,子类反应到数据库中,就有多种实现形式了,子类和父类可以映射到同一张表中,子类也可以单独映射成一张表,但是用不同的标签实现,子类表和父类表的关系也不同。在映射文件中,有三个标签可以实现继承关系,分别是:subclass、joined-subclass、union-subclass,先陈述一下这三个标签的区别:

subclass标签就是为子类嵌入父类的表中而设计的,而且要指定鉴别器,即用subclass一定要有判断类型的一个列,鉴别器指定记录属于哪个类型。但是subclass也提供了为子类设置单独的表的功能,即join标签。但是不管是内嵌表还是外部表,都得指定鉴别器。

joined-subclass标签,提供的功能是只能为子类设定外部表,而且没有鉴别器,即子类一张表,父类一张表,子类以父类的主键为外键。父类对应的表中,没有判断类型的列。

注意:这两个标签不能混用,若是只想要内嵌表,或者是既想要有内嵌表,又想要外部表,又或者是只想要外部表,那么只能使用subclass标签,需要注意的是,不论哪种方式,都要指定鉴别器,即父类对应的表中,一定有一个判断类型的列;而若是只想要外部表,又不想在父类对应的表中,要那个判断类型的列,那么只能使用join-subclass

union-subclass是将父类中的属性,添加到子类对应的表中去了。包括父类中的外键。union-class和前两个的区别就在于外键的不同,前两个标签,如果子类有单独对应的表的话,这个表的外键是其父类中的主键,而使用union-class,子类对应的表中的外键则和父类的外键是一样的,因为子类把父类继承的属性,也加到了自己的表当中。这样子类和父类的地位就相当了。不过这不是一种好的理解方式。如果是这样的话,那么就可以把父类设为抽象的类,并且在映射文件中,把父类设置为abstract="true",那么就不会再数据库中生成父类对应的表了,父类就只起到一个抽象的作用了。

下面列举员工和部门的例子说明,假设员工类还有两个子类:一个是技术人员,一个是销售人员:

复制代码
public class Employee {
    private int id;
    private String name;
    private Department depart;

        ……//set/get方法
}
复制代码
public class Skiller extends Employee {
    private String skill;

        ……//set/get方法
}
public class Saler extends Employee {
    private String sale;
    private int age;

        ……//set/get方法
}
复制代码
public class Department {
    private int id;
    private String name;
    private Set<Employee> emps;

        ……//set/get方法
}
复制代码

 

部门类的映射文件:

复制代码
<class name="Department">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
        
        <set name="emps" inverse="true">
            <key column="depart_id"/>
            <one-to-many class="Employee"/>
        </set>
</class>
复制代码



 (1)下面关键是员工类的映射文件,我们先来看使用subclass标签的情况:

复制代码
<class name="Employee" discriminator-value="0">
        <id name="id">
            <generator class="native"/>
        </id>
        <discriminator column="type" type="int"/><!-- 指定了鉴别器 -->
        <property name="name"/>
        <many-to-one name="depart" column="depart_id"/>
        
        <subclass name="Skiller" discriminator-value="1">
            <property name="skill"/>
        </subclass>
        <subclass name="Saler" discriminator-value="2">
            <property name="sale"/>
        </subclass>
</class>
复制代码

这种情况就是所谓的将整个继承树映射到同一个表当中,即子类的信息,全部映射到了父类对应的表中。注意,一定要指定鉴别器,它的作用是在父类的映射表中,添加了一个type的列,用来鉴别员工是属于哪个子类,并且在每个类的标签中要指定鉴别器值,如上例中用0表示是普通的员工,用1表示技术人员,用2表示销售人员。我们可以看看创建表的ddl语句:

复制代码
CREATE TABLE `employee` (
  `id` int(11) NOT NULL,
  `type` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `depart_id` int(11) DEFAULT NULL,
  `skill` varchar(255) DEFAULT NULL,
  `sale` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK4AFD4ACE972E0614` (`depart_id`),
  CONSTRAINT `FK4AFD4ACE972E0614` FOREIGN KEY (`depart_id`) REFERENCES `department` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk
复制代码

用这种方式有缺点有优点,缺点是在表中,会有很多的空值,假如又有了新的子类,那么就要修改表的结构,并且相应的空值会更多;优点是所有信息整合到一张表中,查询的效率高。

下面再来看subclass标签的第二种用法:

复制代码
<class name="Employee" discriminator-value="0"><!--  discriminator-value="0" -->
        <id name="id">
            <generator class="native"/>
        </id>
        <discriminator column="type" type="int"/><!-- 指定了鉴别器 -->
        <property name="name"/>
        <many-to-one name="depart" column="depart_id"/>
        
        <subclass name="Skiller" discriminator-value="1">
            <property name="skill"/>
        </subclass>
        
        <subclass name="Saler" discriminator-value="2">
            <join table="saler">
                <key column="emp_id"/>
                <property name="sale"/>
            </join>
        </subclass>
</class>
复制代码

改变的地方,主要是Saler类的标签,在subclass标签中用join标签,这样就为Saler子类单独映射了一个saler表,其主键为父类对应的表的主键。而Skiller类还是和父类映射到了一张表中。来看看Saler创建表的ddl语句:

复制代码
CREATE TABLE `saler` (
  `emp_id` int(11) NOT NULL,
  `sale` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`emp_id`),
  KEY `FK682490B3F739201` (`emp_id`),
  CONSTRAINT `FK682490B3F739201` FOREIGN KEY (`emp_id`) REFERENCES `employee` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk
复制代码

需要注意的一个地方是这个表的外键,外键和主键是一样的,都是父类表的主键。
 

(2)joined-subclass的使用

复制代码
<class name="Employee"><!--  discriminator-value="0" -->
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
        <many-to-one name="depart" column="depart_id"/>

        <joined-subclass name="Skiller" table="skiller">
            <key column="emp_id"/>
            <property name="skill"/>
        </joined-subclass>
        <joined-subclass name="Saler" table="saler">
            <key column="emp_id"/>
            <property name="sale"/>
        </joined-subclass>
</class>
复制代码

用joined-subclass只能创建为子类单独创建表,子类对应的表的主键和外键都是其父类的主键。注意,joined-subclass不能和subclass混合使用。


(3)union-subclass的使用

复制代码
<class name="Employee">
        <id name="id">
            <generator class="hilo"/>
        </id>
        <property name="name"/>
        <many-to-one name="depart" column="depart_id"/>

        <union-subclass name="Skiller" table="skiller">
            <property name="skill"/>
        </union-subclass>
        
        <union-subclass name="Saler" table="saler">
            <property name="age"/>
            <property name="sale"/>
        </union-subclass>
    </class>
复制代码

注意,这里主键的生成方式就不能是自增的了,这里用的hilo方式,可以为子类的表分配和父类的表不同的主键。这样每个子类生成了一个表,并且结合了父类中的属性。生成的表的ddl语句为:

复制代码
CREATE TABLE `skiller` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `depart_id` int(11) DEFAULT NULL,
  `skill` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK4AFD4ACE972E06147ffd86be` (`depart_id`),
  CONSTRAINT `FK4AFD4ACE972E06147ffd86be` FOREIGN KEY (`depart_id`) REFERENCES `department` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk
复制代码

注意,这里的外键是部门类对应的表的主键,这是和上面两个标签的区别之处。若是将父类设置为抽象类,并且在class标签中设置abstract="true",那么父类就不会生成对应的表了。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值