为了对spring开启事务有进一步的了解,下面看这个简单的例子:
1 实体类User.java
package org.lab24.entity; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "User") public class User implements Serializable{ public User(){ } @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
2 抽象dao类,这里设为抽象类主要是为了不让它直接实例化对象(这里这样设计好像有点多余,这个应该是一种设计模式吧):
package org.lab24.dao; import java.util.List; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public abstract class BasicDao extends HibernateDaoSupport{ //这里的返回值,可以直接返回getHibernateTemplate().save(entity)的值,这个会返回新插入记录的键值,也可以如下方式,返回一个持久化到数据库的对象 public Object save(Object entity) throws Exception{ getHibernateTemplate().save(entity); return entity; } public List getAllObjects(Class object)throws Exception{ return getHibernateTemplate().find("from " + object.getName()); } //delete返回的是一个空值,因为记录都删除了,就不会有键值或对象的返回 public void delete(Object entity)throws Exception{ getHibernateTemplate().delete(entity); } }
3 dao实现类:
package org.lab24.dao.impl; import java.util.List; import org.lab24.dao.BasicDao; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /* * @Service用于标注业务层组件, * @Controller用于标注控制层组件(如struts中的action), * @Repository用于标注数据访问组件,即DAO组件, * 而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 */ @Repository("basicDaoService") /* * readOnly表示事务是否只读的,不能进行插入更新操作 * propagation = Propagation.REQUIRED表示执行这个类实例方法需开启事务 * rollbackFor = Throwable.class表示遇到Throwable类及子类(即发生了异常)时事务进行回滚操作 */ @Transactional(readOnly=false , propagation = Propagation.REQUIRED , rollbackFor = Throwable.class) public class BasicDaoImpl extends BasicDao{ @Override public Object save(Object entity) throws Exception { return super.save(entity); } @Override public void delete(Object entity) throws Exception { super.delete(entity); } @Override public List getAllObjects(Class object) throws Exception { List objectList = super.getAllObjects(object); return objectList; } }
4 业务层接口类:
package org.lab24.service; import java.util.List; import org.lab24.entity.User; public interface UserService { public void save(User user)throws Exception; public List getAllUsers()throws Exception; public void deleteUser(User user)throws Exception; }
5 业务层实现类:
package org.lab24.serviceImpl; import java.util.List; import javax.annotation.Resource; import org.lab24.dao.impl.BasicDaoImpl; import org.lab24.entity.User; import org.lab24.service.UserService; import org.springframework.stereotype.Service; @Service("userService")//自己给这个交给spring管理的bean一个名字,若不给,就直接用类的名字第一个字母改为小写而形成bean的名字 public class UserServiceImpl implements UserService{ @Resource BasicDaoImpl basicDaoService;//basicDaoService对应BasicDaoImpl成为数据访问组件的bean名字 public List getAllUsers() throws Exception{ List userList = basicDaoService.getAllObjects(User.class); return userList; } public void save(User user) throws Exception{ basicDaoService.save(user); } public void deleteUser(User user) throws Exception { basicDaoService.delete(user); } }
6 测试类:
package org.lab24.junit.test; import java.util.Iterator; import java.util.List; import javax.annotation.Resource; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.lab24.entity.User; import org.lab24.service.UserService; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration(locations = "classpath:applicationContext.xml") @RunWith(SpringJUnit4ClassRunner.class) public class UserServiceImplTest{ @Resource private UserService userService;//userService对应UserServiceImpl成为服务组件的bean名字 @BeforeClass public static void setUpBeforeClass() throws Exception { } @Test public void testGetAllUsers() { List<User> users = null; try { users = userService.getAllUsers(); } catch (Exception e) { e.printStackTrace(); } Iterator iterator = users.iterator(); User temp; while(iterator.hasNext()){ temp = (User) iterator.next(); System.out.println("ID:" + temp.getId() + " name:" + temp.getName()); } } @Test public void testSave() { User user = new User(); user.setName("333333333"); try { userService.save(user); } catch (Exception e) { e.printStackTrace(); } } }
7 spring配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" 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:ehcache="http://www.springmodules.org/schema/ehcache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd" default-autowire="byName" default-lazy-init="true"> <context:component-scan base-package="org.lab24.dao.impl" /> <context:component-scan base-package="org.lab24.serviceImpl" /> <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true" /> <!-- 缓存管理 --> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>classpath:ehcache.xml</value> </property> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" > <property name="lobHandler" ref="lobHandler" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> <property name="configLocation"> <value>classpath:hibernate.cfg.xml</value> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> <!-- 在此进行自动代理配置 --> <bean id="beanNameAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="proxyTargetClass" value="true" /> <property name="beanNames"> <list> <value>*Impl</value> </list> </property> </bean> </beans>
8 hibernate配置文件:
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD/EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password"></property> <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/wtb</property> <!-- Connection Parameters --> <property name="hibernate.connection.provider_class"> org.hibernate.connection.C3P0ConnectionProvider </property> <!-- Connection Pool --> <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 --> <property name="hibernate.c3p0.acquireIncrement">3</property> <!--两次连接中间隔时间,单位毫秒。Default: 1000 --> <property name="hibernate.c3p0.acquireRetryDelay">1000</property> <!--连接关闭时默认将所有未提交的操作回滚。Default: false --> <property name="hibernate.c3p0.autoCommitOnClose">false</property> <!--连接池中保留的最大连接数。Default: 15 --> <property name="hibernate.c3p0.maxPoolSize">15</property> <property name="hibernate.c3p0.max_size">20</property> <property name="hibernate.c3p0.min_size">5</property> <property name="hibernate.c3p0.timeout">5000</property> <property name="hibernate.c3p0.acquire_increment">5</property> <property name="hibernate.c3p0.idle_test_period">3000</property> <property name="hibernate.c3p0.preferredTestQuery"> select id from test where id=1 </property> <property name="hibernate.c3p0.testConnectionOnCheckin"> true </property> <!-- 启动时删数据库中的表,然后创建,退出时不删除数据表,如果没有下面的属性的话,那么要手动写sql语句来建表,才能进行数据库的操作--> <property name="hbm2ddl.auto">update</property> <!-- 为单向关联(一对一, 多对一)的外连接抓取(outer join fetch)树设置最大深度. 值为0意味着将关闭默认的外连接抓取. --> <property name="hibernate.max_fetch_depth">3</property> <property name="hibernate.order_updates">true</property> <property name="hibernate.jdbc.fetch_size">100</property> <property name="hibernate.jdbc.batch_size">0</property> <property name="hibernate.show_sql">false</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.ejb.naming_strategy"> DefaultComponentSafeNamingStrategy </property> <property name="hibernate.query.substitutions"> true=1, false=0 </property> <property name="hibernate.query.factory_class"> org.hibernate.hql.ast.ASTQueryTranslatorFactory </property> <!-- 2.0 <property name="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</property> --> <property name="hibernate.cglib.use_reflection_optimizer"> false </property> <property name="hiberante.defaultAutoCommit">false</property> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <property name="cache.use_second_level_cache">true</property> <!-- Transaction Configuration --> <property name="hibernate.transaction.factory_class"> org.hibernate.transaction.JDBCTransactionFactory </property> <mapping class="org.lab24.entity.User" /> </session-factory> </hibernate-configuration>
我们可以从dao的实现类里面看到它上面有@transactional标签,它要求执行它的实例化方法时开启事务(propagation
= Propagation.REQUIRED),因为数据库的操作是要以事务的形式来保证数据的一致性的,所以要开启事务,如果有需
要,我们也可以在业务层开启事务,如这里在dao的实现类里开启事务,那么spring在用户调用BasicDaoImpl类的实例对
象方法时就会开启事务,当这个方法结束后,事务注提交,这时数据库才有新的记录(如用插入或更新操作),当我们在
UserServiceImpl类里也配置要求开启事务,那么只有在UserServiceImpl方法结束后,事务才提交,数据库的信息才得
以更新,而不是在BasicDaoImpl类方法调用完了就提交,不过这也是取决以事务配置方式,即propagation设置,可参照
前面博客讲到的事务设置的知识。
补充事务基本知识:
数据库事务概念
什么是数据库事务?
事务(transaction)是由一系列操作序列构成的程序执行单元,这些操作要么都做,要么都不做,是一个不可分割的工
作单位。
数据库事务的四个基本性质(ACID)
1. 原子性(Atomicity)
事务的原子性是指事务中包含的所有操作要么全做,要么全不做(all or none)。
2. 一致性(Consistency)
在事务开始以前,数据库处于一致性的状态,事务结束后,数据库也必须处于一致性状态。
拿银行转账来说,一致性要求事务的执行不应改变A、B 两个账户的金额总和。如果没有这种一致性要求,转账过程中就
会发生钱无中生有,或者不翼而飞的现象。事务应该把数据库从一个一致性状态转换到另外一个一致性状态。
3. 隔离性(Isolation)
事务隔离性要求系统必须保证事务不受其他并发执行的事务的影响,也即要达到这样一种效果:对于任何一对事务T1 和
T2,在事务 T1 看来,T2 要么在 T1 开始之前已经结束,要么在 T1 完成之后才开始执行。这样,每个事务都感觉不到
系统中有其他事务在并发地执行。
4. 持久性(Durability)
一个事务一旦成功完成,它对数据库的改变必须是永久的,即便是在系统遇到故障的情况下也不会丢失。数据的重要性
决定了事务持久性的重要性。