关于Spring4与Hibernate4整合

来源:互联网 发布:易赛软件 编辑:程序博客网 时间:2024/04/29 23:51
在做Spring4和Hibernate4整合项目的时候,遇到了很多问题,今天总结一下,方便备忘。

其实这是第二次做这样的整合,因为第一次完全没去关心Spring4和Hibernate4到底和3有什么不一样,但是第二次做的时候,因为业务复杂、逻辑较多报出很多很多的BUG,让我只有老老实实的重新学习和认识一下这两个框架及其整合,今天终于在同事和自己研究的情况下得出了一套自己认为比较合适,而且很简单的整合方案,并记录下来让自己学习和改进,也让大家参考一下,希望能改进本整合方案。

整合原则:最少配置原则(尽量减少XML的配置,而是使用注解方式)
整体框架:Spring4 + Hibernate4 + SpringMVC + Maven3
数据库:mysql

首先第一点讲到的是:
   Spring4对Hibernate的支持,因为Spring对Hibernate的支持只到了Hibernate3X,所以Hibernate4中,就不能使用那个Spring提供的Template来进行数据库查询。那该怎么办呢?我自己想了想,第一我们使用了Spring,所以我们应该将所有的Bean交给Spring来管理,关键是如何才是交给Spring来管理的呢?
    1.Hibernate SessionFactory
       在原生Hibernate框架中,我们是通过SessionFactory.openSession()来获取Session的,所以从这一点我们可以猜测出我们首先的有一个交给Spring管理的SessionFactory,然后我们才能拿的到Session,然后我们才能操作数据库。所以我们首先的在Dao的实现层注入SessionFactory代码如下:
图片

图片


这样我们就得到一个Spring管理的SessionFactory。那么问题又来了,我们是如何获取Session的呢?试想一下,如果是openSession的话那么得到Session是由Spring管理的吗?答案是否定的,openSession是得到一个新的Session,而不是由Spring提供的,那么如何实现呢,到网上找找,发现了一些蛛丝马迹,SessionFactory提供了一个方法getCurrentSession(),获取的是当前Session,那么我们就可以从这里入手了,首先我是尝试了下,直接这样获取Session,结果报错了,(给我说没有这样的Session在当前线程中)。那好吧,我就想既然没有,那么我就将Session绑定到当前线程中就可以了三,我就在Spring配置文件中,配置数据源那里添加上了:
图片

就这样我们就绑定上了,但是这只是错误的开始,当我高高兴兴的再次测试时,MD又错了,这次
 createSQLQuery is not valid without active transaction
 
曾几度怀疑是否配置写出了,dao或service写错了,改来改去的依旧存在问题。当时相当郁闷啊,想啊,你spring不是帮我管理事务么?你不自动开启事务啊,还要我手动开启啊。立马查spring文档,从中文到英文,没发现什么有参考价值的线索,真是相当的打击,代码乱改一通。不死心的去查了hibernate的doc,MD就是咋们刚刚配置的属性绑定到当前线程这个,所以这样我们就陷入了一个死循环,我当时也纠结了很久,好吧这样不行那就只有换种方式了。

那么首先想到的就是咋们注入SessionFactory的方式不对,因为这样获取的Session是错误的,根本不能帮我们解决问题,那么自然就想到换一种方式获取SessionFactory,我们刚刚使用setSessionFactory的方式注入的,那么我们使用构造方法注入SessionFactory,但是为什么构造注入就可以了呢?原因是因为,两种注入方式的依赖不同:set方法注入,是先实例化DaoImpl,在判断sessionFactory是否存在,不存在则在实例化sessionFactory,再通过DaoImpl的set方法注入,这样的话我们可以理解为:就算没有注入sessionFactory,DaoImpl也是可以工作的,所以两者不存在依赖关系,而是组合关系,所以我们获取到的sessionFactory和我们DaoImpl无法正常的关联起来,所以获取的session也需要绑定当前线程,就算绑定了事务也无法自动开启等问题就出现了。
那么构造注入的方式就是:首先你得先调用sessionFactory的构造,实例化之后才能调用DaoImpl的构造,再将sessionFactory当作参数传入
DaoImpl的构造,所以构造注入提现了如果没有sessionFactory,就没有DaoImpl,所以两者是依赖关系,那么他们就相互绑定的,所以问题就迎刃而解了。(自己研究出来的还是有点小激动,如果有误请指点~)
图片


这样以上的问题就都解决了。
 

接下来从Spring事务入手 ,咋们事务采用的是注解的方式,即使用了:
图片
 
proxy-target-class这个是什么呢?其实是设置代理模式,让事务强制使用cglib的代理,而不是JDK代理,因为:

和CGLIB不同的是,JDK代理只能代理接口,不能代理类。 
使用JDK代理时,如何处理一个特定的方法调用的决定是在程序运行时做出的,也就是在每次方法被调用时。使用CGLIB代理可以边开这种处理方法,CGLIB会在运行中随时为代理创建新类的字节码,并尽可能的重用已经生成的类的字节码。

CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
 
既然是注解的形式,咋们注解注解到哪里呢?我们想想以前AOP设置切点的位置,一般都是在Service中设置,那咋们的事务注解也应该在
Service层吧。那好,其实注解形式的,和配置区别不大,配置文件主要是针对一个类或者是一个类中的某些方法,又或者是一个包;那么注解针对的是一个方法,使用@Transactional,同样也可以配置事务的传播行为和隔离模式,而且我觉得这种方式更精准吧,哈哈,不多说贴代码:
图片


 propagation是传播行为的配置
 isolation 是隔离模式的配置

这里有一点就是,当你事务设置了readOnly属性的时候,只读会影响你事务的提交,最好在有对数据库进行增删改的时候设置readOnly为false。

 再说一点就是@Transactional事务回滚,需要抛出RuntimeException才会回滚事务,如果要设置自定义异常回滚,需要设置
rollbackFor等属性具体的我就不详细解释了,自己看API比较好,这样容易记住,我这里说了也就说了。

那这样真的就整合完成了吗?那你就错了,按照常理来说这样已经整合完成了,但是这里因为在Service层使用了@Transactional,查了Doc发现@Transactional不能和@Service注解连着用,会出现Session不能flush的问题,这样事务就不能正常提交。(我当时就想说坑爹啊)那好吧这样咋们该怎么办呢?其实这有点像脑经急转弯,既然不能用@Service,那咋们就不用了,转成写配置吧:
图片
 
因为Dao使用了注解,所以这里无需引用

这样咋们的整合算是真的完成了,虽然只有那么一点点,但是也是值得研究的。

其他地方配置和注解方式基本没有改变如:web.xml,Springmvc的配置等。

补充一点:mysql也会引起事务不能提交的问题主要是对于数据库引擎的选择,MyISAM这个引擎是不能提交事务的,所以建议改为InnoDB
0 0
原创粉丝点击