Hibernate

一、优缺点、核心、工作原理

1)优缺点

优点:

(1)对象/关系数据库映射(Basic O/R Mapping)

(2)透明持久化(Persistent)

(3)事务Transaction(org.Hibernate.Transaction)

(4)它没有侵入性,即所谓的轻量级框架。

(5)移植性会很好。

(6)缓存机制。提供一级缓存和二级缓存。

(7)简洁的HQL编程。

缺点:

(1)Hibernate在批量数据处理的时候是有弱势。

(2)在批量数据处理的时候有弱势

2)核心

    从上图中,我们可以看出Hibernate六大核心接口,两个主要配置文件,以及他们直接的关系。Hibernate的所有内容都在这了。那我们从上到下简单的认识一下,每个接口进行一句话总结。

  1、Configuration接口:负责配置并启动Hibernate

  2、SessionFactory接口:负责初始化Hibernate

  3、Session接口:负责持久化对象的CRUD操作

  4、Transaction接口:负责事务

  5、Query接口和Criteria接口:负责执行各种数据库查询

  注意:Configuration实例是一个启动期间的对象,一旦SessionFactory创建完成它就被丢弃了。

SessionFactory是线程安全的吗?Session是线程安全的吗,两个线程能够共享同一个Session吗?

SessionFactory是线程安全的,可以被多个线程并发访问。SessionFactory一般只会在启动的时候构建,而且是单例的。

Session是一个轻量级非线程安全的对象(线程间不能共享session),它表示与数据库进行交互的一个工作单元。Session是由SessionFactory创建的,在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口。Session会延迟获取数据库连接(也就是在需要的时候才会获取)。为了避免创建太多的session,可以使用ThreadLocal来取得当前的session,无论你调用多少次getCurrentSession()方法,返回的都是同一个session。

3)工作原理

1.通过Configuration config = newConfiguration().configure();//读取并解析hibernate.cfg.xml配置文件
2.由hibernate.cfg.xml中的<mappingresource="com/xx/User.hbm.xml"/>读取并解析映射信息
3.通过SessionFactory sf = config.buildSessionFactory();//创建SessionFactory
4.Session session = sf.openSession();//打开Sesssion
5.Transaction tx = session.beginTransaction();//创建并启动事务Transation
6.persistent operate操作数据,持久化操作
7.tx.commit();//提交事务
8.关闭Session
9.关闭SesstionFactory

二、Session

1)实体对象的三种状态以及转换关系

临时状态:当new一个实体对象后,这个对象处于临时状态,即这个对象只是一个保存临时数据的内存区域,如果没有变量引用这个对象,则会被JVM的垃圾回收机制回收。这个对象所保存的数据与数据库没有任何关系,除非通过Session的save或者saveOrUpdate把临时对象与数据库关联,并把数据插入或者更新到数据库,这个对象才转换为持久对象。

持久状态:持久化对象的实例在数据库中有对应的记录,并拥有一个持久化标识。对持久化对象进行delete操作后,数据库中对应的记录将被删除,那么持久化对象与数据库记录不再存在对应关系,持久化对象变成

临时状态。持久化对象被修改变更后,不会马上同步到数据库,直到数据库事务提交。

游离状态:当Session进行了close、clear或者evict后,持久化对象虽然拥有持久化标识符和与数据库对应记录一致的值,但是因为会话已经消失,对象不在持久化管理之内,所以处于游离状态(也叫脱管状态)。游离状态的对象与临时状态对象是十分相似的,只是它还含有持久化标识。

2)session类的方法

1、get/load/query()

①get/load和query()区别

get/load会优先在session缓存里寻找对象,如果找不到再去查询数据库,query()会直接查询数据库

②get与load区别

1、 如果没有找到符合条件的记录, get方法返回null,load方法抛出异常

2、get方法直接返回实体类对象, load方法返回实体类对象的代理

③Query接口的list方法和iterate方法有什么区别?

