- spring整合Hibernate需要整合两个方面
- 由IOC容器来生成Hibernate的SessionFactory
- 让Hibernate使用上Spring的声明式事物
- 整合步骤
- 先加入Hibernate
- 加入Hibernate的jar包
- 配置Hibernate的配置文件
- 编写持久化类对应的*.hbm.xml文件
- 再加入Spring
- 加入jar包
- 加入Spring的配置文件
- 配置数据源
- 配置Hibernate的SessionFactory示例
- 配置Spring声明式事务
hibernate.cfg.xml文件配置
- 虽然在整合Spring的时候,这个文件可以不用设置,但是最好还是保留一个hibernat配置文件,有些配置写在这里里面更加合适
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--数据库链接源由Spring4完成配置-->
<!--Hibernate的映射文件 *.hbm.xml文件也由在配置SessionFactory时配置-->
<!--配置Hibernate的基本操作-->
<!--配置Sql方言-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!--设置自动生成数据表策略-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!--打印sql语句-->
<property name="hibernate.show_sql">true</property>
<!--格式化sql语句-->
<property name="hibernate.format_sql">true</property>
</session-factory>
</hibernate-configuration>
-
准备数据表
-
account描述用户信息
- book,描述书籍信息
- book_stock描述书的库存信息
- 准备出数据表的持续化类和对应的hibernate映射文件
Spring配置文件设置
- 定义数据库连接信息,设置db…properties文件
jdbc.user=root
jdbc.password=mysqladmin
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/maoshu
- 定义Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--读取资源文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置数据源-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
<!--配置Hibernate的SessionFactory实例:通过Spring的LocalhostSessionFactory配置-->
<!--要根据Hibernate的开发包版本选择对应的类-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--配置Hibernate的配置文件-->
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<!--配置Hibernate映射文件名称以及位置-->
<!--"*" 表示通配符,匹配所有的*.hbm.xml文件-->
<property name="mappingLocations" value="classpath:mao/shu/vo/*.hbm.xml"/>
</bean>
<!--配置Spring的声明式事物-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!--配置事务属性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get" read-only="true"/>
<tx:method name="find" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务切点,并将事务属性于切点关联起来-->
<aop:config proxy-target-class="true">
<aop:pointcut id="txPointcut" expression="execution(* mao.shu.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
测试代码
- 编写DAO层
- IBookShopDao接口
package mao.shu.dao;
public interface IBookShopDao{
/**
* 根据书的编号查询数据价格
* @param isban 书的编号
* @return
*/
public double findPriceByisban(Integer isban);
/**
* 更新书的库存
* @param isbn 书的编号
* @param number 更新的数量
* @return
*/
public boolean updateBookStock(Integer isbn, Integer number);
/**
* 更新账户的余额
* @param uid 账户编号
* @param consumption 消费金额
* @return
*/
public boolean updateAccount(Integer uid, Double consumption);
}
- BookShopDaoimpl实现BookShopDao接口
- DAO层中使用SessionFactory获取当前线程绑定的Session
- 不建议使用HibernateTemplate和HibernateSupport这样会使DAO和Spring的API耦合.可移植性变差
package mao.shu.dao.impl;
import mao.shu.dao.IBookShopDao;
import mao.shu.util.BookStockException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class BookShopDaoImpl implements IBookShopDao {
@Autowired
private SessionFactory sessionFactory;
public Session getSession(){
return this.sessionFactory.getCurrentSession();
}
@Override
public double findPriceByisban(Integer isbn) {
String hql = "SELECT b.price FROM Book b WHERE b.isbn = ?";
Query query = this.getSession().createQuery(hql).setInteger(0,isbn);
Double price = (Double) query.uniqueResult();
return price;
}
@Override
public boolean updateBookStock(Integer isbn, Integer number) {
int stock = this.getStockByIsbn(isbn);
if(stock == 0){
throw new BookStockException("库存不足");
}
String hql = "UPDATE BookStock b SET b.stock = b.stock+? WHERE b.isbn=?";
int result = this.getSession().createQuery(hql).setInteger(0,number).setInteger(1,isbn).executeUpdate();
return result > 0;
}
@Override
public boolean updateAccount(Integer uid, Double consumption) {
if(this.getBalanceByUid(uid) < consumption){
throw new BookStockException("余额不足");
}
String hql = "UPDATE Account a SET a.balance = a.balance-? WHERE a.uid=?";
int result = this.getSession().createQuery(hql).setDouble(0,consumption).setInteger(1,uid).executeUpdate();
return result > 0;
}
/**
* 通过书的编号查询书的库存
* @param isbn
* @return
*/
public Integer getStockByIsbn(Integer isbn){
String hql = "SELECT b.stock FROM BookStock b WHERE b.isbn = ?";
Query query = this.getSession().createQuery(hql).setInteger(0,isbn);
Integer stock = (Integer) query.uniqueResult();
return stock;
}
/**
* 查询指定的账户余额
* @param uid
* @return
*/
public Double getBalanceByUid(Integer uid){
String hql = "SELECT a.balance FROM Account a WHERE a.uid= ?";
Query query = this.getSession().createQuery(hql).setInteger(0,uid);
Double balance = (Double) query.uniqueResult();
return balance;
}
}
- 定义Service层接口
- ShopService
- Cashier
package mao.shu.service;
public interface ShopService {
/**
* 账户消费操作
* @param uid 账户id
* @param isbn 购买的书本编号
* @return 成功返回true,否则返回false;
*/
public boolean purchase(Integer uid, Integer isbn);
}
package mao.shu.service;
import java.util.List;
public interface Cashier {
/**
* 进行顾客结账操作,
* @param uid 账户id
* @param isbns 书本编号集合
* @return
*/
public boolean checkout(Integer uid, List<Integer> isbns);
}
- 定义Service接口的实现子类
- ShopServiceImpl
- CashierImpl
package mao.shu.service.impl;
import mao.shu.dao.IBookShopDao;
import mao.shu.dao.impl.BookShopDaoImpl;
import mao.shu.service.ShopService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ShopServiceImpl implements ShopService {
@Autowired
private BookShopDaoImpl bookShopDao;
@Override
public boolean purchase(Integer uid, Integer isbn) {
double bookPrice = this.bookShopDao.findPriceByisban(isbn);
if(!(this.validateAccount(uid,bookPrice)&& this.validateStock(isbn,1))){
return false;
}
if (this.bookShopDao.updateBookStock(isbn,-1)){
return this.bookShopDao.updateAccount(uid,bookPrice);
}
return false;
}
}
package mao.shu.service.impl;
import mao.shu.service.Cashier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CashierImpl implements Cashier {
@Autowired
private ShopServiceImpl shopService;
@Override
public boolean checkout(Integer uid, List<Integer> isbns) {
for (Integer isbn:isbns){
if (!this.shopService.purchase(uid,isbn)){
return false;
}
}
return true;
}
}
- 编写测试类
package mao.shu.service.impl;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
import static org.junit.Assert.*;
public class ShopServiceImplTest {
private ApplicationContext app;
private ShopServiceImpl shopService;
private CashierImpl cashier;
@Before
public void before(){
this.app = new ClassPathXmlApplicationContext("application.xml");
this.shopService = this.app.getBean("shopServiceImpl",ShopServiceImpl.class);
this.cashier = this.app.getBean("cashierImpl",CashierImpl.class);
}
@Test
public void purchase() {
this.shopService.purchase(1,1);
}
@Test
public void checkout(){
this.cashier.checkout(1, Arrays.asList(1,1,1,1));
}
}
- 在使用注解的方式注入Bean实例的时候,需要在Spring的配置文件中添加注解扫描位置
<!--设置Spring注解扫描基包-->
<context:component-scan base-package="mao.shu" />
- 在<tx:advice>标签中定义事务属性
<!--配置事务属性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--定义事务属性-->
<tx:method name="get" read-only="true"/>
<tx:method name="find" read-only="true"/>
<tx:method name="checkout" propagation="REQUIRES_NEW"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
不使用Hibernate配置文件
- Hibernate中的配置可以直接在Spring文件中配置
在配置LocalSessionFactoryBean实例中,使用 <property name=“hibernateProperties”>标签配置Hibernate属性
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--配置Hibernate属性-->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!--配置Hibernate映射文件名称以及位置-->
<!--"*" 表示通配符,匹配所有的*.hbm.xml文件-->
<property name="mappingLocations" value="classpath:mao/shu/vo/*.hbm.xml"/>
</bean>