Spring整合Hibernate

        之前讲过纯粹的Hibernate开发,但是需要手动创建SessionFactory实例。在实际开发中,应该利用Spring容器声明式管理的特点,以配置文件来管理SessionFactory实例。Spring的IoC容器不仅能以声明式的方式配置SessionFactory实例,也可充分利用IoC容器的作用,为SessionFactory注入数据源引用。以下讲解的是Spring整合Hibernate的示例,从而理清整个过程具体步骤。

        我接下来举的例子就是有一家餐厅,假设每个来吃饭的用户都会带一张充值卡来消费,然后根据菜单点菜,点完之后要进行刷卡,如果正常则保存余额,如果菜品不足或者余额不足则会进行提醒。那么从该例子可以看出持久化实体类有菜品和卡账户相关信息。

1.持久化类的创建并完成映射
1.1.持久化类的创建
        何谓持久化类,简单来说就是要映射成数据库的对象,该例子涉及到数据操作的是菜品数量变化,以及消费卡账户余额的变化。所以接下来进行持久化类的创建,分别是Food类和Customer类。
package spring.hibernate.plugin.entity;

public class Food {
	private int id;  
    private String foodName;  
    private String identi;  
    private float price;  
    private int stock;  
  
    public int getId() {  
        return id;  
    }  
  
    public void setId(int id) {  
        this.id = id;  
    }  
  
    public String getFoodName() {  
        return foodName;  
    }  
  
    public void setfoodName(String foodName) {  
        this.foodName = foodName;  
    }  
  
    public String getIdenti() {  
        return identi;  
    }  
  
    public void setIdenti(String identi) {  
        this.identi = identi;  
    }  
  
    public float getPrice() {  
        return price;  
    }  
  
    public void setPrice(float price) {  
        this.price = price;  
    }  
  
    public int getStock() {  
        return stock;  
    }  
  
    public void setStock(int stock) {  
        this.stock = stock;  
    }  
      

}
package spring.hibernate.plugin.entity;

public class Customer {
	private int id;  
    private String customername;  
    private float balance;  
      
    public int getId() {  
        return id;  
    }  
  
    public void setId(int id) {  
        this.id = id;  
    }  
  
    public String getCustomername() {  
        return customername;  
    }  
  
    public void setCustomername(String customer) {  
        this.customername = customer;  
    }  
  
    public float getBalance() {  
        return balance;  
    }  
  
    public void setBalance(float balance) {  
        this.balance = balance;  
    }  

} 
1.2.映射配置--hbm.xml文件
     接下来对持久化类进行映射,这里采用*.hbm.xml文件分别对每个类进行映射配置,配置文件如下:
Food.hbm.xml:
<?xml version="1.0"?>   
<!DOCTYPE hibernate-mapping PUBLIC   
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping>  
    <class name="spring.hibernate.plugin.entity.Food" table="FOOD_MENU">  
        <id name="id" type="int">  
            <column name="ID" />  
            <generator class="native" />  
        </id>  
          
        <property name="foodName" type="java.lang.String">  
            <column name="FOODNAME" />  
        </property>  
          
        <property name="identi" type="java.lang.String">  
            <column name="IDENTI" />  
        </property>  
          
        <property name="price" type="float">  
            <column name="PRICE" />  
        </property>  
          
        <property name="stock" type="int">  
            <column name="STOCK" />  
        </property>  
          
    </class>  
</hibernate-mapping>  
Customer.hbm.xml:
<?xml version="1.0"?>  
<!DOCTYPE hibernate-mapping PUBLIC   
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
<hibernate-mapping>  
    <class name="spring.hibernate.plugin.entity.Customer" table="CUSTOMER">  
        <id name="id" type="int">  
            <column name="ID" />  
            <generator class="native" />  
        </id>  
          
        <property name="customername" type="java.lang.String">  
            <column name="CUSTOMERNAME" />  
        </property>  
          
        <property name="balance" type="float">  
            <column name="BALANCE" />  
        </property>  
          
    </class>  
</hibernate-mapping>  
        其实,配置文件比较简单,根据模板修改即可,主要是对持久化类属性的配置。
