在写之前,先说明一下在Eclipse中安装Hibernate的插件,方便生成cfg文件和hbm文件。

在help->Install New SoftWare中,添加地址 http://download.jboss.org/jbosstools/updates/stable/helios/

在All Jboss tools下找到Hibernate tools进行安装即可。

一、实现功能思路

Hibernate在之前实习时,公司的项目有用到过,不过当时是直接使用的,用Hibernate的事务进行管理的,并没有在Spring中整合使用。Spring和Hibernate整合的目的在于,让Spring使用Hibernate的sessionFactory,并让Spring用声明式事务来管理Hibernate数据库操作。

总体功能:使用Spring声明式事务管理Hibernate,实现对购买图书的操作。

实体对象方面:

1、实现实体类Book,Account。分别表示图书类,账户类。以及对应的Hibernate映射文件。

2、图书商店的Dao层:BookShopDao接口和BookShopDaoImpl实现。
3、图书商店的Service层:BookShopService接口和BookShopServiceImpl实现。


配置方面:

1、配置数据源和Hibernate属性,并使Hibernate的数据库连接交给数据源来管理,这里使用C3P0数据(可以相应的替换为DBCP、Druid)

2、在Spring bean配置中,配置sessionFactory,设置Hibernate的相应属性和cfg、hbm文件路径。

3、配置Spring的事务管理器,需要加入sessionFactory引用。

4、配置事务的属性,即通知或行为。

5、配置事务切点, 并把切点和事务属性关联起来,此处是利用AOP实现。 


除外还有依赖包配置,可直接参考后面的pom文件。要知道到底某个功能需要哪些包,其实也蛮麻烦的,因为有的包还依赖其他的包的功能,因此,用maven可以直接申明需要的最终的包,会自动下载最终的包需要的其它包。

二、具体实现


对上面的步骤逐一实现:


2.1实体对象方面

1、Book类和Account类:


package com.yefeng.spring.spring5;

public class Account {
	private int id;
	private String name;
	private int money;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}	
}
package com.yefeng.spring.spring5;

public class Book {
	private int id;
	private String bookname;
	private int isbn;
	private int price;
	private int number;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getBookname() {
		return bookname;
	}
	public void setBookname(String bookname) {
		this.bookname = bookname;
	}
	public int getIsbn() {
		return isbn;
	}
	public void setIsbn(int isbn) {
		this.isbn = isbn;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public int getNumber() {
		return number;
	}
	public void setNumber(int number) {
		this.number = number;
	}	
}

生成对应的hbm文件时,因为我安装了eclipse的Hibernate的插件,直接选择Book类和Account类,右键New选择other,找到Hibernate下的Hibernate XML Mapping file即可自动生成。其中可能需要修改的是Id,生成的assigned,将其改为native自动生成,还有table名字可以修改,

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-5 19:42:19 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.yefeng.spring.spring5.Account" table="ACCOUNT">
        <id name="id" type="int">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="money" type="int">
            <column name="MONEY" />
        </property>
    </class>
</hibernate-mapping>


2、Dao层

BookShopDao接口:

package com.yefeng.spring.dao;

public interface BookShopDao {

	public int findBookPriceByIsbn(int isbn);
	
	public void updateBookStock(int isbn);
	
	public void updateUserAccount(String username, int price);

	public int findBookStockByIsbn(int isbn);

	public int findAccountMoneyByName(String name);
}

实现:

package com.yefeng.spring.dao.impl;

import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.yefeng.spring.dao.BookShopDao;

@Repository
public class BookShopDaoImpl implements BookShopDao {

