目前项目开发过程中对于Spring的事务管理,主要就这么两种方式:XML配置方式和注解方式
在平时开发中,Spring团队建议使用注解的方式进行配置,这样配置文件显得精简,同时也会做到精确控制。
注解方式
必须包
User.java
package com.javacrazyer.spring.bean;
public class User
{
private int id;
private String username;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
}
这个user在mysql中肯定是要创建的
create table users
(
id int(11) not null auto_increment,
username varchar(20) not null,
primary key (id)
)
UserDAO.java类
package com.javacrazyer.spring.dao;
import java.util.List;
import com.javacrazyer.spring.bean.User;
public interface UserDAO
{
public void save(User user);
public void update(User user);
public User getUser(int id);
public void delete(int id);
public List<User> getAllUsers();
}
UserRowMapper类
package com.javacrazyer.spring.dao.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import com.javacrazyer.spring.bean.User;
public class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();
user.setUsername(rs.getString("username"));
user.setId(rs.getInt("id"));
return user;
}
}
UserDAOImpl.java具体实现类
package com.javacrazyer.spring.dao.impl;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.javacrazyer.spring.bean.User;
import com.javacrazyer.spring.dao.UserDAO;
@Transactional
public class UserDAOImpl implements UserDAO
{
//private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource)
{
//this.dataSource = dataSource;
this.jdbcTemplate=new JdbcTemplate(dataSource);
}
public void delete(int id)
{
jdbcTemplate.update("delete from users where id=?", new Object[]{id},
new int[]{java.sql.Types.INTEGER});
//jdbcTemplate.update("delete from users1 where id=10");
}
public List<User> getAllUsers()
{
return (List<User>)jdbcTemplate.query("select * from users", new UserRowMapper());
}
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public User getUser(int id)
{
return (User)jdbcTemplate.queryForObject("select * from users where id=?", new Object[]{id},
new int[]{java.sql.Types.INTEGER}, new UserRowMapper());
}
public void save(User user)
{
jdbcTemplate.update("insert into users(username) values(?)", new Object[]{user.getUsername()},
new int[]{java.sql.Types.VARCHAR});
}
public void update(User user)
{
jdbcTemplate.update("update users set username=? where id=?", new Object[]{user.getUsername(),user.getId()},
new int[]{java.sql.Types.VARCHAR, java.sql.Types.INTEGER});
}
}
最后的测试类
package com.javacrazyer.spring.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.annotation.Transactional;
import com.javacrazyer.spring.bean.User;
import com.javacrazyer.spring.dao.UserDAO;
@Transactional
public class UserDAOImplTest
{
UserDAO userDAO;
@Before
public void init()
{
try {
ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
userDAO= (UserDAO) cxt.getBean("userDAO");
} catch (RuntimeException e) {
e.printStackTrace();
}
}
@Test
public void testDelete()
{
userDAO.delete(11);
}
@Test
public void testGetAllUsers()
{
for(User user : userDAO.getAllUsers()){
System.out.println(user.getUsername());
}
}
@Test
public void testGetUser()
{
User user = userDAO.getUser(1);
System.out.println(user.getUsername());
}
@Test
public void testSave()
{
for(int i=0; i<5; i++)
{
User user=new User();
user.setUsername("coolszy"+i);
userDAO.save(user);
}
}
@Test
public void testUpdate()
{
User user=userDAO.getUser(1);
user.setUsername("kuka");
userDAO.update(user);
}
}
Spring的applicationContext.xml的配置
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-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">
<!--这里占位符配置可以配置在一个占位符类bean,也可以全局配置一句话就行 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- <bean id="propertyConfigurer"-->
<!-- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">-->
<!-- <property name="location" value="/WEB-INF/jdbc.properties" />-->
<!-- </bean>-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="${initialSize}" />
<!-- 连接池的最大值 -->
<property name="maxActive" value="${maxActive}" />
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="${maxIdle}" />
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="${minIdle}" />
</bean>
<!--spring事务管理器 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 指定数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启对注解的支持 -->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="userDAO" class="com.szy.spring.dao.impl.UserDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
jdbc.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
username=root
password=root
initialSize=1
maxActive=100
maxIdle=2
minIdle=1
在注解方式编写测试代码,代码运行正常。
在我们实现的每个方法中如delete()方法,如果delete方法是这样
public void delete(int id)
{
jdbcTemplate.update("delete from users where id=?", new Object[]{id},
new int[]{java.sql.Types.INTEGER});
jdbcTemplate.update("delete from users where id=?", new Object[]{id},
new int[]{java.sql.Types.INTEGER});
}
这样每条语句都会在各自的事务中执行,并不能保证在同一使用中执行,为了保证在同一事务中执行,我们应使用Spring容器提供的声明事务,我们在UserDAOImpl 类上加入@Transactional,表示该类受Spring事务管理。如果该类中每个方法不需要事务管理,如getUser方法,则在该方法前加入
@Transactional(propagation=Propagation.NOT_SUPPORTED)
XML配置方式
必须包
这里就UserDAOImpl.java,applicationContext.xml与上边不同
UserDAOImpl.java就是去掉了所有@Transactional的注解
applictionContext.xml有稍微大些的变化
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-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">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="${initialSize}"/>
<!-- 连接池的最大值 -->
<property name="maxActive" value="${maxActive}"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="${maxIdle}"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="${minIdle}"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 指定数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<aop:config>
<aop:pointcut id="transactionPointcut" expression="execution(* com.szy.spring.dao.impl..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 如果方法是以get开头,能不进行事务管理 -->
<tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<bean id="userDAO" class="com.szy.spring.dao.impl.UserDAOImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
这里没有了注解支持,而是配成了tx标签开头的声明式事务管理方式,基于aspectJ的配置
下面我们测试下数据库操作是否在同一事务中执行。
假设我们的delete方法如下:
public void delete(int id)
{
jdbcTemplate.update("delete from users where id=?", new Object[]{id},
new int[]{java.sql.Types.INTEGER});
jdbcTemplate.update("delete from users1 where id=10");
}
在第二条删除语句中,users1表是不存在的,如果两次update语句是在两个事务中执行,则第一条能成功执行,并且数据库中该id的记录已经被删除,而第二条由于不存在该表不能正常删除。如果在同一事务中执行,由于第二条update出错,数据库中不能删除任何记录。
测试代码
@Test
public void testDelete()
{
userDAO.delete(5);
}
程序报错,同时id=5的记录没有被删除。如果我们把配置文件中关于事务配置的信息给注释掉,再次测试,程序同样报错,但是id=5的记录被成功删除掉,这说明这两条update语句是在两个不同的事务中运行。
所以配置事务就是保证,无论方法中有多少条更新语句,那么我只要保证整个方法在同一个事务中执行就OK