1、 list方法无法利用缓存,它对缓存只写不读; iterate方法可以充分利用缓存, 如果目标数据只读或者读取频繁, iterate可以减少性能开销

2、list方法不会引起N+1查询问题, 而iterate方法会引起N+1查询问题

2、flush/reflush()

flush 将session缓存里的数据同步到数据库,触发相应的sql语句。

以下情况会触发flush操作:

①调用commit的时候会触发session.flush操作

②执行session.createQuery查询的时候也会触发

reflush是将数据库的信息同步到session缓存

3、clear/evict()

从session缓存中清理数据

4、dowork

可以将jdbc的connection对象暴露出来,用于插入一些jdbc操作语法

5、save/persist()

都是用来将瞬时对象转变为持久化对象,即将数据插入数据库,对应insert语句。Save是hibernate原生语法,persist是jpa的语法

在执行的时候,不会立刻插入数据,只有执行了flush操作才能真正触发数据库操作。

save/persist方法会立刻为实体类对象生成主键

区别:

save会忽略你的赋值

persist会抛异常

6、update/merge()

主要用来完成实体类对象的修改,对应update语句。

update是hibernate原生语法,merge是jpa的语法

7、save、update与saveOrUpdate()

save()方法很显然是执行保存操作的,如果是对一个新的刚new出来的对象进行保存,自然要使用这个方法了,数据库中没有这个对象。

update()方法如果是对一个已经存在的托管对象进行更新那么肯定是要使用update()方法了,数据库中有这个对象。

saveOrUpdate()这个方法是更新或插入,有主键就执行更新,如果没有主键就执行插入。

区别:

对于一个从托管状态到瞬态的对象(对于一个从数据库中取出来又被删除的对象),这个对象本身是有主键的,但是因为被删除了,所以这个时候因为数据库中已经没有了这条记录了。不过它还有主键存在,所以这个时候不可以使用update()或者是saveOrUpdate(),因为update()方法是认为数据库中肯定有这条记录的,而saveOrUpdate的执行过程就是先查看这个对象是不是有主键,有主键那么就执行update()方法,没有主键就执行save()方法,因此结果跟调用了update()方法的效果是一样的,结果就会出错,因为这个对象已经被删除了,数据库中已经没有这条记录了,只是它还有主键而已(仅仅是存在于内存中),因此这个时候要执行的是save()方法。

3)持久化与加载对象

hibernate怎样实现持久化

1.hibernate是一种ORM(object relation mapping,对象关系映射)框架,所谓的对象关系映射,通俗的说,就是把JAVA对象保存到关系型数据库中,即进行持久化的框架。

2.hibernate进行持久化首先要建立对象到关系数据库的映射。

3.hibernate根据定义的映射规则将对象持久化(保存到)数据库存中。这就实现了对象持久化。

Session加载实体对象的过程

Session加载实体对象的步骤是:

① Session在调用数据库查询功能之前, 首先会在缓存中进行查询, 在一级缓存中, 通过实体类型和主键进行查找, 如果一级缓存查找命中且数据状态合法, 则直接返回

③ 如果一级缓存没有命中, 接下来Session会在当前NonExists记录(相当于一个查询黑名单, 如果出现重复的无效查询可以迅速判断, 从而提升性能)中进行查找, 如果NonExists中存在同样的查询条件,则返回null

③ 对于load方法, 如果一级缓存查询失败则查询二级缓存, 如果二级缓存命中则直接返回

④ 如果之前的查询都未命中, 则发出SQL语句, 如果查询未发现对应记录则将此次查询添加到Session的NonExists中加以记录, 并返回null

⑤ 根据映射配置和SQL语句得到ResultSet,并创建对应的实体对象

⑥ 将对象纳入Session(一级缓存)管理

⑦ 执行拦截器的onLoad方法(如果有对应的拦截器)

⑧将数据对象纳入二级缓存

