在写之前,先说明一下在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);
}
}