事务处理:
事务就是一组相互依赖的操作,要么全成功,要么全部失败。比如:A转账到B100,必须A账户减少100,B账户同时增加100,两个都成功才可以。在程序里面不能将事务设置成自动提交(查询除外),在JDBC里面是用Connection的setAutoCommit方法来实现的,在操作之前设置成false,在最后一步commit,在catch里面回滚。
事务不能读到没有提交的,或者已经取消的,事务隔离级别一般由数据库控制,有四种,一般选用read committed,mysql默认是repeattable read.
Repeatable read:在同一个事务中两次读到同一个记录,应该得到一样的结果,其实就是说,一个事物读到一条记录,但是还没提交,另一个事务同时来读,要修改它时,必须等到之前那个事务提交了,该事务才能修改,但是是可读的。
程序中一般是乐观锁实现。
悲观锁很影响性能,他会在发sql时加上for update,select … for update。这样一查出来就被锁定。别的事务可读,但是不可修改。直到他提交。悲观锁就是认为,事务的数据总会受到别的事务的影响,就一开始就上锁。
Read Committed:
要解决non-repeatable read的问题。用到乐观锁和悲观锁来实现。
出现的问题:例如:账户a,取出余额,计算后再放回数据库,但是取出余额到放回数据库的过程中,有另一个事务改了这个a的值,这时,这个事务再放回数据库就冲掉了那个数据。
实现就是乐观锁:
注解版本解决方法:在实体类里面有
private int version; getVersion()方法上加注解@Version;即可。在保存数据时,这个字段不用管,由hibernate自动维护。此时:第一个session load,第二个session load相同的,然后第一个事务修改数据,第二个事务修改数据,最后第一个session提交,第二个session提交。这时,第二个session就会被报错。因为第一个session提交后,version改变了,第二个session提交时发现这个version不是它load数据的那个值,就会报错了。这个事务就会回滚。出现这种情况,可以稍等一会再提交。
----------关系到hibernate的性能问题
第一个性能问题:分页,一条50条,在同一个session中时,不断翻页,这个session会不断增加记录。要记得session.clear();(这种情况比较少)。
Java在语法上是没有内存泄露的。因为有垃圾回收器会清理,但是打开资源时,没有关闭,是可能造成内存泄露的。
1+N问题:
一个category对应几个topic,是一对多关系。循环查询topic时候,会把每个topic关联的category都查询出来,发了一堆sql语句。但是本意只是取topic。这是因为fetchType=eager.因为hql语句from Topic就只发一条,但是关联起来,会from Category where id=。。。。。。就变成发N条。因此叫1+N。就是说本来只发一条,结果发了1+N条。
解决方案:在@ManyToOne(fetch=FetchType.LAZY)这是说不要立即取,不是就不能取了,用到的时候再取。
但是查Category的语句什么时候发?查的时候发就是t.getCategory.getName()时发。
第二种是用createCriteria方法不用createQuery();
第三种解决方法是:在被关联的Category类上面加上@BatchSize(size=5),就是关联取时不要取完,一次取5条,取完所有数据。BatchSize比较少用。
关联关系的crud:
存:group,user,user有外键是group,正常情况,设关联关系,存group再存user是没有问题的。但是只存user希望存user时级联的存进group是会报错的。想级联的能保存,需要设置:cascade,all是说crud都级联,persist是仅保存时级联。具体的设置方法是
@ManyToOne(cascade={CascadeType.ALL});或者ALL换成PERSIST。
(但是cascade只是写起来方便,还是按照默认的手动存的好。)
一个写法:@OneToMany(mappedBy=”group”,cascade={CascadeType.ALL})
取:manytoone取多的时候是get出来的,默认的会将一的一方取出来。就是取某个user会默认把他所在的组取出来。
但是取一的一方,GROUP时,对应的users默认的就不会被取出来。即使CascadeType.ALL也不会取出来。Cascade就是crud把r去掉的,r是fetchType对应的。也就是说多对一取多时fetchType是eager的。
改:
Load出来user,user.setName更新,user.getGroup.setName()会发两条语句,更新user和group。
删:
User和group,删除u1,如果设置的cascadeType.ALL,就会级联的删,将所有的都删掉。想删除的话,最好先把关联关系设置为null,就可以删除了。默认的是哪一项?会级联删除吗?删除最好用hql就是session.createQuery(“delete from User u where u.id=?”).executeUpdate();
这样删除比较精确;如果想删除group:先load出来group,如果设置了关联ALL会级联删除,最好是先循环取出user去掉关联关系再删除。
Hibernate的缓存:
每开一个session它自己是有缓存的,这一个session里面两次getName是不会再发sql语句的。但是两个session先后都取同一个get Name时,相互之间缓存就不能互取,就会发两次sql语句。
这就用到二级缓存,就是sessionFactory的缓存。
<property name=”cache.use_second_level_cache”>true</property>
<property name=”cache.provider_class”>org.hibernate.cache.EhCacheProvider</property>
这两个配置后,还需要:一个ehcache.xml这个是ehcache真正的配置文件,规定怎么用cache,例子文件去etc下面找ehcache.xml这个文件,拷贝到src下面即可。
Cache的配置:最多放多少个对象,缓存中的对象是否永存,idle空闲多久后被清除。timeToLive放进缓存后多久必须被销毁了。有默认的配置项,使用即可。
但是只有:
经常被访问;改动不大;数据量不大的数据才适合放进缓存中。
例如:用户权限。改动不大,数量也不大,又常被访问。
组织机构;
配置好后:那些需要放入二级缓存的类还要配置一下,在annotation方式中是配
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE); (读写最常用,read_only很少用)
不配置的话,这个类是不会被放入二级缓存中去的。
还有两个注意点:需要ehcache.jar包,这个包用的日志系统是apache的,需要加入:commons-logging.jar文件。
此时,两个session同时去load一个对象,getName时就不会发两次SQL语句了。
默认:load会使用二级缓存,就是查询一个对象时,它会先去二级缓存里里面找,iterator默认也是用二级缓存。
List默认往二级缓存里面加数据,但是查询的时候不用。(就是查询不查二级缓存,但是查询出来,放二级缓存里面)。
以上缓存是load时用缓存,或者用 get得到时,但是用hql查询时,还是不会用缓存的,要用的话还得配置查询缓存。
---查询缓存:
打开查询缓存:查询缓存是依赖于二级缓存的,所以上面的二级缓存必须打开。
<property name=”cache.use_query_cache”>true</property>
在程序中要:
session.createQuery(“from Category”).setCacheable(true).list();
关键是setCacheable(true)//使用查询缓存。
双向一对多关联详解:
从一到多的方向控制时,加set标签,子标签有one-to-many,key是说多的一方用哪个字段进行关联,inverse设置为true表示多的一方来控制这种关联关系,默认是false,一般一的一方设置为true,多的一方不用配置这个选项。