⑨返回数据对象

三、检索

1)SQL和HQL区别

1、SQL面向数据库表查询,HQL面向对象查询

2、SQL:from后面跟的是表名、where后用表中字段做条件

HQL:from后面跟的是类名+类对象、where后用对象的属性做条件

2)五种检索方式

1.导航对象图检索方式。(根据已经加载的对象,导航到其他对象。)

2.OID检索方式。(按照对象的OID来检索对象。)

3.HQL检索方式。(使用面向对象的HQL查询语言。)

4.QBC检索方式。(使用QBC(Qurey By Criteria) API来检索对象。)

5.本地SQL检索方式。(使用本地数据库的SQL查询语句。)

1、导航对象图检索方式

利用类与类之间的关系来检索对象。譬如我们要查找一份订单,就可以由订单对象自动导航找到订单所属的客户对象。当然,前提是必须在对象-关系映射文件上配置了它们的多对一的关系。

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

Customercustomer = order.getCustomer();

2、OID检索方式

主要指用Session的get()和load()方法加载某条记录对应的对象。

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

Customercustomer = (Customer)session.load(Customer.class,1);

3、HQL检索方式

HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。它具有以下功能:

在查询语句中设定各种查询条件。

支持投影查询,即仅检索出对象的部分属性。

支持分页查询。

支持分组查询,允许使用group by和having关键字。

提供内置聚集函数,如sum()、min()和max()。

能够调用用户定义的SQL函数。

支持子查询,即嵌套查询。

支持动态绑定参数。

Session类的Qurey接口支持HQL检索方式,它提供了以上列出的各种查询功能。

注:Qurey接口支持方法链编程风格,它的set方法都返回自身实例,而不是返回void类型。方法链编程风格能使程序代码更加简洁。

示例代码:

Query query =session.createQuery("from Customeras c where "+"c.name=:customerName andc.age=:customerAge");

// 动态绑定参数query.setString("customerName", "Test");query.setInteger("customerAge", 21);

// 执行检索 Listresult = query.list();

// 方法链编程风格 Listresult1 =session.createQuery( "from Customer as c wherec.name=:customerName andc.age=:customerAge").setString("customerName","Test").setInteger("customerAge",21) .list();

①select语句和返回值

From Emp ejoin e.dept where e.name = ‘xxx’; //默认写select,将结果封装到Emp对象中

Select namefrom Emp; //返回值Object

Selectname,salay  from Emp; //返回值Object[]

Select newlist(name) from Emp; //返回值ArrayList

Select newmap(name,salay) from Emp; //返回值HashMap

Select newBoy(name,salay) from Emp; //返回值Boy对象

session.createQuery(“fromBook”,Book.class).uniqueResult();

session.createQuery(“fromBook”,Book.class).list();

session.createQuery(“fromBook”,Book.class).iterate().next();

②连接查询

Query不能使用JOIN抓取策略,默认使用select语句进行关联数据的加载,如果想强制使用join语句,需要通过hql语句指定

fetch决定最后结果的形式:

有fetch:[Emp,Emp…..]

无fetch:[[Emp,Dept],[ Emp,Dept]….]

From Emp ejoin e.dept where e.name = ‘xxx’;

From Emp eleft join e.dept where e.name = ‘xxx’;

From Emp eleft join fetch e.dept where e.name = ‘xxx’;

③分页查询

Customer customer = (Customer)session.createQuery("from customer c order by c.name asc") .setFirstResult(1).setMaxResults(5).uniqueResult();

④delete&update

delete Emp ewhere e.name = :name;

update Emp setname = :name where id = :id;

⑤级联操作的设置,对Query也是无效的。比如,想删除一个部门,需要先删除员工再删除部门

delete Emp ewhere e.deptment.deptno = ‘ #DN’;

delete Deptwhere deptno =  ‘ #DN’;

4、QBC(QureyByCriteria)检索方式

