在spring中使用hibernate

在spring中使用hibernate

一、基本配置

在Spring的servlet-context.xml文件中添加

    <!-- 配置数据库 -->
    <!-- 简单的使用jdbc的DriverManagerDataSource,没有连接池 -->
    <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <beans:property name="url" value="jdbc:mysql://localhost:3306/your_database" />
        <beans:property name="username" value="your_name" />
        <beans:property name="password" value="your_password" />
    </beans:bean>

    <!-- 配置hibernate的session factory -->
    <beans:bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <beans:property name="dataSource" ref="dataSource" />
        <!-- 定义去哪些包下扫描实体类(@Entity注解) -->
        <beans:property name="packagesToScan">
            <beans:list>
                <!-- 可以加多个包 -->
                <beans:value>com.funway.mybet.model</beans:value>
            </beans:list>
        </beans:property>
        <beans:property name="hibernateProperties">
            <beans:props>
                <beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</beans:prop>
                <beans:prop key="hibernate.show_sql">true</beans:prop>
                <beans:prop key="hibernate.format_sql">true</beans:prop>
                <beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
            </beans:props>
        </beans:property>
    </beans:bean>

然后如果没有Lib的话,我们需要在maven的pom.xml配置文件中添加如下几个dependency,来自动添加hibernate所需要的库。

屏幕快照 2016-02-26 下午2.54.42

然后定义一个获取User的Dao(暂时,我们不考虑User实体类,以及它与数据库User表的映射关系,只考虑从User表中获取用户数)

@Repository
public class UserDaoHibernate {
    
    @Autowired
    private SessionFactory sessionFactory;

    public int getUserCounts() {
        Session session = sessionFactory.openSession();
        Query query = (Query) session.createSQLQuery("select count(*) from mb_user");
        int counts = Integer.parseInt(query.list().get(0).toString());
        session.close();
        return counts;
    }
    
}

上面,我们使用@Repository注解声明自动生成一个UserDaoHibernate类型的实例对象,用@Autowired注解将servlet-context.xml中定义的sessionFactory注入到成员属性中。

然后,我们就可以在任意地方调用这个UserDaoHibernate实例的getUserCounts( )方法了。以一个Controller为例:

@Controller
public class HomeController {

	@Autowired
	private UserDaoHibernate userDao;

        /**
         * ...省略
         **/
   
	@RequestMapping(value="/hiber", produces = "text/plain;charset=UTF-8")
	public @ResponseBody String testJdbcTemplate() {
	    return "已注册用户数: " + userDao.getUserCounts();
	}

二、使用xml配置的声明式事务

上面的getUserCounts()方法中,我们手动打开session,进行查询,最后再手动关闭session,并没有用到事务的概念。当然,简单的查询用户数量确实并不需要事务,但下面仍以这个方法为例,在getUserCounts上添加事务管理。

首先,我们先修改下getUserCounts()方法的实现:

    public int getUserCounts() {
        Session session = sessionFactory.getCurrentSession();
        Query query = (Query) session.createSQLQuery("select count(*) from mb_user");
        int counts = Integer.parseInt(query.list().get(0).toString());
        return counts;
    }

这里使用了getCurrentSession()来获取一个数据库会话。这个方法的实现是与事务相关的,如果不在Spring的xml文件中添加事务管理的配置,直接调用修改后的getUserCounts()方法的话,程序将会报错:

    Could not obtain transaction-synchronized Session for current thread

    <!-- 配置hibernate的事务管理器 -->
    <beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
      <beans:property name="sessionFactory" ref="sessionFactory" />
    </beans:bean>

    <!-- 定义事务策略,transaction-manager指定事务管理器为transactionManager -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="*" read-only="true" />
        </tx:attributes>
    </tx:advice>
      
    <aop:config>
        <!-- 定义AOP通知器,连接切点与事务策略 -->
        <aop:advisor 
            pointcut="execution(* com.funway.mybet.model.UserDaoHibernate.getUserCounts(..))" 
            advice-ref="txAdvice"/>
    </aop:config>

同时,还需要在该xml文件的开头加上tx跟aop命名空间。

屏幕快照 2016-02-26 下午5.21.37

另外由于使用了Spring AOP,因此还要为程序添加如下两个依赖库

屏幕快照 2016-02-26 下午3.32.47

通过上面的配置,UserDaoHibernate.getUserCounts()方法就被事务管理器transactionManager包裹进来了。然后在方法执行前,transactionManager会自动去打开一个session,这样,方法中的getCurrentSession才能得到当前会话,最后在方法结束后transactionManager会自动commit并且关闭这个session,如果方法出错了,就会自动rollback。这叫做声明式事务。(当然,也可以在getUserCounts()的代码中自行管理事务,那样就叫做编程式事务)

PS:上面只是做了一个不实用的例子,在实际三层架构的项目中,并不推荐在Dao层使用事务。Dao层只实现基本的CURD(Create/Update/Read/Delete),更复杂的业务应该放在Service层。比如订单业务,Service层有一个order方法,该方法里面调用了Dao层的两个方法,一个是生成订单,insert到数据库的订单表;一个是更新库存,update数据库的库存表。那么事务就应该切入到Service层的order方法中。而不是在Dao层。

因此,事务应该切入在Service层的方法中。

三、使用@Transactional注解的声明式事务

在要声明为一个事务的getUserCounts()上面使用@Transactional注解:

    @Transactional
    public int getUserCounts() {
        Session session = sessionFactory.getCurrentSession();
        Query query = (Query) session.createSQLQuery("select count(*) from mb_user");
        int counts = Integer.parseInt(query.list().get(0).toString());
        return counts;

然后修改servlet-context.xml中定义的关于事务的配置

    <!-- 配置hibernate的事务管理器,这个有什么用? -->
    <beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
      <beans:property name="sessionFactory" ref="sessionFactory" />
    </beans:bean>
    
    <!-- 启动基于注解的事务 -->
    <tx:annotation-driven />

<tx:annotation-driven>元素告诉Spring检查上下文中所有的Bean并找到使用@Transactional注解的Bean(@Transactional可以注解在类上级别,也可以注解在方法级别)。对于每个使用了@Transactional注解的Bean,自动添加AOP事务通知器。

另外,可以使用transaction-manager属性为<tx:annotation-driven>指定特定的事务管理器(该属性默认值即为"transactionManager",所以上面的例子中我们无需特别指定)

    <tx:annotation-driven transaction-manager="txManager" />

使用@Transactional注解,就可以无需在xml文件中定义繁琐的AOP切面以及事务策略了。

四、写入Mysql后中文为??的问题

被各种中文乱码伤透心 =。=#

要把jdbc的url配置改为如下:

<beans:property name="url" value="jdbc:mysql://localhost:3306/mybet?useUnicode=true&amp;characterEncoding=UTF-8" />

注意,由于配置文件是xml格式,所以userUnicode与characterEncoding中间的&符号应该用其对应的转义字符&amp;否则eclipse的xml检查会报错。

Leave a Reply

Your email address will not be published. Required fields are marked *

TOC