目录
1.2 加入项目需要使用到的Maven依赖(pom.xml)
1.4.1 商品实体类Goods对应的dao接口和mapper文件
1.4.2 销售记录实体类Sale对应的dao接口和mapper文件
1.8.1 引入外部属性配置文件(jdbc.properties)
1.8.2 声明数据源、SqlSessionFactory对象、读取mapper文件等文件
3 @Transactional注解和AspectJ框架各自特点
1.通过实例来体现事务
1.1通过数据库建两张表
其中 sale 表存放的是销售记录,id表示销售记录的编号,主键,是自动增长的;gid是购买的商品编号;num是购买的商品数量。初始情况下,sale表中无数据。
goods表存放是每种商品的具体信息。id是商品编号,主键;name是商品名称;amount是商品库存;price是商品单价。
1.2 加入项目需要使用到的Maven依赖(pom.xml)
<dependencies>
<!--测试类依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- spring事务的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!-- spring和mybatis集成-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!-- 阿里的连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
1.3 编写实体类(Goods类、Sale类)
package com.liuhaiyang.entity;
public class Goods {
//商品编号,主键
private Integer id;
//商品名称
private String name;
//商品库存
private Integer amount;
//商品单价
private Float price;
//set和get以及tostring、有参构造方法、有参构造方法
}
package com.liuhaiyang.entity;
public class Sale {
//主键
private Integer id;
//购买商品的id
private Integer gid;
//购买商品的数量
private Integer num;
//set和get以及tostring、有参构造方法、有参构造方法
}
1.4 编写dao接口和对应的mapper映射文件
1.4.1 商品实体类Goods对应的dao接口和mapper文件
package com.liuhaiyang.dao;
import com.liuhaiyang.entity.Goods;
public interface GoodsDao {
//根据商品id查询商品
Goods selectById(Integer id);
//参数goods表示本次购买商品的id和购买商品数量
//id商品的id:amount:本次购买的此物品数量
int updateGoods(Goods goods);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liuhaiyang.dao.GoodsDao">
<!--使用insert,uodate,delete,select标签写sql-->
<select id="selectById" resultType="com.liuhaiyang.entity.Goods">
select * from goods where id=#{id}
</select>
<update id="updateGoods">
update goods set amount=amount-#{amount} where id=#{id}
</update>
</mapper>
1.4.2 销售记录实体类Sale对应的dao接口和mapper文件
package com.liuhaiyang.dao;
import com.liuhaiyang.entity.Sale;
public interface SaleDao {
int intsertsale(Sale sale);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liuhaiyang.dao.SaleDao">
<!--使用insert,uodate,delete,select标签写sql-->
<insert id="intsertsale" >
insert into sale(gid,nums) values (#{gid},#{nums})
</insert>
</mapper>
1.5 编写MyBatis主配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 设置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 别名-->
<!-- <typeAliases>-->
<!-- <package name="com.liuhaiyang.entity"/>-->
<!-- </typeAliases>-->
<!--指定其他mapper文件的位置 才能找到其他文件sql语句-->
<mappers>
<!-- <mapper resource="com/lhy/dao/studentDao.xml"/>-->
<package name="com.liuhaiyang.dao"/>
</mappers>
</configuration>
1.6 定义异常类(运行时异常)
这个异常类主要用来处理购买商品时,库存、商品是否存在这些情况下,所发生的异常信息。
package com.liuhaiyang.excetion;
//运行时异常
public class NotException extends RuntimeException{
public NotException() {
super();
}
public NotException(String message) {
super(message);
}
}
1.7 定义Service接口和对应的实现类
package com.liuhaiyang.service;
public interface BuyGoodsService {
// 购买商品 goodsid表示购买商品id num表示购买商品数量
void buyboods(Integer goodsid,Integer num);
}
1.7.1 @Transactional 控制事务
@Transactional 的所有可选属性如下所示:
1. propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为 Propagation.REQUIRED。
2. isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为 Isolation.DEFAULT。
3. readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
4. timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
5. rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
6. rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
7. noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
8.noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public 方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非public 方法上的@Transaction 注解。若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
package com.liuhaiyang.service.impl;
import com.liuhaiyang.dao.GoodsDao;
import com.liuhaiyang.dao.SaleDao;
import com.liuhaiyang.entity.Goods;
import com.liuhaiyang.entity.Sale;
import com.liuhaiyang.excetion.NotException;
import com.liuhaiyang.service.BuyGoodsService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class BuyGoodsServiceImpl implements BuyGoodsService {
private GoodsDao goodsDao=null;
private SaleDao saleDao=null;
public void setGoodsDao(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}
public void setSaleDao(SaleDao saleDao) {
this.saleDao = saleDao;
}
// 事务注解
/*@Transactional 放到public方法的上面,表示方法有事务功能,注意当是private的时候是加不上的,只有在公共事务上时候才能加的上
* 特点: 1) spring框架自己提供的事务
* 2)适合中小型项目-在代码中更改
* 3)使用方便,效率高
*/
/* 第一种方式
@Transactional(
propagation = Propagation.REQUIRED, // 指定传播行为的值 发生运行时异常回滚
isolation = Isolation.DEFAULT, //表示隔离级别
readOnly = false,timeout = 20, // readOnly,是否只读,默认是false timeout表示超时时间
rollbackFor = { NullPointerException.class,NotException.class} //当出现异常,需要回滚
)*/
/* 第二种方式:
@Transactional(
propagation = Propagation.REQUIRED, //表示始终有事务
isolation = Isolation.DEFAULT, //表示隔离级别
readOnly = false,timeout = 20)
rollbackFor的使用:
1)框架首先检查方法抛出的异常是不是在rollbackFor的数组中,如果在一定回滚。
2)如果方法抛出的异常不在rollbackFor数组,框架会继续检查 抛出的异常是不是在RuntimeException。
如果是RuntimeException,一定回滚
在特殊的情况下,会用到第一个 例如,抛出SqlException IOException时,不属于运行时异常可以用到第一种
rollbackFor={SQLException.class,IOException.class}
*/
//第三种方式 @Transactional 表示所有的都是默认值,REQUIRED表示发生异常时回滚 为默认值
@Transactional
@Override
public void buyboods(Integer goodsid, Integer num) {
System.out.println("buy方法的开始!!!");
//销售记录
Sale sale=new Sale();
sale.setQid(goodsid);
sale.setNums(num);
saleDao.intsertsale(sale);
//查询商品
Goods goods1=goodsDao.selectById(goodsid);
if (goods1== null){
throw new NullPointerException(goods1+"商品不存在");
} else if (goods1.getAmount()<num){
throw new NotException(goods1+"商品数量不足");
}else {
System.out.println(goods1);
}
//更新库存
Goods goods=new Goods();
goods.setId(goodsid);
goods.setAmount(num);
goodsDao.updateGoods(goods);
System.out.println("buy方法的完成!!!");
}
}
1.8 定义Spring配置文件
1.8.1 引入外部属性配置文件(jdbc.properties)
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
1.8.2 声明数据源、SqlSessionFactory对象、读取mapper文件等文件
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 加载引入外部属性配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--声明数据源DataSource 可以有多个数据源-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--声明SqlSessionFactoryBean,在这个类内部,创建SqlSessionFactory-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定数据源 -->
<property name="dataSource" ref="myDataSource"/>
<!--指定myBatis主配置文件
Resource可以直接使用value属性赋值-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--声明MapperScannerConfiguration
SqlSession.getMapper(StudentDao.class)
MapperScannerConfigurer作用:
循环basePackage所表示的包,把包中的每个接口都找到,调用SqlSession.getMapper
把每个dao接口都创建出dao对象,dao代理对象方法到容器中。
相当于:
ApplicationContext ctx=...
SqlSessionFactory sqlSessionFactory=ctx.getBean("factory");
for(接口: com.liuhaiyang.dao){
接口 对象=session.getMapper(接口)
springMap.put(对象名, 对象)
}
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定sqlsessionFactory对象的名称-->
<property name="sqlSessionFactoryBeanName" value="factory"/>
<!-- 指定基本包,dao接口所在的包名-->
<property name="basePackage" value="com.liuhaiyang.dao"/>
</bean>
<!-- 声明service-->
<bean id="buyselect" class="com.liuhaiyang.service.impl.BuyGoodsServiceImpl">
<property name="goodsDao" ref="goodsDao"/>
<property name="saleDao" ref="saleDao"/>
</bean>
<!-- <context:component-scan base-package="com.liuhaiyang.service"/>-->
<!-- 声明事务的控制-->
<!-- 声明事务管理器 id可以为任意值,最好是transactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 指定数据源DataSources-->
<property name="dataSource" ref="myDataSource"/>
</bean>
<!-- 开启事务注解驱动: 告诉框架使用注解管理事务 事务一般都是tx结尾
属性:transaction-manager 指定事务管理器的id 可以有多个id(也可以有多个数据源)
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
1.9定义测试类及结果
1.9.1 测试类1
import com.liuhaiyang.service.BuyGoodsService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
@Test
public void test01(){
String config="application.xml";
ApplicationContext app=new ClassPathXmlApplicationContext(config);
BuyGoodsService buy=(BuyGoodsService) app.getBean("buyselect");
buy.buyboods(1001,20);
}
}
结果截图:
1.9.2测试类2(异常)
@Test
public void test01(){
String config="application.xml";
ApplicationContext app=new ClassPathXmlApplicationContext(config);
BuyGoodsService buy=(BuyGoodsService) app.getBean("buyselect");
buy.buyboods(1001,50);
}
结果截图:
数据库
2.使用AspectJ框架控制事务
使用 XML 配置事务代理的方式的不足是,每个目标类都需要配置事务代理。当目标类较多,配置文件会变得非常臃肿。
使用 XML 配置顾问方式可以自动为每个符合切入点表达式的类生成事务代理。其用法很简单,只需将前面代码中关于事务代理的配置删除,再替换为如下内容即可。
使用这种方法控制事务的步骤如下:
在pom.xml中加入 spring-aspects 依赖
在spring配置文件中声明事务的内容:①声明事务管理器;②声明业务方法需要的事务属性;③声明切入点表达式。
与上面使用 注解(@Transactional)相比,大部分代码都是一样的。只有Spring的配置文件中有所改动。将第一种方法中spring配置文件中的 声明事务注解驱动 删掉,换成下面的代码就可以了。
<!-- 2.声明业务方的事务属性(隔离级别,传播行为,超时)
id:给业务方法配置事务段代码起个名称,唯一值(可以自定义)
transaction-manager:事务管理器id
-->
<tx:advice id="selectAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 给具体的业务方法,说明他需要的事务属性
name:业务方法的名称。配置name的值:1.业务方法的名称:2.带有通配符(*)的方法名称 3.使用*
propagation:指定传播行为的值 isolation:隔离级别 read-only:是否只读。默认是false timeout表示超时时间
rollback-for:指定回滚的异常类型列表,使用的异常全限定名称-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" read-only="false"
timeout="20" rollback-for="com.liuhaiyang.excetion.NotException,java.lang.NullPointerException"/>
<!-- 表示对业务方法中所有以buy开头的业务方法进行事务操作-->
<!-- <tx:method name="buy*" 等等/>-->
<!-- 以上方法以外的 单独使用表示所有,如果和其他的连用则表示出其他以外的方法-->
<!-- <tx:method name="*" 等等/>-->
</tx:attributes>
</tx:advice>
<!-- 声明切入点表达式:表示那些包中的类,类中的方法参数与事务-->
<aop:config>
<!-- 声明切入点表达式
expression:切入点表达式,表达那些类和类中的方法要参议事务
id:表示切入点表达式的名称,唯一值(任意取值)-->
<aop:pointcut id="servicePointcut" expression="execution(* *..service.*(..))"/>
<!-- 关联切入点表达式和事务通知-->
<aop:advisor advice-ref="selectAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
3 @Transactional注解和AspectJ框架各自特点
一、使用@Transactional注解
1.Spring框架自己提供的事务控制。
2.适合中小型项目。
3.使用方便,效率高。二、使用AspectJ框架
缺点:理解难,配置复杂。
优点:代码和事务配置是分开的。控制事务,其源代码不用修改。能快速的了解和掌控项目的全部事务,适合大型项目。