采用HQL检索方式时,在应用程序中需要定义基于字符串形式的HQL查询语句。QBC API提供了检索对象的另一种方式,它主要由Criteria接口、Criterion接口和Expression类组成,它支持在运行时动态生成查询语句。

示例代码:

Criteriacriteria =session.createCriteria(Customer.class);

Criterioncriterion1 = Expression.like("namr","T%");

Criterioncriterion2 = Expression.eq("age", newInteger(21));

criteria =criteria.add(criterion1);

criteria =criteria.add(criterion2);

// 执行检索 Listresult = criteria.list();

// 方法链编程风格 Listresult1=session.createCriteria(Customer.class).add(Expression.like("namr""T%")).add(Expression.

eq("age",new Integer(21))).list();

Hibernate还提供了QBE(Qurey By Example)检索方式,它是QBC的子功能。QBE允许先创建一个随想模板,然后检索出和这个样板相同的对象。

示例代码:

CustomerexampleCustomer=new Customer();

exampleCustomer.setAge(21);

List result1 =session.createCriteria(Customer.class).add(Example.create(exampleCustomer)).list();

QBE的功能不是特别强大,仅在某些场合下有用。一个典型的使用场合就是在查询窗口中让用户输入一系列的查询条件,然后返回匹配的对象。QBE方式目前只能支持对象属性字段的等于查询和字符串的模糊匹配,不能支持区间,或者,大于等操作。在这些情况下,还是采用HQL检索方式或QBC检索方式。

5、本地SQL检索方式

采用HQL或QBC检索方式时,Hibernate生成标准的SQL查询语句,使用于所有的数据库平台,因此这两种检索方式都是跨平台的。有的应用程序可能需要根据底层数据库的SQL方言,来生成一些特殊的查询语句。在这种情况下,可以利用Hibernate提供的SQL检索方式。

示例代码:

Query query =session.createSQLQuery("select {c.*} from CUSTOMER as cwhere c.NAME like:customerName andc.AGE=:customerAge");

// 动态绑定参数query.setString("customerName", "Test");query.setInteger("customerAge", 21);

// 执行检索 List result = query.list();

四、缓存

1)hibernate缓存机制

1、一级缓存

①第一级别的缓存是session级别的缓存,它是属于事务范围的缓存,这一级别的缓存由hibernate管理的,默认只支持一级缓存

②每个session内部都自带一个一级缓存

③每个session被关闭时,其对应的一级缓存自动清除

Session的缓存的作用

(1)减少访问数据库的频率。应用程序从内存中读取持久化对象的速度显然比到数据库中查询数据的速度快多了,因此Session的缓存可以提高数据访问的性能。

(2)保证缓存中的对象与数据库中的相关记录保持同步。当缓存中持久化对象的状态发生了变换,Session并不会立即执行相关的SQL语句,这使得Session能够把几条相关的SQL语句合并为一条SQL语句,以便减少访问数据库的次数,从而提高应用程序的性能。

2、二级缓存

第二级别的缓存是sessionFactory级别的缓存,它是属于进程范围的缓存,二级缓存独立于session,默认不开启。

使用hibernate二级缓存的数据:

什么样的数据适合存放到第二级缓存中?

  1、很少被修改的数据

  2、不是很重要的数据,允许出现偶尔并发的数据

  3、不会被并发访问的数据

  4、参考数据

 不适合存放到第二级缓存的数据?

  1、经常被修改的数据

  2、财务数据,绝对不允许出现并发

  3、与其他应用共享的数据。

配置使用二级缓存过程:

①加入jar包

②配置ehcache.xml

