spring tx
1、Jdbctemplate
目标,使用spring管理jdbc框架,对指定表实现增删改查,批量增删改的效果,使用过程与queryrunner-99%相似
(1)、环境配置
A.jar包
B.Sql
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`c_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`c_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`t_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
PRIMARY KEY (`c_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
INSERT INTO `course` VALUES ('01', '语文', '02');
INSERT INTO `course` VALUES ('02', '数学', '01');
INSERT INTO `course` VALUES ('03', '英语', '03');
C.命名空间引入
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
D.配置bean.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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///demo"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- jdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注解扫描 -->
<context:component-scan base-package="com.tx"></context:component-scan>
</beans>
(2)、Dao层配置
package com.tx.dao;
import java.util.List;
import com.tx.pojo.Course;
public interface CourseDao {
//常规jdbc
public void insertCourse(Course course);//增加课程
public void updateCourse(Course course);//修改课程
public void deleteById(String id);//删除课程
public int countAll();//查询count计数
public Course getById(String id);//根据id查询
public List<Course> getAllCourse();//查询课程列表
//批量增删改
public void batchAdd(List<Object[]> batchParams);//批量增加课程参数
public void batchUpdate(List<Object[]> batchParams);//批量修改课程参数
public void batchDelete(List<Object[]> batchParams);//批量删除课程参数
}
(3)、DaoImpl实现
A.DI(依赖注入)- jdbc框架
@Repository
public class CourseDaoImpl implements CourseDao {
@Autowired
private JdbcTemplate jdbcTemplate;
B.增删改
@Override
public void insertCourse(Course course) {
String sql ="insert into course values(?,?,?)";
Object[] params= {course.getCid(),course.getCname(),course.getTid()};
int update=jdbcTemplate.update(sql, params);
System.out.println(update);
}
@Override
public void updateCourse(Course course) {
String sql ="update course set c_name=?,t_id=? where c_id=?";
Object[] params= {course.getCname(),course.getTid(),course.getCid()};
jdbcTemplate.update(sql, params);
}
@Override
public void deleteById(String id) {
String sql ="delete from course where c_id=?";
jdbcTemplate.update(sql, id);
}
a.对应service层
@Service
public class CourseService {
@Autowired
private CourseDao courseDao;
public void addCource(Course course) {
courseDao.insertCourse(course);
}
public void updateCourse(Course course) {
courseDao.updateCourse(course);
}
public void deleteCourse(String id) {
courseDao.deleteById(id);
}
C.查询个数,单行记录,多行记录
a.RowMapper 引入——类比queryrunner之ResultSetHandler
RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成 数据封装
@Override
public int countAll() {
String sql="select count(c_id) from course";
Integer count=jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
@Override
public Course getById(String id) {
String sql="select c_id cid,c_name cname,t_id tid from course where c_id";
Object[] params= {id};
//BeanProperRowMapper 实体属性行mapper
Course course=jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Course>(Course.class),params);
return course;
}
@Override
public List<Course> getAllCourse() {
String sql = "select c_id cid,c_name cname,t_id tid from course";
//调用方法
List<Course> courseList = jdbcTemplate.query(sql,new BeanPropertyRowMapper<Course>(Course.class), null);
return courseList;
}
b.对应service层
public void countAll() {
System.out.println(“总数为:”+courseDao.countAll( ));
}
public void queryForBean( String id) {
Course course=courseDao.getById(id) ;
System.out.println( course);
}
public void queryForBeanList() {
List<Course> list=courseDao.getAllCourse();
for(Course course : list) {
System.out.println(course);
}
}
D.批量增删改
//批量增加
@Override
public void batchAdd(List<Object[]> batchParams) {
String sql = "insert into course values( ? , ?, ?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchParams);
System.out.println(Arrays.toString( ints));
}
//批量修改
@Override
public void batchUpdate(List<Object[]> batchParams) {
String sql = "update course set c_name=?,t_id=? where c_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchParams);
System.out.println(Arrays.toString(ints));
}
//批量删除@Override
public void batchDelete(List<Object[]> batchParams) {
String sql = "delete from course where c_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchParams);
System.out.println(Arrays.toString(ints));
}
对应service层
public void batchAdd(List<Object[]> batchParams) {
courseDao.batchAdd(batchParams ) ;
}
public void batchUpdate(List<Object[]> batchParams) {
courseDao.batchUpdate( batchParams ) ;
}
public void batchDelete(List<Object[]> batchParams){
courseDao.batchDelete( batchParams);
}
(4)、测试
package com.tx.test;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.tx.pojo.Course;
import com.tx.service.CourseService;
public class DemoTest {
@Test
public void addTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
Course course=new Course("04","哲学","01");
courseService.addCource(course);;
}
@Test
public void updateTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
Course course=new Course("04","美术","02");
courseService.updateCourse(course);
}
@Test
public void deleteTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
courseService.deleteCourse("04");
}
@Test
public void countTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
courseService.countAll();;
}
@Test
public void queryForBeanTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
courseService.queryForBean("01");
}
@Test
public void queryForBeanListTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
courseService.queryForBeanList();
}
@Test
public void batchAddTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
//批量添加测试
List<Object[]> batchParams = new ArrayList<>();
Object[] o1 = {"04","物理","04"};
Object[] o2 = {"05","化学","05"};
Object[] o3 = {"06","生物","06"};
batchParams.add(o1);
batchParams.add(o2);
batchParams.add(o3);
//调用批量添加
courseService.batchAdd(batchParams);
}
@Test
public void batchUpdateTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
//批量添加测试
List<Object[]> batchParams = new ArrayList<>();
Object[] o1 = {"04","化学","04"};
Object[] o2 = {"05","生物","05"};
Object[] o3 = {"06","物理","06"};
batchParams.add(o1);
batchParams.add(o2);
batchParams.add(o3);
//调用批量添加
courseService.batchUpdate(batchParams);
}
@Test
public void batchDeleteTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
CourseService courseService=context.getBean("courseService",CourseService.class);
//批量添加测试
List<Object[]> batchParams = new ArrayList<>();
Object[] o1 = {"04"};
Object[] o2 = {"05"};
Object[] o3 = {"06"};
batchParams.add(o1);
batchParams.add(o2);
batchParams.add(o3);
//调用批量添加
courseService.batchDelete(batchParams);
}
}
2、事务管理
(1)、生活场景
当我们注册平夕夕的时候,app会给我们发代金券或者优惠券,如果注册成功了没有发,或者没有注册成功却领了对应的券,用户体验就非常不好。要么注册成功就能领到券,要么没有注册成功就根本无法领到券,本质是多条sql要么全部执行成功,要么就全部失败
(2)、问题原因
使用sql直接操作数据库,很容易产生读写问题(内部,外部异常)
内部异常:执行一套课程数据更新sql,数据库突然重启,丢失更新语句,无原始记录对比
外部异常:如上,条件为电脑重启
(3)、解决方案
不让sql直接操作数据库,采用记录日志的过程解决,这种技术就是事务机制
Mysql中一共有5种日志文件,采用其中的redo日志和undo日志完成问题解决
Sql语句操作什么记录,这些记录会被复制到undo日志中(from后面跟的表),然后增删改查操作的结果会复制到redo日志中,假如这些增删改查的操作没有什么问题,最后把redo日志同步到数据库数据文件中,即便中途在同步过程中出现了问题,恢复之后继续让redo日志继续和数据文件进行同步即可,这样的操作依赖,数据库抵抗风险的能力得到加强
(4)、事务定义
事务(Transaction)是一个或者多个sql语句组成的整体,要么全部执行成功,要么全部执行失败,mysql一条sql的默认事务是自动开启,但是对于多条sql组合成的在外界看起来是一次操作的流程(要么全都执行成功,要么全都执行失败)需要保证他们在同一个事务之下
(5)、数据库使用案例
案例:删除接待部门,需要将该部门下的原来员工全部拷贝进入后勤部门,再执行删除部门操作,整个操作在外部操作人员来看就是“删除接待部门这一件事”,但实际上内部操作有两条步骤,要么都成功,要么都失败后回到初始状态
(6)、ACID
A.A-事务原子性-不可分割-看着单位一
原子——>分子
对于事务来说,从【开启事务–多条sql执行–提交事务】这整个过程就是原子性的,不可分割的,要么全部执行成功形成一次使用者眼中的功能(删除部门案例),要么就什么都没执行,回滚到执行前的样子
B.C-事务的一致性
不管任何时间,并发多少,事务必须保证运行结果的一致性
如何保证呢?阻止事务之间相互读取没有提交的临时数据,一个事务不能读取另外一个事务的undo和redo日志
C.I-事务隔离性
为了结果的一致性,机制上看就是事务的隔离机制起效果
D.D-事务持久性
事务一旦提交,结果便是永久性的,即便发生异常,仍然可以依靠事务日志完成数据的持久化,无论如何数据都可以依靠事务提交到数据文件中
E.开发中的事务使用
由于事务一般管控多条sql执行一个生活业务操作集合,所以最好将事务打在service层面
a.Jdbc层面开启事务
参考web应用案例中Daofactory的事务aop设计
b.Spring管理事务(AOP-aspecj技术实现)
事务切入点在类上,代表该类所有方法执行事务管理
切入点在方法上,该方法执行事务管理
① 注解配置事务管理
环境:
配置xml
<?xml version="1.0" encoding="UTF-8"?><!-- 注解扫描 -->
<context:component-scan base-package="com.tx.annconfig"></context:component-scan>
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///demo"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- jdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
CourseService.java
package com.tx.ann;
public interface CourseService {
public void updateCourse(Course c1, Course c2);
}
对应的service实现类
package com.tx.ann;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service(“service”)
public class CourseServiceImpl implements CourseService {
@Autowired
private CourseDao courseDao;
@Transactional
@Override
public void updateCourse(Course c1, Course c2) {
courseDao.updateCourse(c1);
int i = 10/0;
courseDao.updateCourse(c2);
}
}
测试类
package com.tx.ann;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DemoTest {
@Test
public void updateTest() {
// 加载spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("com/tx/ann/bean.xml");
// 获取配置创建对象
CourseService service = context.getBean("service", CourseService.class);
Course c1 = new Course("01","数学","02");
Course c2 = new Course("02","语文","01");
service.updateCourse(c1, c2);
}
}
② xml配置事务管理
环境:
配置xml
<?xml version="1.0" encoding="UTF-8"?><!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<!-- 加载驱动 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///demo"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- jdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入DataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注解扫描包 -->
<context:component-scan base-package="com.tx.xmlconfig"></context:component-scan>
<!-- 开启事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入DataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务声明 -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- requied 都得开启事务 -->
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="edit*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="new*" propagation="REQUIRED"/>
<tx:method name="set*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="change*" propagation="REQUIRED"/>
<!-- read-only 只读,不开启事务 -->
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
<tx:method name="select*" propagation="REQUIRED" read-only="true"/>
<tx:method name="load*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 开启事务切面管理 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut expression="execution(* com.tx.xmlconfig.*.*(..))" id="serviceMethod"/>
<!-- < aop:aspect>大多用于日志,缓存 -->
<!-- < aop:advisor>大多用于事务管理 -->
<!-- 区别在于 -->
<!--juge yes: setAutoCommit(false) commit rollback no:read-only -->
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="serviceMethod"/>
</aop:config>
<!-- 指定数据库,druid无法识别由spring动态代理后的Proxy -->
<bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">
<property name="dbType" value="mysql" />
</bean>
其他类与注解配置一样
③ 完全注解方式
环境:
配置配置类
txConfig.java替换xml
package com.tx.ann;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.alibaba.druid.pool.DruidDataSource;
@Configuration //配置类
@ComponentScan(basePackages = “com.tx.ann”) // 包扫描
@EnableTransactionManagement // 开启事务
public class TxConfig {
// 创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///demo");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
// 创建jdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
// 到ioc容器中根据类型找到DataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
// 注入DataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
// 创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
// 连接池事务管理器
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
// 注入DataSource
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
c.事务参数
① Propagation:事务传播
多事务方法调用(嵌套使用事务)
Spring框架的7种事务传播行为
工程环境:
bean1.xml
<?xml version="1.0" encoding="UTF-8"?><!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql:///demo" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入 dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注解扫描 -->
<context:component-scan base-package="com.tx.propagation">
</context:component-scan>
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启注解配置 -->
<tx:annotation-driven transaction-manager="transactionManager" />
StuService.java package com.tx.propagation; public interface StuService { public void saveParent(); public void saveChildren(); } StuServiceImpl.java package com.tx.propagation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StuServiceImpl implements StuService{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void saveParent() {
Stu stu=new Stu();
stu.setName(“parent”);
stu.setAge(30);
String sql=“insert into stu (name,age) values (?,?)”;
Object[] params= {stu.getName(),stu.getAge()};
jdbcTemplate.update(sql, params);
}
@Transactional(propagation=Propagation.SUPPORTS)
@Override
public void saveChildren() {
saveChild1();
int i=10/0;
saveChild2();
}
public void saveChild1() {
Stu stu1=new Stu();
stu1.setName("child-1");
stu1.setAge(10);
String sql="insert into stu (name,age) values (?,?)";
Object[] params= {stu1.getName(),stu1.getAge()};
jdbcTemplate.update(sql, params);
}
public void saveChild2() {
Stu stu1=new Stu();
stu1.setName("child-2");
stu1.setAge(20);
String sql="insert into stu (name,age) values (?,?)";
Object[] params= {stu1.getName(),stu1.getAge()};
jdbcTemplate.update(sql, params);
}
}
TestTransactionService.java
package com.tx.propagation;
public interface TestTransactionService {
public void testPropagationTrans();
}
TestTransactionServiceImpl.java
package com.tx.propagation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service(“service”)
public class TestTransactionServiceImpl implements TestTransactionService{
@Autowired
private StuService stuService;
@Transactional(propagation=Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a=10/0;
}
}
TransactionTest.java
package com.tx.propagation;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TransactionTest {
@Test
public void tranTest() {
ApplicationContext context =new ClassPathXmlApplicationContext("com/tx/propagation/bean1.xml");
TestTransactionService service=context.getBean("service",TestTransactionService.class);
service.testPropagationTrans();
}
}
REQUIRED与REQUIRED
有事务则在事务中运行,没事务则开启事务)
(只要有一个报错,就不会有数据加入到数据库)
REQUIRED与SUPPORTS
------下列,不会有数据加入到数据库
REQUIRED与REQUIRES_NEW
内有错,外没错,REQUIRES_NEW开启的新事务会被挂起,则不会添加到数据库
REQUIRED与NEVER
有事务,则抛出异常,所以不会有数据
② ioslation:事务隔离级别
(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
(2)有三个读问题:脏读、不可重复读、虚(幻)读
(3)脏读:一个未提交事务读取到另一个未提交事务的数据(开启两个事务,事务A修改了数据,并未提交,事务B读取到事务A未提交的数据)
(4)不可重复读:针对同一个数据,一个未提交事务读到另一个事务已经修改并提交的数据
(5)幻读:每次读都多一条数据,一个未提交事务读取到另一个事务新提交的新增数据(注意:是已提交事务)
脏读是问题,需要解决
不可重复读和幻读是状态,大部分业务里没有太大问题
解决脏读方案:设置隔离级别
③ timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交进行回滚
(2)默认值是 -1 ,设置时间以秒单位进行计算
④ readOnly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作
(3)设置 readOnly 值是 true,设置成 true 之后,只能查询
⑤ rollbackFor:回滚
(1)设置出现哪些异常进行事务回滚
⑥ noRollbackFor:不回滚
(1)设置出现哪些异常不进行事务回滚