它是Spring提供的一个对象,是对原始的Jdbc Api对象的简单封装。Spring提供了很多操作模版类。
操作关系型数据库:JdbcTemplate、HibernateTemplate操作nosql数据库:RedisTemplate操作消息队列:JmsTemplate 用于和数据库交互,实现对表的CRUD操作。
抽取Dao接口实现类中的重复代码。即目前测试项目中的JdbcTemplate的定义和注入。
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { public Account findAccountById(Integer id) { List<Account> accountList = super.getJdbcTemplate().query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id); return accountList.isEmpty() ? null : accountList.get(0); } public Account findAccountByName(String name) { List<Account> accountList = super.getJdbcTemplate().query("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name); if (accountList.isEmpty()) { return null; } if (accountList.size() > 1) { throw new RuntimeException("结果集不唯一。"); } return accountList.get(0); } public void updateAccount(Account account) { super.getJdbcTemplate().update("update account set name=?,money=? where id=?", account.getName(), account.getMoney(), account.getId()); } } <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="accountDaoImpl" class="com.xijianlv.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/spring?useSSL=false"></property> <property name="username" value="root"></property> <property name="password" value="welcome1"></property> </bean> </beans> 上述方式在使用xml配置中通过继承JdbcDaoSupport来实现对JdbcTemplate的定义和注入进行抽取。减少重复代码。但是在注解开发中,无法在JdbcDaoSupport类中进行注解,所有需要在持久层接口实现类中通过@Autowired注解来注入JdbcTemplate。
PlatformTransactionManager:事务管理器
public interface PlatformTransactionManager { //获取事务状态信息 TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; //提交事务 void commit(TransactionStatus var1) throws TransactionException; //回滚事务 void rollback(TransactionStatus var1) throws TransactionException; }TransactionDefinition:事务的一些基础信息,如超时时间、隔离级别、传播属性等
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable { public static final String PREFIX_PROPAGATION = "PROPAGATION_"; public static final String PREFIX_ISOLATION = "ISOLATION_"; public static final String PREFIX_TIMEOUT = "timeout_"; public static final String READ_ONLY_MARKER = "readOnly"; static final Constants constants = new Constants(TransactionDefinition.class); private int propagationBehavior = 0; private int isolationLevel = -1; private int timeout = -1; private boolean readOnly = false; ...... } private int isolationLevel = -1;:事务的隔离级别反映事物提交并发访问时的处理太多,采用底层数据库默认的隔离级别。private int propagationBehavior = 0;:事务的传播行为 REQUIRED:默认值0,如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)。 private int timeout = -1;:事务的超时时间。默认值为-1,没有时间限制。如果有,按秒为单位设置。private boolean readOnly = false;:是否是只读事务。 事务的隔离级别是数据库本身的事务功能。
事务的传播属性是Spring提供的功能,数据库事务没有事务的传播属性这一说法。
TransactionStatus:事务的一些状态信息,如是否一个新的事务、是否已被标记为回滚
public interface TransactionStatus extends SavepointManager, Flushable { boolean isNewTransaction();//事务是否为新事务 boolean hasSavepoint();//获取事务是否存在保存点 void setRollbackOnly();//设置事务回滚 boolean isRollbackOnly();//事务是否回滚 void flush();//刷新事务 boolean isCompleted();//事务是否完成 }配置事务管理器
配置事务的通知(导入相关约束)
使用<tx:advice>标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个管理器引用
配置aop中的通用切入点表达式
建立事务通知和切入点表达式的对应关系
配置事务的属性
在事务的通知tx:advice标签的内部
属性:
isolation:指定事务的隔离级别。默认值为DEFUALT,表示使用数据库的默认隔离级别。
propagation:指定事务的传播行为。默认为REQUIRED,表示一定有事务,增删改。查询可以选择SUPPORTS。
read-only:指定事务是否只读。只有查询才能设置为true。默认为false。
timeout:指定事务的超时时间。默认-1,表示没有超时限制。单位为秒。
rollback-for:指定一个异常,当产生此异常,事务回滚;产生其他异常事务不回滚。没有默认值,所有异常均回滚。
no-rollback-for:指定一个异常,当产生此一次,事务不回滚;产生其他异常事务回滚。没有默认值,所有异常均回滚。
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://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="accountServiceImpl" class="com.xijianlv.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDaoImpl"></property> </bean> <!-- 配置持久层 --> <bean id="accountDaoImpl" class="com.xijianlv.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/spring?useSSL=false"></property> <property name="username" value="root"></property> <property name="password" value="welcome1"></property> </bean> <!-- 1.配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 2.配置事务的通知 --> <tx:advice id="txAdvic" transaction-manager="transactionManager"> <!-- 5.配置事务的属性 --> <tx:attributes> <!-- 表示所有的方法都设置为非只读,传播行为:一定有事务 --> <tx:method name="*" read-only="false" propagation="REQUIRED"></tx:method> <!-- 表示所有的查询方法都设置为只读,传播行为:不一定有事务。。。。但是查询方法但命名必须符合此规范,这条配置但优先级高于上一条。 --> <tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method> </tx:attributes> </tx:advice> <!-- 配置aop --> <aop:config> <!-- 3.配置aop中的通用切入点表达式 --> <aop:pointcut id="pointcut1" expression="execution(* com.xijianlv.service.impl.*.*(..))"></aop:pointcut> <!-- 4.建立事务通知和切入点表达式的对应关系 --> <aop:advisor advice-ref="txAdvic" pointcut-ref="pointcut1"></aop:advisor> </aop:config> </beans>Dao实现类的修改,不再继承JdbcDaoSupport,需要配置JdbcTemplate。
@Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private JdbcTemplate jdbcTemplate; public Account findAccountById(Integer id) { List<Account> accountList = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id); return accountList.isEmpty() ? null : accountList.get(0); } public Account findAccountByName(String name) { List<Account> accountList = jdbcTemplate.query("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name); if (accountList.isEmpty()) { return null; } if (accountList.size() > 1) { throw new RuntimeException("结果集不唯一。"); } return accountList.get(0); } public void updateAccount(Account account) { jdbcTemplate.update("update account set name=?,money=? where id=?", account.getName(), account.getMoney(), account.getId()); } }Service层的修改,事务的不同,配置的属性也不一致。
@Service("accountServiceImpl") @Transactional(propagation = Propagation.REQUIRED,readOnly = true)//只读型事务的配置 public class AccountServiceImpl implements IAccountService { @Autowired private AccountDao accountDao; public Account findAccountById(int id) { return accountDao.findAccountById(id); } @Transactional(propagation = Propagation.REQUIRED,readOnly = false)//读写型事务的配置 public void transfer(String source, String target, Float money) { System.out.println("transfer..."); Account sourceAccount = accountDao.findAccountByName(source); Account targetAccount = accountDao.findAccountByName(target); sourceAccount.setMoney(sourceAccount.getMoney()-money); targetAccount.setMoney(targetAccount.getMoney()+money); accountDao.updateAccount(sourceAccount); // int i= 10/0; accountDao.updateAccount(targetAccount); } }XML文件修改如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.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:component-scan base-package="com.xijianlv"></context:component-scan> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/spring?useSSL=false"></property> <property name="username" value="root"></property> <property name="password" value="welcome1"></property> </bean> <!-- 基于注解的声明式事务控制配置 1.配置事务管理器 2.开启spring对注解事务对支持 3.在需要事务支持的地方使用@Transactional注解 --> <!-- 1.配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 2.开启spring对注解事务对支持 --> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>