<ehcache>

    <!-- 如果缓存内存溢出则存储到磁盘 -->

    <diskStorepath="java.io.tmpdir"/>

    <!-- <defaultCache

           maxElementsInMemory="10000" 内存支持的最大对象的数量         

            eternal="false"                对象是否永久生效,建议为false,这样下面的两个参数才会有效

            timeToIdleSeconds="120"        对象的间隔周期,默认单位为秒

            timeToLiveSeconds="120"        对象的生命周期,默认单位为秒

            overflowToDisk="true"          是否支持溢出到磁盘,建议为true

           memoryStoreEvictionPolicy="LRU" 对象替换策略      LRU:最近最少被访问算法(时间策略),会忽略访问频率

                                           LFU:最近最未使用算法(频率优先),会忽略访问先后时间

            /> -->    

     <defaultCache

           maxElementsInMemory="1000"       

            eternal="false"           

            timeToIdleSeconds="60"    

            timeToLiveSeconds="120"       

            overflowToDisk="true"     

            memoryStoreEvictionPolicy="LFU"

            />

</ehcache>

③在hibernate.cfg.xml中启用

<!-- 启用二级缓存 -->  

<property name="hibernate.cache.use_second_level_cache">true</property>

<!-- 配置二级缓存提供商 -->

        <propertyname="hibernate.cache.provider_class">

            org.hibernate.cache.EhCacheProvider

        </property>

<!-- 配置哪些类需要二级缓存 -->

        <class-cacheusage="read-write" class="cn.it.shop.model.Product" />

或使用注解

@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)

3、查询缓存

Hibernate中的查询缓存

对于经常使用的查询语句,如果启用了查询缓存,当第一次执行查询语句时,hibernate会将查询结果存储在二级缓存中,以后再次执行该查询语句时,从缓存中获取查询结果,从而提高查询性能;

hibernate的查询缓存主要是针对普通属性结果集的缓存,而对于实体对象的结果集只缓存id;

查询缓存的生命周期,若当前关联的表发生修改,那么查询缓存的生命周期结束;

1,查询缓存的配置

查询缓存基于二级缓存,使用查询缓存前,必须首先配置好二级缓存;

在配置了二级缓存的基础上,在hibernate的配置文件hibernate.cfg.xml中添加如下配置,可以启用查询缓存:

<propertyname="hibernate.cache.use_query_cache">false</property>

此外在程序中还必须手动启用查询缓存:

query.setCacheable(true);

2,测试查询缓存:

1>开启查询缓存,关闭二级缓存,开启一个session,分别调用query.list查询属性,测试前,先在先在hibernate.cfg.xml文件中开启查询缓存,关闭二级缓存,如下所示:

<propertyname="hibernate.cache.use_query_cache">true</property>
<propertyname="hibernate.cache.use_second_level_cache">false</property>

public static void query(){

       Session sess = HibernateSessionFactory.getSession();

       Transaction tx = sess.beginTransaction();

       Query query = sess.createQuery("select s.namefrom Student s");

       query.setCacheable(true);

       List names = query.list();

       for(Iterator iter = names.iterator();iter.hasNext();){

           String name = (String)iter.next();

           System.out.println(name);

       }

       System.out.println("----------");

       query = sess.createQuery("select s.name fromStudent s");

       query.setCacheable(true);

       names = query.list();

       for(Iterator iter = names.iterator();iter.hasNext();){

           String name = (String)iter.next();

           System.out.println(name);

       }

       tx.commit();

       HibernateSessionFactory.closeSession();

   }

第二次没有去查数据库,因为启用了查询缓存;

2)hibernate查找对象如何应用缓存

    当hibernate根据ID访问数据的时候,首先从session一级缓存中查;查不到。如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存

删除、更新、增加数据的时候同时更新缓存

3)谈一谈Hibernate的一级缓存、二级缓存和查询缓存。

Hibernate的Session提供了一级缓存的功能,默认总是有效的,当应用程序保存持久化实体、修改持久化实体时,Session并不会立即把这种改变提交到数据库,而是缓存在当前的Session中,除非显示调用了Session的flush()方法或通过close()方法关闭Session。通过一级缓存,可以减少程序与数据库的交互,从而提高数据库访问性能。

