JdbcTemplate概述
-
以往使用jdbc时,每次都需要自己获取PreparedStatement,执行sql语句,关闭连接等操作。操作麻烦冗余,影响编码的效率。
-
Spring把对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate(jdbc模板)之中,这样我们只需要做一些简单的操作(eg:编写SQL语句、传递参数)就可以了。
-
spring框架根据不同持久层方案为我们提供了不同的JdbcTemplate(jdbc模板类)。例如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等
QuickStart
导入spring-jdbc和spring-tx坐标
<dependencies>
<!-- Spring上下文坐标,如果使用到了Spring的上下文容器,需要导入-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- 使用Spring JdbcTemplate需要导入下面两个坐标 -->
<!-- 导入spring-jdbc坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- 导入spring-tx事务管理坐标,如果不使用事务管理,可以不导 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- 因为操作Mysql数据库,需要导入数据库驱动坐标 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- 数据源选择使用c3p0连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- 使用单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
在测试类中创建JdbcTemplate对象并使用
public class AccountTest {
@Test
public void test1() throws PropertyVetoException {
//1. 创建JdbcTemplate模板对象
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//2. 设置数据源ComboPooledDataSource对象
//创建DataSource对象,并设置连接四要素
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("root");
//JdbcTemplate依赖于DataSource
jdbcTemplate.setDataSource(dataSource);
//3. 执行增删改查操作
int row = jdbcTemplate.update("insert into account values (?,?)", "赵丽颖", 666);
System.out.println(row);
}
}
Spring容器创建并维护JdbcTemplate及相关对象
1)思路分析
-
Spring的核心之一就是Ioc,控制反转,也就是由Spring容器负责对象的创建和对象间依赖关系的管理。上述代码中DataSource、JdbcTemplate对象以及他们之间的关系都是由我们手动创建,并且手动设置依赖关系的。
-
经过观察,JdbcTemplate类可以通过无参构造创建该类对象,同时又setDataSource()方法用于设置数据源依赖,所以JdbcTemplate类的对象完全可以交给Spring容器管理,数据源DataSource可以交给Spring容器管理,那我们就可以使用Spring容器管理并维护上述两个对象。
2)代码实现
导入坐标:因为要使用到Spring容器创建对象并维护对象依赖关系,所以要在pom.xml中确定导入了spring-context坐标。
<!-- Spring上下文坐标,使用到了Spring的上下文容器,需要 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
Xml配置:在applicationContext.xml文件中先配置数据源,然后配置JdbcTemplate,最后将数据源注入到JdbcTemplate中。
<!-- DataSource对象 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
Java代码:通过Spring容器获得JdbcTemplate对象,调用JdbcTemplate对象的update方法,传入sql语句及参数完成插入数据的操作
@Test
public void test2() throws PropertyVetoException {
//1. 获取JdbcTemplate模板对象(Spring已经将DataSource注入其中)
ApplicationContext ac= new ClassPathXmlApplicationContext(
"applicationContext.xml");
JdbcTemplate jdbcTemplate = ac.getBean(JdbcTemplate.class);
//2. 不需要设置数据源ComboPooledDataSource对象
//3. 执行增删改查操作
int row = jdbcTemplate.update("insert into account values (?,?)", "shheima", 60000);
System.out.println(row);
}
抽取jdbc配置
-
目前的jdbc四要素已经脱离了代码,已经实现了解耦,之后如果数据库信息修改后直接修改配置文件即可。
-
但是目前数据库四要素的配置耦合进了Spring的配置文件,为了更方便的维护,建议将该信息抽取到一个独立properties文件中。
-
提取数据源的基本连接信息到配置文件jdbc.properties(名字任意,格式为properties)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///test
jdbc.username=root
jdbc.password=root
引入properties文件,并修改DataSource注入基本参数的配置,具体如下:
<!-- 导入jdbc.properties文件。classpath表示文件在类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- DataSource对象 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--
property的name属性名称通过set方法截取;因为属性值为String,所以通过value属性设置
通过${key}的方式可以从properties文件中读取key对应的值。
-->
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
如果有多个properties文件需要引入可以使用通配符:
<!--如果需要导入多个文件,可以使用通配符 -->
<context:property-placeholder location="classpath:*.properties"/>
JdbcTemplate常用操作
jdbcTemplate更新数据库常用方法
- update (更新数据,也就是增删改)
- queryForObject (单行查询)
- query (多行查询)
- queryForObject (单值查询)
JdbcTemplate常用操作-增删改操作
JdbcTemplate对象的update方法可以完成增删改的操作,只是传入的sql语句及对应参数不同。
语法:对数据库进行增删改的操作,返回影响的行数
jdbcTemplate.update(sql语句, sql语句参数列表)
代码如下:
@RunWith(SpringJUnit4ClassRunner.class) //指定测试器
@ContextConfiguration("classpath:applicationContext.xml") //指定Spring配置文件并加载
public class JdbcTemplatetTest {
@Autowired //自动注入JdbcTemplate对象
private JdbcTemplate jdbcTemplate;
/**
* testUpdate:测试删除
*/
@Test
public void testDelete(){
int row = jdbcTemplate.update("delete from account where name=?","nanjingheima");
System.out.println(row);
}
/**
* testUpdate:测试修改
*/
@Test
public void testUpdate(){
int row = jdbcTemplate.update("update account set money=? where name=?", 500000,"hzheima");
System.out.println(row);
}
}
JdbcTemplate常用操作-查询操作
JdbcTemplate对象的query**()
方法可以完成查询的操作。
多行查询
List<T> query(sql语句, new BeanPropertyRowMapper<T>(T.class))
查询并将结果通过BeanPropertyRowMapper将查询的结果集封装到List集合中
List<T> query(sql语句, new BeanPropertyRowMapper<T>(T.class), sql语句参数列表)
查询并将结果通过BeanPropertyRowMapper将查询的结果集封装到List集合中
单行查询
T queryForObject(sql语句, new BeanPropertyRowMapper<T>(T.class), sql语句参数列表)
单行查询
查询并将结果通过BeanPropertyRowMapper将查询结果封装到javaBean对象中
单值查询
要求的查询结果类型 queryForObject(sql语句, 要求的查询结果类型.class)
查询并将结果通过BeanPropertyRowMapper将聚合查询结果转化到要去的结果类型
要求的查询结果类型 queryForObject(sql语句, 要求的查询结果类型.class, sql语句参数列表)
查询并将结果通过BeanPropertyRowMapper将聚合查询结果转化到要去的结果类型
代码如下:
@RunWith(SpringJUnit4ClassRunner.class) //指定测试器
@ContextConfiguration("classpath:applicationContext.xml") //指定Spring配置文件并加载接卸
public class JdbcTemplatetTest {
@Autowired //自动注入JdbcTemplate对象
private JdbcTemplate jdbcTemplate;
/**
* testQueryCount:聚合查询
*/
@Test
public void testQueryCount(){
long count = jdbcTemplate.queryForObject("select count(*) from " +
"account",long.class);
System.out.println(count);
count = jdbcTemplate.queryForObject("select count(*) from " +
"account where money = ?",long.class,60000);
System.out.println(count);
}
/**
* testQueryOne:查询单个
*/
@Test
public void testQueryOne(){
Account account = jdbcTemplate.queryForObject("select * from account " +
"where name = ?",
new BeanPropertyRowMapper<Account>(Account.class) ,
"hzheima");
System.out.println(account);
}
/**
* testQuerySome:按条件查询部分
*/
@Test
public void testQuerySome(){
List<Account> accounts = jdbcTemplate.query("select * from account where money = ?",
new BeanPropertyRowMapper<Account>(Account.class), 60000);
System.out.println(accounts);
}
/**
* testQueryAll:查询所有
*/
@Test
public void testQueryAll(){
List<Account> accounts = jdbcTemplate.query("select * from account",
new BeanPropertyRowMapper<Account>(Account.class));
System.out.println(accounts);
}
}
补充:
批量更新(增删改)
//批量增删改,batchArgs为批量更新的参数列表,类型为Object[]数组类型的List集合
jdbcTemplate.batchUpdate(String sql, List batchArgs);
/**
*testBatechUpdate:批量更新,批量 insert,update,delete
*/
@Test
public void testBatechUpdate() {
String sql = "insert into account(name, money) values(?,?)";
List<Object[]> batchArgs = new ArrayList<Object[]>();
batchArgs.add(new Object[] {"zhangsan",2000});
batchArgs.add(new Object[] {"lisi",2000});
batchArgs.add(new Object[] {"wangwu",2000});
jdbcTemplate.batchUpdate(sql, batchArgs);
}
自定义绑定(使用场景较少)
除了使用系统提供的RowMapper的实现类之外,我们也可以自己手动编写RowMapper实现类,以实现特殊的结果集到javaBean的封装转化,实例代码如下:
Account account = jdbcTemplate.queryForObject(
"select * from account where money = ?",
new Object[]{60000L},
new RowMapper<Account>() {
//重写该方法时只需要将获取的结果集中单个结果封装到某个JavaBean中,
//query方法会根据实际情况选择选择返回一个javaBan对象还是一个List
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setUsername(rs.getString("name"));
account.setMoney(rs.getString("money"));
return account;
}
});
执行DDL语句(使用场景很少)
Spring的JdbcTemplate也可以使用execute(…)方法运行任意SQL语句。所以,该方法通常用于DDL语句。下面代码创建一个表:
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
Spring事务管理
Spring事务管理主要应用在软件分层之后的业务层,也就是Service层。一个service层的操作依赖的多个dao层操作要被事务管理起来,保证数据安全准确。eg:转账。
Spring事务管理核心API
Spring事务管理的三个高层接口
-
PlatformTransactionManager:Spring提供的事务管理器核心接口,内部规定了常用的事务管理的规则。
-
TransactionDefinition:Spring提供的封装事务属性的接口,用于封装和操作事务的属性
-
TransactionStatus:Spring提供的封装事务状态的接口,用于封装事务的状态
PlatformTransactionManager
Spring提供的事务管理器接口,整个Spring进行事务管理的核心接口,内部规定了常用的操作事务的规则。
a) 接口中规定的操作事务的方法:
操作事务的方法 | |
---|---|
TransactionStatus getTransaction (TransactionDefination defination) |
根据事务定义信息获取事务状态信息 |
void commit(TransactionStatus status) | 提交事务 |
void rollback(TransactionStatus status) | 回滚事务 |
根据上述方法,
TransactionStatus getTransaction(TransactionDefination defination)
我们可以推断:PlatformTransactionManager根据TransactionDefinition对象获得 TransactionStatus 对象
-
三者的关系:平台事务管理器(PlatformTransactionManager)根据 事务属性管理器(TransactionDefinition)中定义的事务属性对事务进行管理;事务运行过程中,每一个时间点都有对应的事务状态信息(TransactionStatus)。
-
也就是说,干活的是Manager(PlatformTransactionManager),根据事务属性管理器(TransactionDefinition)的配置信息干活,在干活的过程中会有一些状态信息,通过TransactionStatus体现。
b) 不同的Dao层技术则有不同的实现类
PlatformTransactionManager是一个接口,只负责制定规范,那具体的实现是哪些类呢?Spring根据不同的数据持久层技术提供了不同实现类:
-
Dao 层技术是jdbc、 mybatis、Apache DBUtils时:org.springframework.jdbc.datasource.DataSourceTransactionManager
-
Dao层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager
TransactionDefinition
Spring提供的封装事务属性的接口,用于规定封装事务的属性,通过该接口实现类对象可以获取(子类对象可以设置)事务属性。
a) 接口中规定的获取事务属性的方法:
方法名 | 说明 |
---|---|
int getIsolationLevel() | 获取事务的隔离级别,隔离级别用于解决事务并发访问读问题 |
int getPropagationBehavior() | 获取事务的传播行为,解决的就是两个事务方法事务统一性的问题 |
int getTimeout() | 获取超时时间 |
boolean isReadOnly() | 判断当前事务是否只读 |
-
接口中只规定了获取的方法,在其实现类中有设置的方法,eg:DefaultTransactionDefinition
-
事务属性需要在进行声明式事务配置时,需要在配置文件中对于事务属性进行配置,以便Spring框架进行解析。
b) 事务的四个特性:
ACID:原子性、一致性、隔离性、持久性。
-
Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成 功,要么全部失败。
-
Consistency(一致性):事务完成时,数据必须处于一致状态,数据的完整性约束没有被破坏,事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
-
Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性 和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
-
Durability(持久性):事务结束后,事务处理的结果必须能够得到持久化。
c) 事务属性:隔离级别(理解)
在并发访问的时候,会出现一些读问题。
读问题
- 脏读:一个事务读取了另外一个事务改写但还未提交的更新数据,如果这些数据被回滚,则读到的数据是无效的。
- eg:小明工资表,老板算工资的时候被小明看到了。
- 不可重复读:在同一个事务中,多次读取同一个数据返回的结果有所不同。换句话说就是,后续读取可以读取到另一个事务已提交的更新数据。相反,“可重复读”在同一个事务中多次读取数据时,能保证数据时一样,也就是,后续读取不能读取到另一个事务已经提交的更新数据。
- eg:小明买手机前后老婆查账。
- 幻读:一个事务读取了(按照某个规则的)全部数据后,另一个事务插入了一些记录,当后续再次查询的时候,幻读就发生了。在后续查询中,第一个事务就会发现有一些原来没有的记录。
- eg:老婆查岗打印流水,又发现消费多了。
设置隔离级别,可以解决事务并发产生的读问题,如脏读、不可重复读和虚读(幻读)。
-
ISOLATION_DEFAULT 默认级别,使用数据库内部默认级别(mysql - Repeatable read,其他大多数是 - Read committed)
-
ISOLATION_READ_UNCOMMITTED 读未提交:一个事务可以读取另一个未提交事务的数据,该级别下容易上述所读有问题。性能最高,安全性最差。
-
ISOLATION_READ_COMMITTED 读提交:一个事务要等另一个事务提交后才能读取数据。能解决脏读问题,但是可能会有不可重复读、幻读的问题。
-
ISOLATION_REPEATABLE_READ 重复读,在开始读取数据(事务开启)时,不再允许修改操作。能解决脏读、不可重复读的问题,但是可能会有幻读的问题。
-
ISOLATION_SERIALIZABLE 序列化,解决所有三个问题,相当于锁表安全性高,串行化处理事务,但是非常耗数据库性能,效率低下,一般不用。
列表如下(√表示可能出现,×表示不会出现):
隔离级别\读问题 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted读未提交 | √ | √ | √ |
Read commited读提交 | × | √ | √ |
Repeatable read重复读 | × | × | √ |
Serializable | × | × | × |
上述事务的四大特性、四个隔离级别是原来是在数据库内部规定的,Spring从数据库引入了 过来。但是接下来的七种传播行为和数据库没有直接关系,是Spring为了解决实际开发中的问题而引入的。
d) 事务属性:传播行为
即然是传播,那么至少有两个东西,才可以发生传播。单体不存在传播这个行为。
事务传播行为(propagation behavior)指的就是当一个事务方法A调用另一个事务方法B时,事务方法B应该以何种事务特性进行。是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。
事务传播行为是为了解决业务方法相互调用时,事务如何管理的问题。
通常情况下,一个业务方法就是一个事务。但是多个业务方法相互调用的时候可能会出现问题。
eg:取钱打印小票
取钱 required
{
插卡 required
输密码 required
吐钱 required
打印小票 requires_new
}
七种事务传播行为(当前方法:被调用的方法):
-
PROPAGATION_REQUIRED:表示当前方法必须运行在事务中。如果调用者事务存在,当前方法就会在该事务中运行;否则,会启动一个新的事务。一般的选择(默认值)。
-
PROPAGATION_SUPPORTS:表示当前方法不需要事务上下文,但是如果当前事务存在的话,那么该方法会在这个事务中运行。
-
PROPAGATION_MANDATORY:表示当前方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。
-
PROPAGATION_REQUERS_NEW:表示当前方法必须运行在它自己的事务中,一个新的事务将被启动。如果存在当前事务,该方法执行期间,当前事务会被挂起。
-
PROPAGATION_NOT_SUPPORTED:表示当前方法不应该运行在事务中。如果存在当前事务,该方法运行期间,当前事务会被挂起。
-
PROPAGATION_NEVER:表示当前方法不应该运行在事务上下文中。如果存在当前事务,则会抛出异常。
-
PROPAGATION_NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行独立的提交和回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持不同,可以参考资源管理器的文档来确认他们是否支持嵌套事务。
e) 事务其他属性
超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置。一般使用默认。
是否只读:建议查询操作时设置为只读,效率更高。
事务中的readOnly属性表示对应的事务会优化为只读事务。如果值为true就会告诉Spring我这个方法里面没有insert或者update,你只需要提供只读的数据库Connection就行了,这种执行效率会比read-write的Connection高。
如果设置了只读事务,Mysql数据库增删改会报错,Oracle无影响(不支持)
TransactionStatus
Spring提供的封装事务状态的接口,用于封装事务的状态。
事务的状态是事务自身的属性,在事务管理不同的时间段,事务会表现出不同的状态,所以不需要配置。
a) 接口中规定的判断事务状态的方法:
方法名 | 说明 |
---|---|
boolean isNewTransaction() | 是否是新事务。如果返回false,说明当前事务已存在,或者该操作没在事务环境中。 |
boolean hasSavepoint() | 是否存在回滚点 |
boolean isRollbackOnly() | 判断当前事务是否被设置了rollback-only |
boolean isCompleted() | 事务是否已结束 |
声明式事务概述
-
Spring 的声明式事务就是采用声明的方式来设置事务的属性参数,事务会按照指定的规则自动管理运行。
-
这里所说的声明,指在配置文件中配置(或添加注解)<事务的属性参数>,用在Spring 配置文件(注解)中声明的事务来代替用代码编写的事务。
-
在业务代码运行过程中,Spring容器(中的事务管理器对象)跟据配置文件中的配置参数,动态的把事务加入到业务代码中。(AOP思想)
-
(编码时)事务管理不侵入业务组件。编写业务代码的时候并没有体现事务管理的代码。
-
事务管理策略维护方便。如果需要修改事务管理策略,只要在配置文件上修改,无需改变代码重新编译。
基于XML的声明式事务管理
配置切点
<!--pointcut 目标对象 内部的方法就是切点-->
<bean id="accountService" class="cs.wy.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
配置通知(事务管理器)
<!-- advice 配置通知-事务管理器就是通知(增强)所在的类 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--
jdbc 使用的(平台)事务管理器是 DataSourceTransactionManager
事务管理管理的是Service层,service层需要调用dao层操作数据库,
dao层依赖DataSource管理连接池并连接数据库
所以事务管理器中需要注入DataSource
-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
配置织入
<!-- weaving 配置事务的织入。 增强方法 + 目标方法 -->
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* cs.wy.service.impl.*.*(..))"/>
</aop:config>
基于XML的声明式事务管理参数详解
重点分析如下配置:
<!-- advice 配置通知-事务管理器就是通知(增强)所在的类 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
其中:
tx:advice
advice,增强方法所在的类(transactionManager的class)
tx:attributes
具体的事务需要配到这个标签里面
tx:method
每一个tx:method代表一个类别事务
在这个标签里面的属性上会配置某些业务逻辑的方法(目标方法),并同时配置对应的事务管理逻辑。
tx:nethod ~ name
name属性,用于配置被切点表达式拦截的所有方法中,哪些业务逻辑方法被当前事务管理。
该属性并非Spring事务本身的属性,而是为了让该事务匹配关联到某些业务逻辑方法。
name值可以使用*统配,比方说*代表所有方法,save*代表所有save开头的方法
tx:nethod ~ isolation
isolation属性,配置当前事务的隔离级别;如不设置或设置DEFAULT,使用默认的隔离级别。
mysql默认的Repeatable read,其他大多数是Read committed。
tx:method ~ propagation
propagation属性,配置当前事务的传播行为。不设置则使用默认值REQUIRED。
tx:method ~ timeout
timeout属性,配置当前事务的超时时间,不设置则使用默认值-1,表示不限制。一般不设置。
tx:method ~ readonly
readonly属性,值为boolean类型。默认值为false。
关于tx:attributes
a. 如果不配置该标签,所有切点方法都会单独开启属性为默认值的事务
b. 如果配置该标签,内部tx:method中name匹配的方法会按照当前配置开启事务,
其他未匹配成功的方法会单独开启属性为默认值的事务
c. 存在多个tx:method标签时,会按照配置顺序挨个匹配,直到匹配成功,未匹配成功开启默认事务
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="upd*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
关于切点表达式:多个切点表达式可以进行逻辑运算连接(主要使用或||):
<aop:pointcut id="tranpc" expression="execution(* cs.wy.user.service.*.*(..))|| execution(* cs.wy.goods.service.*.*(..))"/>
关于readonly
对于mysql数据库:
如果是查询操作,建议设为true,优化查询性能;其他设为false或者不设置使用默认值。
增删改操作如果设置为true,则会抛出异常。
对于Oracle数据库:
无效果
基于注解的声明式事务控制
使用基于注解的声明式事务控制,步骤如下:
1、 在xml中开启自动代理
<aop:aspectj-autoproxy/>
2、然后在方法或类上配置@Transactional注解
1. 配置在类上表示该类下所有方法均开启事务
2. 配置在方法上,表示该方法开启事务
- 推荐做法:
在类上添加@Transactional统一开启事务,
如有需要,可以在特定方法上添加@Transactional用于配置特属性。
@Transactional(readOnly = true,,propagation = Propagation.SUPPORTS)
来源:CSDN
作者:挽远
链接:https://blog.csdn.net/Wan_Yuan/article/details/104540751