	@Autowired
	private SessionFactory sessionFactory;

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	@Override
	public int findBookPriceByIsbn(int isbn) {
		String hql = "select b.price from Book b where b.isbn = ?";
		// setString 被废除,换成setParameter
		// int price = getSession().createQuery(hql).setString(0,isbn).getFirstResult();
		Query query = getSession().createQuery(hql).setParameter(0, isbn);
		List<Integer> price = query.getResultList();
		return price.get(0);
	}
	@Override
	public int findBookStockByIsbn(int isbn) {
		String hql = "select b.number from Book b where b.isbn = ?";
		Query query = getSession().createQuery(hql).setParameter(0, isbn);
		List<Integer> number = query.getResultList();
		return number.get(0);
	}
	@Override
	public int findAccountMoneyByName(String name){
		String hql = "select a.money from Account a where a.name = ?";
		Query query = getSession().createQuery(hql).setParameter(0, name);
		List<Integer> number = query.getResultList();
		return number.get(0);
	}
	@Override
	public void updateBookStock(int isbn) {
		String hql = "update Book b set b.number = b.number - 1  where b.isbn = ?";
		getSession().createQuery(hql).setParameter(0, isbn).executeUpdate();
	}
	@Override
	public void updateUserAccount(String username, int price) {
		String hql = "update Account a set a.money = a.money - ? where a.name = ?";
		getSession().createQuery(hql).setParameter(0, price).setParameter(1, username).executeUpdate();
	}
}





3、Service层:

BookShopService接口:

package com.yefeng.service;

public interface BookShopService {
	
	public void purchase(String username, int isbn);
	
}

实现:

package com.yefeng.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.yefeng.service.BookShopService;
import com.yefeng.spring.dao.BookShopDao;

@Repository
public class BookShopServiceImpl implements BookShopService {
	@Autowired
	private BookShopDao bookShopDao;
	
	@Override
	public void purchase(String username, int isbn) {
		// TODO Auto-generated method stub
		int price = bookShopDao.findBookPriceByIsbn(isbn);
		bookShopDao.updateBookStock(isbn);
		bookShopDao.updateUserAccount(username, price);
	}
}



2.2配置方面

1、mysql连接属性:

user=root
password=
#old driver 
#driverClass=com.mysql.jdbc.Driver
#new driver is as follow:
driverClass=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/spring?serverTimezone=UTC

initPoolSize=5
maxPoolSize=10


2、配置数据源:从上面的属性文件中获取属性值,方便修改

<!-- 读取数据源配置 -->
	<context:property-placeholder location="classpath:mysql.properties" />
	<!-- 数据源bean -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${user}"></property>
		<property name="password" value="${password}"></property>
		<property name="driverClass" value="${driverClass}"></property>
		<property name="jdbcUrl" value="${jdbcUrl}"></property>