SessionFactory级别的二级缓存是全局性的,所有的Session可以共享这个二级缓存。不过二级缓存默认是关闭的,需要显示开启并指定需要使用哪种二级缓存实现类(可以使用第三方提供的实现)。一旦开启了二级缓存并设置了需要使用二级缓存的实体类,SessionFactory就会缓存访问过的该实体类的每个对象,除非缓存的数据超出了指定的缓存空间。

一级缓存和二级缓存都是对整个实体进行缓存,不会缓存普通属性,如果希望对普通属性进行缓存,可以使用查询缓存。查询缓存是将HQL或SQL语句以及它们的查询结果作为键值对进行缓存,对于同样的查询可以直接从缓存中获取数据。查询缓存默认也是关闭的,需要显示开启。

五、两个配置文件

hibernate.cfg.xml配置文件

Hibernate 配置文件主要用于配置数据库连接和 Hibernate 运行时所需的各种属性

hibernate.cfg.xml的常用属性

<!-- 配置连接数据库的基本信息 -->

<propertyname="connection.username">root</property>

<propertyname="connection.password">1230</property>

<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<propertyname="connection.url">jdbc:mysql:///hibernate5</property>

<!-- 配置 hibernate 的基本信息 -->

<!-- hibernate 所使用的数据库方言 -->

<propertyname="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>     

<!-- 执行操作时是否在控制台打印 SQL -->

<propertyname="show_sql">true</property>

<!-- 是否对 SQL 进行格式化 -->

<propertyname="format_sql">true</property>

<!-- 指定自动生成数据表的策略 -->

<propertyname="hbm2ddl.auto">update</property>

<!-- 设置 Hibernate 的事务隔离级别 -->

<propertyname="connection.isolation">2</property>     

<!-- 删除对象后, 使其 OID 置为 null-->

<property name="use_identifier_rollback">true</property>     

<!-- 配置 C3P0 数据源 -->

<propertyname="hibernate.c3p0.max_size">10</property>

<propertyname="hibernate.c3p0.min_size">5</property>

<propertyname="c3p0.acquire_increment">2</property>

<property name="c3p0.idle_test_period">2000</property>

<propertyname="c3p0.timeout">2000</property>       

<propertyname="c3p0.max_statements">10</property>    

<!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->

<propertyname="hibernate.jdbc.fetch_size">100</property>      

<!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->

<propertyname="jdbc.batch_size">30</property>

<!-- 启用二级缓存 -->

<propertyname="cache.use_second_level_cache">true</property>    

<!-- 配置使用的二级缓存的产品 -->

<propertyname="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

或<!-- 配置二级缓存提供商 -->

<propertyname="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

<!-- 配置启用查询缓存 -->

<propertyname="cache.use_query_cache">true</property>

<!-- 配置管理 Session 的方式 -->

<propertyname="current_session_context_class">thread</property>          

<!-- 指定关联的 .hbm.xml 文件 -->

<mappingresource="com/atguigu/hibernate/helloworld/News.hbm.xml"/>

<!-- 配置哪些类需要二级缓存 -->

<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Employee"/>

<collection-cache usage="read-write"collection="com.atguigu.hibernate.entities.Department.emps"/>

jdbc.fetch_size 和 jdbc.batch_size (MySQl不支持,Oracle支持)

对象关系映射文件(.hbm.xml)

ORM(Object/Relation Mapping): 对象/关系映射

ORM 主要解决对象-关系的映射

–      类层次:class

•       主键:id

•       基本类型:property

•       实体引用类: many-to-one  |  one-to-one

•       集合:set | list | map | array

–      one-to-many

–      many-to-many

•       子类:subclass | joined-subclass

•       其它:component | any 等

–      查询语句:query(用来放置查询语句,便于对数据库查询的统一管理和优化)

<hibernate-mapping package="com.atguigu.hibernate.entities">

