Spring与JDBC集合以及事物处理
2014-06-13 19:27
363 查看
除了我们常见的SSH框架整合,Spring还可以和JDBC整合,Spring容器提供专门针对JDBC操作的辅助类:JdbcTemplate,需要使用注入的方式给JDBC辅助类注入数据源。
事物的配置有两种,一种是通过注解的方式,另外一种是通过XML文件配置的方式。
@Transactional(类型=值)
1.事物超时设置:
@Transactional(timeout=30)//默认是30秒
2.事物隔离级别:
@Transactional(isolation=Isolation.READ_UNCOMMITTED)//读取未提交的数据(会出现脏读,不可重复读)基本不使用。
@Transactional(isolation=Isolation.READ_COMMITTED)读取已提交数据(出现不可重复读和幻读)。
@Transactional(isolation=Isolation.REPEATABLE_READ)可重复读(会出现幻读)。
@Transactional(isolation=Isolation.SERIALIZABLE)串行化。
注:
Mysql:默认为REPEATABLE_READ级别。
SQLSERVE:默认为READ_COMMITTED。
脏读:一个事物读取到另外一个事物未提交的更新数据。
不可重复读:在同一事物中,多次提取统一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事物已提交的更新数据,相反,"可重复读"即在同一事物中多次读取数据时,能够保证所读数据一样,也就是说,后续读取不能读到另一事物已提交的更新数据。
幻读:一个事物读取到另一事物已提交的insert数据。
事物的传播行为:
1.@Transactional(propagation=Propagation.REQUIRED)如果有事物,那么加入事物,没有的话就新建一个(默认情况下)
2.@Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED):容器不为这个方法开启事物(这个非常实用,因为查询方法是不用开启事物的,我们可以声依永这个注解)。
3.@Transactional(propagation=Propagation.REQUIRES_NEW):不管是否存在事物,都创建一个新的事物,原来的挂起,新的执行完毕之后,继续执行老的事物。
4.@Transactional(propagation=Propagation.MANDATORY):必须在一个已有的事物中进行,否则抛出异常。
5.@Transactional(propagation=Propagation.NEVER):必须在一个没有的事物中执行,否则抛出异常(与Propagation.MANDATORY相反)。
6.@Transactional(propagation=Propagation.SUPPORTS):如果其他bean调用这个方法,在其他bean中声明事物,那就用事物,如果其他bean没有声明事物,那就不用事物。
下面我们通过一个Spring和JDBC整合的案例来深入探究!
项目结构:
数据访问层接口:
数据访问层接口实现类:
业务逻辑层接口:
业务逻辑层接口实现类:
控制层:
获取bean的工具类:
beans.xml文件中配置事物(通过注解)和各种参数:
注:上述配置是通过注解的方式来配置事物的。
除了通过注解的方式配置事物,Spring还提供使用XML的方式配置事物:
测试类:
通过上面的XML形式也可以配置事物,这样配置以后,我们执行我们的测试方法,会发现有问题,如下图:
问题:Connection is read-only. Queries leading to data modification are not allowed
原因:[b]出现这个问题的原因是我们在Service实现类中的添加方法的方法名是add,而我们配置事物时明确指定了添加方法必须以save或者saveOrUpdate开头。其他方法都没有开启事物,是只读的。[/b]
解决:解决方法也很简单那就是把我们Service实现类中的add方法名改为以save或者saveOrUpdate开头即可。
事物的配置有两种,一种是通过注解的方式,另外一种是通过XML文件配置的方式。
@Transactional(类型=值)
1.事物超时设置:
@Transactional(timeout=30)//默认是30秒
2.事物隔离级别:
@Transactional(isolation=Isolation.READ_UNCOMMITTED)//读取未提交的数据(会出现脏读,不可重复读)基本不使用。
@Transactional(isolation=Isolation.READ_COMMITTED)读取已提交数据(出现不可重复读和幻读)。
@Transactional(isolation=Isolation.REPEATABLE_READ)可重复读(会出现幻读)。
@Transactional(isolation=Isolation.SERIALIZABLE)串行化。
注:
Mysql:默认为REPEATABLE_READ级别。
SQLSERVE:默认为READ_COMMITTED。
脏读:一个事物读取到另外一个事物未提交的更新数据。
不可重复读:在同一事物中,多次提取统一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事物已提交的更新数据,相反,"可重复读"即在同一事物中多次读取数据时,能够保证所读数据一样,也就是说,后续读取不能读到另一事物已提交的更新数据。
幻读:一个事物读取到另一事物已提交的insert数据。
事物的传播行为:
1.@Transactional(propagation=Propagation.REQUIRED)如果有事物,那么加入事物,没有的话就新建一个(默认情况下)
2.@Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED):容器不为这个方法开启事物(这个非常实用,因为查询方法是不用开启事物的,我们可以声依永这个注解)。
3.@Transactional(propagation=Propagation.REQUIRES_NEW):不管是否存在事物,都创建一个新的事物,原来的挂起,新的执行完毕之后,继续执行老的事物。
4.@Transactional(propagation=Propagation.MANDATORY):必须在一个已有的事物中进行,否则抛出异常。
5.@Transactional(propagation=Propagation.NEVER):必须在一个没有的事物中执行,否则抛出异常(与Propagation.MANDATORY相反)。
6.@Transactional(propagation=Propagation.SUPPORTS):如果其他bean调用这个方法,在其他bean中声明事物,那就用事物,如果其他bean没有声明事物,那就不用事物。
下面我们通过一个Spring和JDBC整合的案例来深入探究!
项目结构:
数据访问层接口:
/** * 数据操作层接口 * @author Liao * */ public interface IUserDao { /** * 添加用户 * @param user */ public void add(User user); /** * 根据用户查找id * @param id 用户id * @return */ public User findById(int id); /** * 查找所有用户 * @return */ public List<User> findAllUser(); }
数据访问层接口实现类:
@Repository public class UserDaoImpl implements IUserDao { @Resource//注入jdbc操作模板 private JdbcTemplate jdbc; @Override public void add(User user) { /*添加用户*/ jdbc.update("insert into t_user(uid,uname) values(?,?)", user.getUid(),user.getUname()); System.out.println("添加用户成功!"); } @Override public User findById(int id) { return jdbc.queryForObject("select * from t_user where uid = ?", new Object[]{id}, new UserRowMapper()); } @Override public List<User> findAllUser() { return jdbc.query("select * from t_user", new UserRowMapper()); } }注:在数据操作层接口实现类中注入由Spring提供的专门针对JDBC的辅助类。
业务逻辑层接口:
/** * 业务逻辑层接口 * @author Liao * */ public interface IUserService { /** * 添加用户 * @param user */ public void add(User user); /** * 根据用户查找id * @param id 用户id * @return */ public User findById(int id); /** * 查找所有用户 * @return */ public List<User> findAllUser(); }
业务逻辑层接口实现类:
@Service @Transactional public class UserServiceImpl implements IUserService { @Resource//注入Dao对象 private IUserDao userDao; @Override public void add(User user) { /*添加用户*/ userDao.add(user); } @Override @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public User findById(int id) { /*根据id查询用户*/ return userDao.findById(id); } @Override @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public List<User> findAllUser() { /*查询所有用户 */ return userDao.findAllUser(); } }注:在业务接口实现类中配置事物,我们在类上面注解了@Transactional,表示当前类下所有的的方法都要开启事物,但是我们在findById()和findAllUser()这两个方法中又使用@Transactional,注意我们在方法上的注解表示不开启事物为只读。因为这两个方法是查询的,不需要开启事物,所以使用注解的方式非常灵活,可以随心所欲的配置。
控制层:
@Controller public class UserAction { @Resource//注入Service private IUserService userService; /** * 添加用户 */ public void add(){ User user = new User(3, "李克強"); /*通过业务逻辑对象进行添加*/ userService.add(user); } /** * 根据id查询用户 * @param id */ public void findById(int id){ /*查询*/ User user = userService.findById(id); System.out.println(user.getUname()); } /** * 查询所有用户 */ public void findAllUser(){ List<User> users = userService.findAllUser(); System.out.println(users.size()); } }注:在UserAction中注入Service(通过注解的形式注入)。
获取bean的工具类:
/** * 获取bean的工具类 * @author Liao * */ public class BeanUtils { private static AbstractApplicationContext app; static { app = new ClassPathXmlApplicationContext("beans.xml"); } /** * 获取bean * @param name * @param clazz * @return */ public static <T> T getBean(String name,Class<T> clazz){ return app.getBean(name, clazz); } }
beans.xml文件中配置事物(通过注解)和各种参数:
<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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 配置AOP切面 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 配置Spring自動掃面的包 --> <context:component-scan base-package="com.lixue.dao.impl,com.lixue.service.impl,com.lixue.action" /> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/spring_jdbc" /> <property name="username" value="root" /> <property name="password" value="134045" /> <!-- 连接池启动时的初始值 --> <property name="initialSize" value="1" /> <!-- 连接池的最大值 --> <property name="maxActive" value="500" /> <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --> <property name="maxIdle" value="2" /> <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 --> <property name="minIdle" value="1" /> </bean> <!-- 配置spring提供的jdbc操作类,并通过构造方法的方式注入数据源 --> <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource"></constructor-arg> </bean> <!-- 配置事物管理器,并注入數據源 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 采用@Transaction注解的方式使用事物 --> <tx:annotation-driven transaction-manager="txManager" /> </beans>
注:上述配置是通过注解的方式来配置事物的。
除了通过注解的方式配置事物,Spring还提供使用XML的方式配置事物:
<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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 配置AOP切面 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 配置Spring自動掃面的包 --> <context:component-scan base-package="com.lixue.dao.impl,com.lixue.service.impl,com.lixue.action" /> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/spring_jdbc" /> <property name="username" value="root" /> <property name="password" value="134045" /> <!-- 连接池启动时的初始值 --> <property name="initialSize" value="1" /> <!-- 连接池的最大值 --> <property name="maxActive" value="500" /> <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --> <property name="maxIdle" value="2" /> <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 --> <property name="minIdle" value="1" /> </bean> <!-- 配置spring提供的jdbc操作类,并注入数据源 --> <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource"></constructor-arg> </bean> <!-- 配置事物管理器,并注入數據源 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 拦截器方式配置事物 --> <tx:advice id="transactionAdvice" transaction-manager="txManager"> <tx:attributes> <!--被拦截的类中以save开头的方法要使用事物--> <tx:method name="save*" propagation="REQUIRED" /> <!--被拦截的类中以update开头的方法要使用事物--> <tx:method name="update*" propagation="REQUIRED" /> <!--被拦截的类中以saveOrUpdate开头的方法要使用事物--> <tx:method name="saveOrUpdate*" propagation="REQUIRED" /> <!--被拦截的类中以delete开头的方法要使用事物--> <tx:method name="delete*" propagation="REQUIRED" /> <!--被拦截的类中以grant开头的方法要使用事物--> <tx:method name="grant*" propagation="REQUIRED" /> <!--被拦截的类中以init开头的方法要使用事物--> <tx:method name="init*" propagation="REQUIRED" /> <!--除了上面要配置方法,其他方法一律只读,不使用事物--> <tx:method name="*" propagation="REQUIRED" read-only="true" /> </tx:attributes> </tx:advice> <!-- 通过aop切面配置要拦截的范围 --> <aop:config> <!-- 第一个*代表所有的返回值类型;第二个*代表所有的类;第三个*代表类所有方法;..代表子或者孙子包;最后一个..代表所有的参数 --> <aop:pointcut id="transactionPointcut" expression="(execution(* com.lixue..*Impl.*(..)))" /> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" /> </aop:config> </beans>
测试类:
public class UserTest { public static void main(String[] args) { /*获取Action的bean实例*/ UserAction userAction = BeanUtils.getBean("userAction", UserAction.class); /*添加用户*/ userAction.add(); /*根据id查询用户*/ //userAction.findById(1); /*查询所有用户 */ userAction.findAllUser(); } }
通过上面的XML形式也可以配置事物,这样配置以后,我们执行我们的测试方法,会发现有问题,如下图:
问题:Connection is read-only. Queries leading to data modification are not allowed
原因:[b]出现这个问题的原因是我们在Service实现类中的添加方法的方法名是add,而我们配置事物时明确指定了添加方法必须以save或者saveOrUpdate开头。其他方法都没有开启事物,是只读的。[/b]
解决:解决方法也很简单那就是把我们Service实现类中的add方法名改为以save或者saveOrUpdate开头即可。
相关文章推荐
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理
- Spring+hibernate+mysql事物不回滚的原因以及处理