1.3.hibernate配置文件-hibernate.cfg.xml
        这里的hibernate配置文件有别于纯粹的hibernate开发,这里只需要一些hibernate基本配置即可,并不需要跟纯粹的hibernate开发那样将hbm.xml文件配置其中,注意,整合开发的hbm.xml是和hibernate.cfg.xml文件一起配置到Spring容器之中的,整合开发将Spring容器的工厂作用发挥的淋漓尽致。以下是hibernate.cfg.xml文件。
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE hibernate-configuration PUBLIC  
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">  
<hibernate-configuration>  
    <session-factory>  
      <!-- 配置hibernate基本信息 -->  
      <!-- 1.数据源配置在IOC容器中,此处不需要额外配置 -->  
      <!-- 2.关联的.hbm.xml文件也在IOC容器配置SessionFactory时配置 -->  
      <!-- 3.此处配置hibernate的基本信息:数据库方言、SQL显示及格式化,及生成数据表的策略,二级缓存等 -->  
      <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>  
        
      <property name="hibernate.show_sql">true</property>  
      <property name="hibernate.format_sql">true</property>  
        
      <property name="hibernate.hbm2ddl.auto">update</property>
      <property name="hibernate.temp.use_jdbc_metadata_defaults">false</property>
      <property name="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</property> 
      
        
    </session-factory>  
</hibernate-configuration> 
        注意:上面配置文件中"hibernate.temp.use_jdbc_metadata_defaults"和"hibernate.current_session_context_class"这两个属性的配置最好添加,在有的环境没有这两个属性则会报错,所以建议一开始就配置好这两个属性。

2.Spring容器的配置文件applicationContext.xml文件


<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"  
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<!-- 配置注解自动扫描的包 -->  
   	<context:component-scan base-package="spring.hibernate.plugin"></context:component-scan>  
	<!-- 定义数据源Bean,使用C3P0数据源实现,并注入数据源的必要信息 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close"
		p:driverClass="org.gjt.mm.mysql.Driver"
		p:jdbcUrl="jdbc:mysql://localhost:3306/spring_hibernate"
		p:user="root"
		p:password="root"
		p:maxPoolSize="40"
		p:minPoolSize="2"
		p:initialPoolSize="2"
		p:maxIdleTime="30"/>
	<!-- 定义Hibernate的SessionFactory,SessionFactory需要依赖数据源,注入dataSource -->
	<!-- 配置Hibernate的SessionFactory,通过spring提供的 LocalSessionFactoryBean配置-->  
   <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
       <!-- 配置依赖的数据源属性 -->  
       <property name="dataSource" ref="dataSource"></property>  
       <!-- hibernate 配置文件的路径 -->  
       <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>  
       <!-- 配置hibernate映射文件的路径,可以使用通配符 -->  
       <property name="mappingLocations" value="classpath:spring/hibernate/plugin/entity/*.hbm.xml"></property> 
    
	</bean>
</beans>	
         在纯粹的Hibernate访问中,应用程序需要手动创建SessionFactory,而这里使用的是声明式管理SessionFactory实例,可以把它放进Spring大容器进行管理。目前的applicationContext.xml文件先把持久类配置文件现装入其中,然后将管理Hibernate的SessionFactory实例化,而SessionFactory需要传入数据源,所以在这之前先将数据源进行Bean实例化,并对数据源进行参数设置。这样SessionFactory实例就放入了Spring大容器中进行管理了。

3.DAO组件的基类与实现类以及相应的异常类

3.1.DAO组件的基类与实现类
       其实,DAO组件就是一种对数据库进行CRUD方法提取的基类。这里包含了各种操作方法,为了后面传给Service层。
package spring.hibernate.plugin.dao;

public interface RestaurantDao {
	/** 
     * 根据菜单上的菜品号获取食物价格 
     */  
    public float  findPriceByIdenti(String identi);  
      
    /** 
     * 更新书的库存,使菜品号对应的书本减少n份 
     *  
     */  
    public void updateFoodStock(String identi,int n);  
      
    /** 
     * 更新账户余额,使顾客所带钱减少 price*n 
     */  
    public void updateAccount(String customername,float price,int n);  

}
package spring.hibernate.plugin.dao.impl;
import org.hibernate.Query;  
import org.hibernate.Session;  
import org.hibernate.SessionFactory;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Repository;  
import spring.hibernate.plugin.dao.RestaurantDao;
import spring.hibernate.plugin.exception.*;
/**
 * 用hql进行数据库操作
 * @author carson0408
 *
 */
//dao组件用@Repository注释
@Repository
public class RestaurantDaoImpl implements RestaurantDao {
	//自动装配,省去了setter方法
	@Autowired  
    private SessionFactory sessionfactory;  
      
    /**
     * 连接当前线程,使用SessionFactory的getCurrentSession()方法。
     * @return
     */
    private Session getSession(){  
        return sessionfactory.openSession();    
    }  
      
	@Override
	public float findPriceByIdenti(String identi) {
		// TODO Auto-generated method stub
		String hql="select f.price from Food f where f.identi=:a";  
        float price=(float) getSession().createQuery(hql).setString("a", identi).uniqueResult();  
        return price;  
	}