<class name="News" table="NEWS"dynamic-update="true">

<id name="id"type="java.lang.Integer">

    <columnname="ID" />

   <generator class="native" />

</id>

<property name="title" type="java.lang.String" unique="true" index="news_index"length="20">

<columnname="TITLE" />

</property>

映射关系

</class>

</hibernate>

一:one-to-one(一对一),例如人和身份证的关系

1、建立两张表,Person、Card(pid对应cid)

(1)数据库:Person(pid,pname)为主表,(cid,cnum)Card为从表;

(2)实体类:主表中有从表的属性,从表中有主表的属性

(3)关系:先要有Person表才能有Card,如果Person表删了Card也会自动销毁;如果Card表删了,Person表还在。

(4)根据实体类和关系建立hbm.xml文件

Person.hbm.xml:

<one-to-one name="从表的对象名" class="从表类权限定名" cascade="all-delete-orphan"></one-to-one>

Card.hbm.xml:

主键的生成策略:foreign

<generator class="foreign">

<param name="property">从表中定义主表的对象名</param>

</generator>

<one-to-one name="从表中定义主表的对象名" class="主表类权限定名">

二:one-to-many(一对多),例如省和市之间关系

1、建立两张表 Province、City(pid对应pid)

(1)数据库:Province(pid,pname)为主表,(cid,cname,pid)Card也为主表,pid为外键;

(2)实体类:(一端)Province实体类定义多端集合,需要实例化HashSet,一对多的关系;(多端)City实体定义一端对象,多对一的关系;

(3)映射文件:

Province.hbm.xml:

<set name="一端中定义多端的集合名" table="多端表名"cascade="save-update">

     <key column="多端表中外键名"></key>

     <one-to-many class="多端类权限定名">

</set>

   bag: list替代方案,所有流程与list相同但是可以不用设置index

  lazy="true" fetch="join": 此参数是针对查询而言

  inverse="false" cascade="all" 此参数是针对更新(删除、添加、更新)而言

Cascade,通常情况下都不会使用。特别是删除,一定要慎重。

<one-to-many>中,建议inverse=”true”,由“many”方来进行关联关系的维护

   inverse 建议配置true: 对方来维护外键(外键在哪张表,就哪张表维护) 

   inverse:false  则主表来维护 子表的外键,此时会通过发update语句来更新外键 ,此时会多发N条update语句

<list name="sorderList"lazy="true" fetch="join" cascade="all" >

   <!-- 代表的是外键 -->

     <key>

                <columnname="fid"></column>

            </key>

            <indexcolumn="index_"></index>

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

</list>

City.hbm.xml:

<many-to-one name="多端类中定义一端的属性名" class="一端类权限定名" column="多端表中外键名(一端ID)" inverse="true">

</many-to-one>

三:many-to-many(多对多),例如人和角色之间的关系

1、建立三张表Person、Roles、p_s(关系表)

(1)数据库:Person(pid,pname)pid主键、Roles(rid,rname)rid主键、p_r(pid,rid)不设主外键

(2)实体类:(多端Roles)Person实体类定义多端集合,需要实例化HashSet、(多端Person)Roles实体类定义多端集合,需要实例化HashSet

(3)映射文件:
Person.htm.xml:

<set name="(Roles)多端的集合名" table="关系表名(p_r)"  cascade="save-update">

     <keycolumn="pid">    </key>

            <many-to-many class="多端类权限定名"column="rid">

           </many-to-many>

</set>

Roles.htm.xml:

<set name="(Person)多端的集合名" table="关系表名(p_r)" >

     <keycolumn="rid">   </key>

            <many-to-many class="多端类权限定名"column="pid">

           </many-to-many>

</set>

<!-- 配置角色与权限的关联, 双方是多对多的关系

     多对多查询流程: 先到指定的table: privilege_role 通过自己的 id匹配 rid,然后通过 rid去找到相应的pid <column name="pid" />pid 在去匹配Privilege表的 id

