目录
利用ThreadLocal的管理会议(的getCurrentSession)
休眠的三种状态(转载:ImportNew公众号:hibernate三种状态的转换)
一.事务
什么是事务?
是并发控制的单元,是用户定义的一个操作序列。事务通常是以调用BeginTransaction开始,以提交或回滚结束.Commint表示提交,即提交事务的所有操作。
为什么要使用事务?
提高性能,保持业务流程的完整性。使用分布式事务
事务的特性(ACID)
原子性:事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。
一致性:事务在完成时,必须是所有的数据都保持一致状态在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
隔离性:一个事务的执行不能被其他事务所影响企业级的数据库每一秒钟都可能应付成千上万的并发访问,因而带来了并发控制的问题。
持久性:一个事务一旦提交,事物的操作便永久性的保存在数据库中即使此时再执行回滚操作也不能撤消所做的更改。
事务的并发问题
脏读:一个事务读取到了另一个事务未提交的数据操作结果这是相当危险的,因为很可能所有的操作都被回滚。
不可重复读(虚读):一个事务对同一行数据重复读取两次,但是却得到了不同的结果例如事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
幻读:事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据,这是因为在两次查询过程中有另外一个事务插入数据造成的
避免以上问题可以在配置文件中添加<property name =“hibernate.connection.isolation”> ______ </ property>可选值有1,2,4,8 ---- 1:read uncommited(读未提交) 2:读取提交(读已提交Oracle默认)4:可重复读取(可重复读MySQL默认)8:serializeable(串行化读)
利用ThreadLocal的管理会议(的getCurrentSession)
原理:每一个线程是独立的,单一的,所以将会话对象跟当前线程绑定,那么就可以将会议做标记,以便多方访问。
配置文件:hibernate.cfg.xml中
<property name =“current_session_context_class”> thread </ property>
更新数据丢失的解决办法(休眠中并发的解决办法)
悲观锁:采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加for update子句当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的。
代码:session.get(Customer.class,1,LockMode_UPGRADE);
何为悲观:悲观锁认为总是会发生并发,将每一次的请求都当做并发来处理。缺点:会阻塞,只有当前请求完成时才可继续让其他请求进行操作数据库被锁列。
乐观锁:采用版本号的机制来解决的会给表结构添加一个字段版本= 0,默认值是0当甲事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号的版本= 1 ,,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误。
代码实现:1.在实体类中添加一个私有属性用于记录版本号。private Integer version; 提供获得和设置方法
2.书写配置文件在主键之后其他属性之前写<version name =“version”> </ version>告知程序版本属性是用来记录版本号的。
何为乐观:乐观锁认为所有的请求都是独立,如果发生并发,利用验证version的版本号是否相同来确定请求是否成功.
休眠的三种状态(转载:ImportNew公众号:hibernate三种状态的转换)
瞬时态(Transient):当我们利用新创建一个实体对象(代码:Customer customer = new Customer("点点",1)),这个对象此时就处于自由状态,为什么说是自由状态,因为此时的实体对象仅仅是通过JVM获得了一块内存空间,还并未通过session对象save()保存在数据库中,因此休眠的缓存管理中并没有纳入实体对象,所以实体对象此时还自由的游荡在Hibernate的缓存管理之外,自由对象最大的特点就是,在数据库中不存在一条与它对应的记录。特点:1.不和Session实例关联. 2.在数据库中没有关联记录.
持久状态(Persistent):持久化对象就是指已经保存在数据库中的实体对象,并且这个对象也存在于Hibernate的缓存中.此时对该对象的任何修改都会在清理缓存时都会同步于数据库中.(代码:
Customer customer = new Customer("点点",1);
Transaction tx = session.beginTransaction();
session.save(customer);
customer = (Customer)session.load(Customer.class,"1");
customer.setAge(28);
tx.commit();)
这是我们并没有调用session.update()方法来保存更新,但是实体对象的修改依旧会更新于数据库,因此customer对象通过save方法保存进数据库后,已经是持久化对象了,然后通过load方法再次加载它,它仍然是持久化对象,所以它还处于Hibernate缓存的管理之中,这时当执行tx.commit()方法时,Hibernate会自动清理缓存,并且自动将持久化对象的属性变化同步到到数据库中.
持久化的实例对象持久的实例在数据库中有对应的记录,并拥有一个持久化标识 (identifier).
持久对象总是与 Session 和 Transaction 相关联,在一个 Session 中,对持久对象的改变不会马上对数据库进行变更,而必须在 Transaction 终止,也就是执行 commit() 之后,才在数据库中真正运行 SQL 进行变更,持久对象的状态才会与数据库进行同步.在同步之前的持久对象称为脏 (dirty) 对象(结合事务的并发问题中脏读的概念)。
瞬时对象转为持久对象:
-
通过 Session 的 save() 和 saveOrUpdate() 方法把一个瞬时对象与数据库相关联,这个瞬时对象就成为持久化对象.
-
使用 fine(),get(),load使用 fine(),get(),load() 和 iterater() 待方法查询到的数据对象,将成为持久化对象.
持久化对象的特点:1.和Session实例关联 2.在数据库中有和持久对象关联的记录
脱管状态(Detached):当一个持久化对象,脱离开Hibernate的缓存管理后,它就处于游离状态,游离对象和自由对象的最大区别在于,游离对象在数据库中可能还存在一条与它对应的记录,只是现在这个游离对象脱离了Hibernate的缓存管理,而自由对象不会在数据库中出现与它对应的数据记录.
(代码:
Customer customer = new Customer("点点",1);
Transaction tx = session.beginTransaction();
session.save(customer);
customer = (Customer)session.load(Customer.class,"1");
customer.setAge(28);
tx.commit();
session.close();)
与持久对象关联的 Session 被关闭后,对象就变为脱管对象.对脱管对象的引用依然有效,对象可继续被修改.
脱离对象的特点:1.本质上于瞬时对象相同 2.脱离对象比瞬时对象多了一个数据库记录标识值 id.
持久化对象转换为托管对象:当执行 close() 或 clear(),evict() 之后,持久对象会变为脱管对象.
三种状态的转换:
Hibernate中的缓存
一级缓存
Hibernate的一级缓存是指Session(属于事务范围的缓存,由Hibernate管理,无需干预),它是一块内存空间,用来存放从数据库查询出的java对象,有了一级缓存,应用程序可以减少访问数据库的次数,提高了性能。
使用代码来证明Hibernate中一级缓存的存在:
Customer c1 = session.get(Customer.class, 1L);
System.out.println(c1);
Customer c2 = session.get(Customer.class,2L);
System.out.println(c2);
在控制台中可以看到SQL语句只执行了一次,打印了两个对象.说明只在数据库中查询了第一次,而第二次是从session缓存中取出的对象.
一级缓存注意:当session.close(),session.clear()时都会清空一级缓存.一级缓存只保存在当前session对象中,如果session对象不同,将会执行两次SQL语句.
二级缓存
Hibernate的二级缓存是指sessionFactory缓存,二级缓存的生命周期与sessionFactory一样,直到sessionFactory关闭时才会死去.
注意:一级缓存与二级缓存只能是单个对象,不能是集合.
一级缓存默认开启,二级缓存需要手动配置相应文件.
配置文件的书写:
1.在pom文件中添加:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.2.10.Final</version>
</dependency>
2.hibernate.cfg.xml文件添加:
<!--开启(关闭)二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true(flase)</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
<!-- 所需要被保留在二级缓存中的实体类 应写于mapping节点以下 -->
<class-cache usage="read-only" class="com.dream.Customer" />
查询缓存
可以存储一个实体类的多个对象的集合的缓存.保留其属性.
查询缓存依赖于二级缓存,所以需配置二级缓存来使用.
配置文件的书写:
1.在pom文件中添加:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.2.10.Final</version>
</dependency>
2.hibernate.cfg.xml文件添加:
<!--开启(关闭)二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true(flase)</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
<!-- 开启(关闭)查询缓存 -->
<property name="hibernate.cache.use_query_cache">true(flase)</property>
<!-- 所需要被保留在二级缓存中的实体类 应写于mapping节点以下 -->
<class-cache usage="read-only" class="com.dream.Customer" />
在编写测试代码时要注意:Query<Customer> q2 = session.createQuery("from Customer c where c.age > ?", Customer.class).setCacheable(true);
如果没有调用setCacheable(true)方法,那么将不会把q2存入查询缓存中.