	@Override
	public void updateFoodStock(String identi, int n) {
		// TODO Auto-generated method stub
		String hq="Select f.stock from Food f where f.identi=:identi";  
        int stock=(int) getSession().createQuery(hq).setString("identi", identi).uniqueResult();  
        if(stock < n){  
             throw new OutofStockException("so sorry,this food is sold out");  
        }     
        String hql="update Food f set f.stock=f.stock-:a where f.identi=:b";  
        getSession().createQuery(hql).setInteger("a", n).setString("b", identi).executeUpdate();  //更新事物剩余量    

	}

	@Override
	public void updateAccount(String customername, float price, int n) {
		// TODO Auto-generated method stub
		//验证客户所持有的饭店支付卡余额是否足够 
		float cost=n*price;
        String hql="select c.balance from Customer c where c.customername=:a";  

        float balance=(float) getSession().createQuery(hql).setString("a", customername).uniqueResult();  
        if(balance < cost){  
            throw new OutofBalanceException("sorry,your card's money is not enough to pay for this food");  
        }  
        String hql1="update Customer c set c.balance=c.balance-:a where c.customername=:b";  
        getSession().createQuery(hql1).setFloat("a", cost).setString("b", customername).executeUpdate();  
    }  

	

}



        由上DAO实现类可知,对数据库进行操作需要用到Session,所以需要开启Session,在applicationContext.xml中已经将SessionFactory实例化,说明已经在Spring中管理,所以利用Spring的自动装配,用@Autowired进行修饰SessionFactory,相当于setter方法,这里利用了自动装配可以省略setter方法。然后利用SessionFactory的openSession获取Session,然后进行操作。这里的操作都使用hql查询。这里的DAO类用@Repository注释。
3.2.异常类
        上面DAO实现类中出现了异常,以下是DAO组件中用到的异常类:
package spring.hibernate.plugin.exception;

public class OutofStockException extends RuntimeException {

	public OutofStockException() {
		// TODO Auto-generated constructor stub
	}

	public OutofStockException(String arg0) {
		super(arg0);
		// TODO Auto-generated constructor stub
	}

}
package spring.hibernate.plugin.exception;

public class OutofBalanceException extends RuntimeException {

	public OutofBalanceException() {
		// TODO Auto-generated constructor stub
	}

	public OutofBalanceException(String message) {
		super(message);
		// TODO Auto-generated constructor stub
	}

	
}
        以上两个异常分别是菜品不足和余额不足,对应的是菜品对象以及客户对象。



4.Service层

        Service层主要是针对具体功能进行实际操作,比如进行实际的点菜消费等等。这里主要是指消费功能。
package spring.hibernate.plugin.service;

public interface RestaurantService {
	
	public void cost(String username,String identi,int n);

}
实现Service:
package spring.hibernate.plugin.service.impl;

import spring.hibernate.plugin.service.RestaurantService;
import spring.hibernate.plugin.dao.*;
import org.springframework.stereotype.*; 
import org.springframework.beans.factory.annotation.Autowired;  
@Service
public class RestaurantServiceImpl implements RestaurantService {
	
	
	@Autowired
	private RestaurantDao restaurantDao;

	@Override
	public void cost(String username, String identi, int n) {
		// TODO Auto-generated method stub
		float price=restaurantDao.findPriceByIdenti(identi);  
		restaurantDao.updateFoodStock(identi, n);  
		restaurantDao.updateAccount(username, price, n); 

	}

}