在多对一 更新的时:外键就在本身表, 可以通过此方式  student.setGrade(newGrade(1)); 不用配置: cascade="all"

在多对多时:外键在中间表,此时中间表的维护是由它自身相应主表,也不用配置cascade="all", 如果配置则会更新 Privilege

-->

        <setname="privilegeSet" table="privilege_role">

           <!-- key代表是外键,其它表的字段 -->

            <key>

                <column name="rid" />

            </key>

            <many-to-many class="Privilege">

                <column name="pid" />

            </many-to-many>

       </set>

<one-to-many>

操作建议:

    一般对many-to-one和many-to-many不设置级联,这要看业务逻辑的需要;对one-to-one和one-to-many设置级联。

many-to-many关联关系中,一端设置inverse=”false”,另一端设置为inverse=”true”。在one-to-many关联关系中,设置inverse=”true”,由多端来维护关系表

六、其他

1)N+1 问题

 N+1的概念: 主表发出一条sql语句, 然后在发出N条SQL查询关联表,N是不确定的 ,由外键的值来决定

N+1解决方案: 项目中查询的需求是多变的,不能吧配置放到xml中(不灵活) 采用自己编写的HQL语句取代自动生成SQL语句(myBatis框架实现的方式)

解决方案主要有下面几种:

  1.在 hql 语句中,使用 join 语句进行关联查询。

  2.将 Dept#emps 的策略设置为SUBSELECT 方式。

  3.使用二级缓存。

2)延迟加载

延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。Hibernate使用了虚拟代理机制实现延迟加载。返回给用户的并不是实体本身,而是实体对象的代理。代理对象在用户调用getter方法时就会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时,数据库连接就同时关闭了。

延迟加载与session关闭的矛盾一般可以这样处理:

① 关闭延迟加载特性。这种方式操作起来比较简单,则每次查询的开销都会变得很大。

②在session关闭之前先获取需要查询的数据(Hibernate.initialize()方法)。

③ 使用拦截器(Interceptor)或过滤器(Filter)控制Session。

3)锁(Lock)

Hibernate 中,设置锁定有下面三种方式:

session.load(Male.class,1L, LockMode.WRITE)

session.lock(m,LockModeType.WRITE);

session.createQuery(hql).setLockMode(LockModeType.PESSIMISTIC_WRITE);

Hibernate 中锁的类型,分为两种:

 1.悲观锁。使用数据库底层的 for update 语句。数据会被锁定,直到事务结束。

 2.乐观锁。使用实体类中的额外字段(@Version)。它不会真正在数据上加锁,而是用版本号区别记录的不同。 

悲观锁更适用于修改频率大,读取不多的数据。乐观锁适用于修改非常少,但读取特别多的数据。悲观锁需要耗费更多资源。 

4)开发步骤

1、添加jar包

2、通过逆工程反向生成代码(POJO + 映射文件)

3、创建hibernate配置文件(hibernate.cfg.xml)

4、进行数据持久化操作

a)   编写持久化类: POJO + 映射文件

b)   获取 Configuration 对象

c)   获取 SessionFactory 对象

d)   获取 Session,打开事务

e)   用面向对象的方式操作数据库

f)   提交事务

g)   关闭事务,关闭 Session

5)优化

① 制定合理的缓存策略

② 采用合理的Session管理机制

③ 尽量使用延迟加载特性

④设定合理的批处理参数

⑤ 如果可以, 选用UUID作为主键生成器

⑥如果可以, 选用基于version的乐观锁替代悲观锁

⑦ 在开发过程中, 开启hibernate.show_sql选项查看生成的SQL, 从而了解底层的状况;开发完成后关闭此选项

⑧ 数据库本身的优化(合理的索引, 缓存, 数据分区策略等)也会对持久层的性能带来可观的提升, 这些需要专业的DBA提供支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值