		<property name="initialPoolSize" value="${initPoolSize}"></property>
		<property name="maxPoolSize" value="${maxPoolSize}"></property>
	</bean>


3、配置Hibernate.cfg.xml:数据源直接从dataSource获取,因此此文件只定义方言,sql显示和建表策略以及缓存(此处未定义)相关的配置。

<?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>
    	<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.temp.use_jdbc_metadata_defaults">false</property>
    	<property name="hibernate.hbm2ddl.auto">update</property>
    </session-factory>
</hibernate-configuration>


4、在Spring中配置Hibernate的sessionFactory:

<bean id="sessionFactory"
		class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
		<property name="mappingLocations">
			<list>
				<value>classpath:com/yefeng/spring/spring5/Account.hbm.xml</value>
				<value>classpath:com/yefeng/spring/spring5/Book.hbm.xml</value>
			</list>
		</property>
		<!-- <property name="mappingLocations" value="classpath*:com/yefeng/spring/spring5/*.hbm.xml"></property> -->
		<!-- <property name="mappingLocations" value="classpath:/com/yefeng/spring/spring5/*.hbm.xml"></property> -->
	</bean>

以上

sessionFactory是从文件中获取cfg的属性,也可以直接在此文件定义,即使用Props来定义。


5、配置事务管理器:用的Hibernate5。

<bean id="transactionManager"
		class="org.springframework.orm.hibernate5.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>


6、配置事务的属性:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>


7、配置事务的切点:此处Dao和Service层都申明了事务,主要是测试Dao需要Dao的事务。测试后可以去掉。

<aop:config>
		<aop:pointcut id="txPointcut"
			expression="execution(* com.yefeng.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
	</aop:config>
	<aop:config>
		<aop:pointcut id="txPointcut2"
			expression="execution(* com.yefeng.spring.dao.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut2" />
	</aop:config>


2.3依赖包方面

pom文件内容如下,其中比较容易忽略的是aspectjweaver,一开始因为没有这个包出错.

<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>5.2.1.Final</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-annotations -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-annotations</artifactId>
			<version>3.5.6-Final</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
		<dependency>
			<groupId>c3p0</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.1.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>6.0.3</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>4.3.1.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.jboss.spec.javax.transaction/jboss-transaction-api_1.1_spec -->
		<dependency>
			<groupId>org.jboss.spec.javax.transaction</groupId>
			<artifactId>jboss-transaction-api_1.1_spec</artifactId>
			<version>1.0.1.Final</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>4.3.1.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.9</version>
		</dependency>
	</dependencies>



三、测试


新建test类,对Dao层和Service层每个方法进行测试。有的测试需要依赖其他测试成功才可以执行,比如update和purchase,需要依赖其他方法,测试之间有了相互依赖;或者直接不依赖其他方法,测试时到数据库查看结果,这样的结果是测试没实现自动化。除了这两个,暂时没想到其他的方法。


package com.yefeng.spring.test;

import static org.junit.Assert.*;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.yefeng.service.BookShopService;
import com.yefeng.spring.dao.BookShopDao;

public class SpringHibernateTest{
	private ApplicationContext context = null;
	private BookShopDao bookShopDao = null;
	private BookShopService bookShopService =null;
	{
		context = new ClassPathXmlApplicationContext("appicationContext.xml");
		bookShopDao = context.getBean(BookShopDao.class);
		bookShopService = context.getBean(BookShopService.class);
	}
	//测试数据源连接
	@Test
	public void testDataSource() throws SQLException {
		DataSource dataSource = (DataSource) context.getBean("dataSource");
		System.out.println(dataSource.getConnection());
	}
	//测试Dao层的五个方法
	@Test
	public void testFindBookPriceByIsbn() {
		int price = bookShopDao.findBookPriceByIsbn(1001);
		assertEquals(price,100);
	}	
	@Test
	public void testFindBookStockByIsbn(){
		int number = bookShopDao.findBookStockByIsbn(1001);
		assertEquals(number,8);
	}
	@Test
	public void testUpdateBookStock() {
		int isbn=1001;
		int number=bookShopDao.findBookStockByIsbn(isbn);
		bookShopDao.updateBookStock(isbn);
		int number2=bookShopDao.findBookStockByIsbn(isbn);
		assertEquals(number,number2+1);
	}
	@Test
	public void TestFindAccountMoneyByName() {
		int money = bookShopDao.findAccountMoneyByName("aa");
		assertEquals(money,400);
	}
	@Test
	public void testUpdateUserAccount() {
		String name="aa";
		int money = bookShopDao.findAccountMoneyByName("aa");
		bookShopDao.updateUserAccount(name, 30);
		int money2 = bookShopDao.findAccountMoneyByName("aa");
		assertEquals(money,money2+30);
	}	
	//测试Service层的一个方法
	@Test
	public void bookShopSevrviceTest(){
		int isbn=1001;
		String name = "aa";
		int number = bookShopDao.findBookStockByIsbn(isbn);
		int price = bookShopDao.findBookPriceByIsbn(isbn);
		int money = bookShopDao.findAccountMoneyByName(name);
		bookShopService.purchase(name, isbn);
		int number2 = bookShopDao.findBookStockByIsbn(isbn);
		int money2 = bookShopDao.findAccountMoneyByName(name);
		assertEquals(number,number2+1);
		assertEquals(money,money2+price);
	}
}