5.事务配置以及AOP配置

        对其进行事务配置,比如出现异常之后要进行回滚。并且对每一个操作都进行增强处理,以下是完整的applicationContext.xml文件
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"  
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<!-- 配置注解自动扫描的包 -->  
   	<context:component-scan base-package="spring.hibernate.plugin"></context:component-scan>  
	<!-- 定义数据源Bean,使用C3P0数据源实现,并注入数据源的必要信息 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close"
		p:driverClass="org.gjt.mm.mysql.Driver"
		p:jdbcUrl="jdbc:mysql://localhost:3306/spring_hibernate"
		p:user="root"
		p:password="root"
		p:maxPoolSize="40"
		p:minPoolSize="2"
		p:initialPoolSize="2"
		p:maxIdleTime="30"/>
	<!-- 定义Hibernate的SessionFactory,SessionFactory需要依赖数据源,注入dataSource -->
	<!-- 配置Hibernate的SessionFactory,通过spring提供的 LocalSessionFactoryBean配置-->  
   <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
       <!-- 配置依赖的数据源属性 -->  
       <property name="dataSource" ref="dataSource"></property>  
       <!-- hibernate 配置文件的路径 -->  
       <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>  
       <!-- 配置hibernate映射文件的路径,可以使用通配符 -->  
       <property name="mappingLocations" value="classpath:spring/hibernate/plugin/entity/*.hbm.xml"></property> 
    
	</bean>
	
	
	<!-- 配置Hibernate的局部事务管理器,使用HibernateTransactionManager类 -->
	<!-- 该类是PlatformTransactionManager接口针对采用Hibernate的特定实现类 -->
	<!-- 配置HibernateTransactionManager需依赖注入SessionFactory -->
	<bean id="transactionManager" 
		class="org.springframework.orm.hibernate4.HibernateTransactionManager"
		p:sessionFactory-ref="sessionFactory"/>

	<!-- 配置事务增强处理Bean,指定事务管理器 -->
	<tx:advice id="txAdvice" 
		transaction-manager="transactionManager">
		<!-- 用于配置详细的事务定义 -->
		<tx:attributes>
			<!-- 所有以'get'开头的方法是read-only的 -->
			<tx:method name="get*" read-only="true"/>
			<!-- 其他方法使用默认的事务设置,指定超时时长为5秒 -->
			<tx:method name="*" isolation="DEFAULT"
				propagation="REQUIRED" timeout="5"/>
		</tx:attributes>
	</tx:advice>
	<!-- AOP配置的元素 -->
	<aop:config>
		<!-- 配置一个切入点 -->
		<aop:pointcut id="myPointcut" expression="execution(* com.elgin.spring.hibernate.service.*.*(..))"/>
		<!-- 指定在myPointcut切入点应用txAdvice事务增强处理 -->
		<aop:advisor advice-ref="txAdvice" 
			pointcut-ref="myPointcut"/>
	</aop:config>
	<!-- 启动@AspectJ支持 -->
	<aop:aspectj-autoproxy/>
</beans>
   

6.测试

        以下便是测试代码:
package spring.hibernate.plugin.test;
import spring.hibernate.plugin.service.*;
import org.springframework.beans.factory.annotation.*;  
import org.springframework.stereotype.Repository;  
import org.junit.Test;  
import org.junit.runner.RunWith; 
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;  
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration("classpath:applicationContext.xml")  
public class PluginTest {
	@Autowired  
    private RestaurantService restaurantService;  
      
    @Test  
    public void testCost(){  
    	restaurantService.cost("carson", "1102", 2);  
    }  

}
右键选择Run as->Run configurations然后选择Junit,这里将项目进行Junit测试。测试代码上需要有@RunWith(SpringJUnit4ClassRunner.class)和@ContextConfiguration("classpath:applicationContext.xml")  两个注释,运行之后,刚开始若没有建表,相应的数据库中会生成两个相应的表,同时报错。这是因为没法对空表进行相应操作。所以对food_menu和customer两个表添加数据如下:
数据库spring_hibernate:
                                                
food_menu:
                                           
customer:
                                               

        然后再次进行测试,得到如下结果:

                                            
                                                    

        由上面测试例子可以知道,客户"carson"买了两个菜品号为1102的食物,所以库存变为0,客户余额为60元,底层hibernate对数据库的操作代码如下:
Hibernate: 
    select
        food0_.PRICE as col_0_0_ 
    from
        FOOD_MENU food0_ 
    where
        food0_.IDENTI=?
Hibernate: 
    select
        food0_.STOCK as col_0_0_ 
    from
        FOOD_MENU food0_ 
    where
        food0_.IDENTI=?
Hibernate: 
    update
        FOOD_MENU 
    set
        STOCK=STOCK-? 
    where
        IDENTI=?
Hibernate: 
    select
        customer0_.BALANCE as col_0_0_ 
    from
        CUSTOMER customer0_ 
    where
        customer0_.CUSTOMERNAME=?
Hibernate: 
    update
        CUSTOMER 
    set
        BALANCE=BALANCE-? 
    where
        CUSTOMERNAME=?
        接下来为了检测异常功能,将测试代码更改如下:
package spring.hibernate.plugin.test;
import spring.hibernate.plugin.service.*;
import org.springframework.beans.factory.annotation.*;  
import org.springframework.stereotype.Repository;  
import org.junit.Test;  
import org.junit.runner.RunWith; 
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;  
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration("classpath:applicationContext.xml")  
public class PluginTest {
	@Autowired  
    private RestaurantService restaurantService;  
      
    @Test  
    public void testCost(){  
    	restaurantService.cost("carson", "1101", 2); //1
    	restaurantService.cost("adam", "1102", 2);//2
    }  

}
分别测试1和2,分别得到报错:
spring.hibernate.plugin.exception.OutofBalanceException: sorry,your card's money is not enough to pay for this food和spring.hibernate.plugin.exception.OutofStockException: so sorry,this food is sold out并且当余额不足时不能购买,则余额不会改变,而东西卖完之后报错,也不会改变,说明了事务回滚到之前状态。
有需要完整代码的可以点击下面链接下载:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值