在spring中使用hibernate

一、基本配置

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!-- 配置数据库 -->
<!-- 简单的使用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>
<!-- 配置数据库 --> <!-- 简单的使用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>
    <!-- 配置数据库 -->
    <!-- 简单的使用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表中获取用户数)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@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 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
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为例:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Controller
public class HomeController {
@Autowired
private UserDaoHibernate userDao;
/**
* ...省略
**/
@RequestMapping(value="/hiber", produces = "text/plain;charset=UTF-8")
public @ResponseBody String testJdbcTemplate() {
return "已注册用户数: " + userDao.getUserCounts();
}
@Controller public class HomeController { @Autowired private UserDaoHibernate userDao; /** * ...省略 **/ @RequestMapping(value="/hiber", produces = "text/plain;charset=UTF-8") public @ResponseBody String testJdbcTemplate() { return "已注册用户数: " + userDao.getUserCounts(); }
@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()方法的实现:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
}
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; }
    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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!-- 配置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>
<!-- 配置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>
    <!-- 配置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注解:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@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;
@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;
    @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中定义的关于事务的配置

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<!-- 配置hibernate的事务管理器,这个有什么用? -->
<beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<beans:property name="sessionFactory" ref="sessionFactory" />
</beans:bean>
<!-- 启动基于注解的事务 -->
<tx:annotation-driven />
<!-- 配置hibernate的事务管理器,这个有什么用? --> <beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <beans:property name="sessionFactory" ref="sessionFactory" /> </beans:bean> <!-- 启动基于注解的事务 --> <tx:annotation-driven />
    <!-- 配置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 Comment

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