Sprint之事务
这篇文章蛮详细的https://www.cnblogs.com/yixianyixian/p/8372832.html
事务的7大传播行为:
事务中可能出现的3种问题:
事务的隔离级别 :
一,使用jdbc模板操作数据库,封装了数据库的连接,资源释放,没有开启事务
0,导包spring-tx包
1,User实体类,UserDao接口(增删查改方法)
实现类UserDaoImp(私有字段private JdbcTemplate jt;通过set注入)
实现类方法(通过JdbcTemplate操作数据库):
a,增
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao {
@Autowired
private JdbcTemplate JdbcTemplate;
@Override
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECT price FROM book WHERE isbn = ?";
return JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
}
b,根据id查找
@Override
public User getById(Integer id) {
String sql = "select * from user where uid=?";
return jt.queryForObject(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User u = new User();
u.setId(rs.getInt("uid"));
u.setName(rs.getString("uname"));
u.setPassword(rs.getString("upassword"));
u.setSex(rs.getString("sex"));
u.setBirthday(rs.getString("birthday"));
return u;
}
}, id);
}
c,查找全部User
@Override
public List<User> getAll() {
String sql = "select * from user";
List<User> list = jt.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User u = new User();
u.setId(rs.getInt("uid"));
u.setName(rs.getString("uname"));
u.setPassword(rs.getString("upassword"));
u.setSex(rs.getString("sex"));
u.setBirthday(rs.getString("birthday"));
return u;
}
});
return list;
}
2,xml文件配置
导入连接池配置
<context:property-placeholder location="classpath:db.properties"/>
连接池放入容器
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
连接池注入jdbcTemplate,注意property的name不要写错,可以去翻源码
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
jdbcTemplate注入Dao
<bean name="userDao" class="com.cl.jdbcTemplate.UserDaoImp">
<property name="jt" ref="jdbcTemplate"></property>
</bean>
3,测试类
完全手动方法:啥也不需要,一个类一个方法即可。
@Test
public void fun1() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///mybase");
dataSource.setUser("root");
dataSource.setPassword("root");
// 创建jdbc模板对象
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
String sql = "insert into user values('110','hehe','123456','male','1999-09-09')";
jt.update(sql);
}
注入了模板的Dao的测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name = "userDao")
private UserDao ud;
@Test
public void fun() {
User u = new User();
u.setBirthday("1998-09-09");
u.setName("lily");
u.setPassword("mypassword");
u.setSex("female");
ud.save(u);
}
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PlateformTransactionManager,事务核心管理器,封装事务操作,依赖连接池
如果Dao的实现类继承JdbcDaoSupport可以直接调用getJdbcTemplate方法获得JdbcTemplate处理sql语句。
二,使用显示事务模板+注解,或者不显示使用模板,配置需要事务的方法
Service的实现类,如
public class ServiceImp implements AccountService {
private AccountDao ad;
private TransactionTemplate tt;
public void setTt(TransactionTemplate tt) {
this.tt = tt;
}
public void setAd(AccountDao ad) {
this.ad = ad;
}
@Override//下面使用注解配置事务通知,方便多了
@Transactional(isolation=Isolation.REPEATABLE_READ,readOnly=false,propagation=Propagation.REQUIRED)
public void transfer(Integer from, Integer to, Double money) {
tt.execute(new TransactionCallbackWithoutResult() {
@Override // 封装了try-catch和回滚操作
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
ad.minusMoney(from, money);
ad.addMoney(to, money);
}
});
}
}
xml配置文件中先配置DataSource,然后将DataSource注入transactionManager,
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
配置事务模板对象
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
使用注解配置事务通知,这里感觉注解方便许多
<tx:annotation-driven/>
连接池注入Dao
<bean name="accountDao" class="com.cl.springtx.AccountImp">
<property name="dataSource" ref="dataSource"></property>
</bean>
Dao和transactionTemplate注入Service
<bean name="accountService" class="com.cl.springtx.ServiceImp">
<property name="ad" ref="accountDao"></property>
<property name="tt" ref="transactionTemplate"></property>
</bean>
如果不用注解配置+注解方法(直接在需要开启事务通知的方法上使用注解),事务通知的配置如下,指定某个包的哪些方法需要开启事务通知
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- get和find设置为只读 -->
<tx:method name="get*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="true"></tx:method>
<tx:method name="find*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 织入 -->
<aop:config>
<aop:pointcut expression="execution(* com.cl.springtx.*ServiceImp.*(..))" id="account-tx"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="account-tx"/><!-- 切面 -->
</aop:config>
总之,jdbc模板封装了数据库的操作,而transaction模板又封装了jdbc的操作,使得try-catch和回滚操作不用手动处理。