SpringJDBCTemplate和事务控制

tech2022-07-16  152

Spring_JDBCTemplate和事务控制

JDBCTemplate

​ 它是Spring提供的一个对象,是对原始的Jdbc Api对象的简单封装。Spring提供了很多操作模版类。

操作关系型数据库:JdbcTemplate、HibernateTemplate操作nosql数据库:RedisTemplate操作消息队列:JmsTemplate

JdbcTemplate作用

​ 用于和数据库交互,实现对表的CRUD操作。

JdbcTemplate的获取

<?xml version="1.0" encoding="UTF-8"?> <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" > <!-- 配置JdbcTemplate --> <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> </beans> //获取容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //获取JdbcTemplate对象 JdbcTemplate jt = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);

JdbcTemplate的CRUD

public static void main(String[] args) { //获取容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //获取JdbcTemplate对象 JdbcTemplate jt = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class); //执行操作 //保存 jt.update("insert into account(name,money) values (?,?)", "fff", 1000f); //更新 jt.update("update account set name=?,money=? where id=?","update_test",4567f,7); //删除 jt.update("delete from account where id=?",6); //查询所有 //new BeanPropertyRowMapper<Account>(Account.class)想当于DBUtils中当BeanHandler。 List<Account> accounts = jt.query("select * from account where id>?", new BeanPropertyRowMapper<Account>(Account.class), 3); for (Account account : accounts) { System.out.println(account); } //查询一个 List<Account> accounts1 = jt.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), 3); System.out.println(accounts1.isEmpty() ? "没有内容" : accounts1.get(0)); //聚合函数 Long count = jt.queryForObject("select count(*) from account where id > ?",Long.class,2); System.out.println(count); }

JdbcTemplate在DAO中的操作

/*** 持久层接口*/ public interface AccountDao { Account findAccountById(Integer id);//根据id查询账户 Account findAccountByName(String name);//根据名称查询账户 void updateAccount(Account account);//更新账户 } /********************************************************************************************************************************************************************************************/ public class AccountDaoImpl implements AccountDao { private JdbcTemplate jdbcTemplate = null; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.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()); } }

JdbcDaoSupport的使用

​ 抽取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();//事务是否完成 }

基于xml配置的事务控制

配置事务管理器

配置事务的通知(导入相关约束)

使用<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>

基于注解的事务控制

配置事务管理器开启spring对注解事务对支持在需要事务支持的地方使用@Transactional注解

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>
最新回复(0)