Spring 框架学习第六节:Spring 中的 JdbcTemplate 与事务控制

此博客用于个人学习,来源于ssm框架的书籍,对知识点进行一个整理。

6.1 Spring 中的 JdbcTemplate:

可能有人会疑惑:Spring 中既然有 JdbcTemplate,为什么还需要与 MyBatis 框架一起使用?

两者可以在不同的场景下使用,spring JDBC的性能更好,更适合管理域的sql语句,而mybatis则可以将sql语句批量的管理起来,还可以达到sql的重用,同时mybatis还帮你做了数据库的驱动注册加载,数据库连接的创建与销毁等数据库的管理可以让你更关注于业务,更适合在业务域使用。

6.1.1 Spring 中配置数据源:


6.1.1.1 编写 Spring 的配置文件:

<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 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
beans>
6.1.1.2 配置数据源:
  1. 配置 C3P0 数据源:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
	<property name="driverClass" value="com.mysql.jdbc.Driver">property> 
	<property name="jdbcUrl" value="jdbc:mysql:///spring_day02">property> 
	<property name="user" value="root">property>
	<property name="password" value="1234">property>
bean>
  1. 配置 DBCP 数据源:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 
	<property name="driverClassName" value="com.mysql.jdbc.Driver">property> 
	<property name="url" value="jdbc:mysql:// /spring_day02">property> 
	<property name="username" value="root">property> 
	<property name="password" value="1234">property>
bean>
  1. 配置 Spring 内置数据源:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
	<property name="driverClassName" value="com.mysql.jdbc.Driver">property> 
	<property name="url" value="jdbc:mysql:///spring_day02">property> 
	<property name="username" value="root">property> 
	<property name="password" value="1234">property>
bean>
6.1.1.3 将数据库连接的信息配置到属性文件中:

定义属性文件:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_day02
jdbc.username=root
jdbc.password=123

引入外部的属性文件:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:jdbc.properties"/>
bean>

同样也可以这样引入:

<context:property-placeholder location="classpath:jdbc.properties"/>

6.1.2 JdbcTemplate 的增删改查操作:


6.1.2.1 前期准备:
创建数据库:
create database spring_day02;
use spring_day02;
创建表:
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
6.1.2.2 在 Spring 配置文件中配置 JdbcTemplate:

<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 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
	 
	<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:///spring_day02">property> 
		<property name="username" value="root">property> 
		<property name="password" value="1234">property>
	bean>
beans>
6.1.2.3 插入使用:
public class JdbcTemplateDemo2 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		jt.execute("insert into account(name,money)values('eee',500)");
	} 
}
6.1.2.4 保存操作:
public class JdbcTemplateDemo3 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		//保存
		jt.update("insert into account(name,money)values(?,?)","fff",5000);
	} 
}
6.1.2.5 更新操作:
public class JdbcTemplateDemo3 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		//修改
		jt.update("update account set money = money-? where id = ?",300,6);
	} 
}
6.1.2.6 删除操作:
public class JdbcTemplateDemo3 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		//删除
		jt.update("delete from account where id = ?",6);
	} 
}
6.1.2.7 查询所有操作:
public class JdbcTemplateDemo3 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		//查询所有
		List<Account> accounts = jt.query("select * from account where money > ? ", new AccountRowMapper(), 500);
		for(Account o : accounts){
			System.out.println(o);
		} 
	} 
}

public class AccountRowMapper implements RowMapper<Account>{
	@Override
	public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
		Account account = new Account();
		account.setId(rs.getInt("id"));
		account.setName(rs.getString("name"));
		account.setMoney(rs.getFloat("money"));
		return account;	
	}
}
6.1.2.8 查询一个操作:
  1. 使用 RowMapper 的方式(常用的方式):
public class JdbcTemplateDemo3 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		//查询一个
		List<Account> as = jt.query("select * from account where id = ? ", new AccountRowMapper(), 55);
		System.out.println(as.isEmpty()?"没有结果":as.get(0));
	} 
}
  1. 使用 ResultSetExtractor 的方式(不常用的方式):
public class JdbcTemplateDemo3 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		//查询一个
		Account account = jt.query("select * from account where id = ?",new AccountResultSetExtractor(),3);
		System.out.println(account);
	} 
}
6.1.2.9 查询返回一行一列操作:
public class JdbcTemplateDemo3 {
	public static void main(String[] args) {
		//1.获取 Spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据 id 获取 bean 对象
		JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
		//3.执行操作
		//查询返回一行一列:使用聚合函数,在不使用 group by 字句时,都是返回一行一列。最长用的就是分页中获取总记录条数
		Integer total = jt.queryForObject("select count(*) from account where money > ? ",Integer.class,500);
		System.out.println(total);
	} 
}

6.1.3 在 dao 中使用 JdbcTemplate:


6.1.3.1 准备实体类:
/**
* 账户的实体
*/
public class Account implements Serializable {
	private Integer id;
	private String name;
	private Float money;
	
	public Integer getId() {
		return id; 
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name; 
	}
	public void setName(String name) {
		this.name = name;
	}
	public Float getMoney() {
		return money; 
	}
	public void setMoney(Float money) {
		this.money = money;
	}
	
	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
	} 
}
6.1.3.2 第一种方式:在 dao 中定义 JdbcTemplate:

持久层接口:

/**
* 账户的接口
*/
public interface IAccountDao {
	/**
	* 根据 id 查询账户信息
	* @param id
	* @return
	*/
	Account findAccountById(Integer id);
	/**
	* 根据名称查询账户信息
	* @return
	*/
	Account findAccountByName(String name);
	/**
	* 更新账户信息
	* @param account
	*/
	void updateAccount(Account account);
}

持久层实现类:

/**
* 账户的持久层实现类
* 此版本的 dao,需要给 dao 注入 JdbcTemplate
*/
public class AccountDaoImpl implements IAccountDao {
	private JdbcTemplate jdbcTemplate;
	
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	
	@Override
	public Account findAccountById(Integer id) {
		List<Account> list = jdbcTemplate.query("select * from account where id = ? ",new AccountRowMapper(),id);
		return list.isEmpty()?null:list.get(0);
	}
	
	@Override
	public Account findAccountByName(String name) {
		List<Account> list = jdbcTemplate.query("select * from account where name = ? ",new AccountRowMapper(),name);
		if(list.isEmpty()){
			return null; 
		}
		if(list.size()>1){
			throw new RuntimeException("结果集不唯一,不是只有一个账户对象");
		}
		return list.get(0);
	}
	
	@Override
	public void updateAccount(Account account) {
		jdbcTemplate.update("update account set money = ? where id = ? ",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 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
	 
	<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
		 
		<property name="jdbcTemplate" ref="jdbcTemplate">property>
	bean>
	
	 
	<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:///spring_day04">property> 
		<property name="username" value="root">property> 
		<property name="password" value="1234">property>
	bean>
beans>

但我们可以看出这种方式存在一个问题:就是我们的 dao 有很多时,每个 dao 都有一些重复性的代码。

6.1.3.3 第二种方式:让 dao 继承 JdbcDaoSupport:

持久层接口与上面的一致,实现类做出一些改变:

/**
* 账户的持久层实现类
* 此版本 dao,只需要给它的父类注入一个数据源
*/
public class AccountDaoImpl2 extends JdbcDaoSupport implements IAccountDao {
	@Override
	public Account findAccountById(Integer id) {
		//getJdbcTemplate()方法是从父类上继承下来的。
		List<Account> list = getJdbcTemplate().query("select * from account where id = ? ",new AccountRowMapper(),id);
		return list.isEmpty()?null:list.get(0);
	}
	
	@Override
	public Account findAccountByName(String name) {
		//getJdbcTemplate()方法是从父类上继承下来的。
		List<Account> list = getJdbcTemplate().query("select * from account where name = ? ",new AccountRowMapper(),name);
		if(list.isEmpty()){
			return null; 
		}
		if(list.size()>1){
			throw new RuntimeException("结果集不唯一,不是只有一个账户对象");
		}
		return list.get(0);
	}
	
	@Override
	public void updateAccount(Account account) {
		//getJdbcTemplate()方法是从父类上继承下来的。
		getJdbcTemplate().update("update account set money = ? where id = ? ",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 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
	 
	<bean id="accountDao2" class="com.itheima.dao.impl.AccountDaoImpl2">
		 
		<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:///spring_day04">property> 
		<property name="username" value="root">property> 
		<property name="password" value="1234">property>
	bean>
beans>

注意:
第一种在 Dao 类中定义 JdbcTemplate 的方式,适用于所有配置方式(xml 和注解都可以)。
第二种让 Dao 继承 JdbcDaoSupport 的方式,只能用于基于 XML 的方式,注解用不了。

6.2 Spring 中的事务控制:


6.2.1 基于 XML 的声明式事务控制(配置方式):


6.2.1.1 环境搭建:
  1. 创建 spring 的配置文件并导入约束。
    注:此处需要导入 aop 和 tx 两个名称空间

<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 
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/tx 
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop 
 http://www.springframework.org/schema/aop/spring-aop.xsd">
beans>
  1. 准备数据库表和实体类。
创建数据库:
create database spring_day04;
use spring_day04;
创建表:
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
/**
* 账户的实体
*/
public class Account implements Serializable {
	private Integer id;
	private String name;
	private Float money;
	
	public Integer getId() {
		return id; 
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name; 
	}
	public void setName(String name) {
		this.name = name;
	}
	public Float getMoney() {
		return money; 
	}
	public void setMoney(Float money) {
		this.money = money;
	}
	
	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money + "]"; 
	} 
}
  1. 编写业务层接口和实现类。
/**
* 账户的业务层接口
*/
public interface IAccountService {
	/**
	* 根据 id 查询账户信息
	* @param id
	* @return
	*/
	Account findAccountById(Integer id);//查
	/**
	* 转账
	* @param sourceName 转出账户名称
	* @param targeName 转入账户名称
	* @param money 转账金额
	*/
	void transfer(String sourceName,String targeName,Float money);//增删改
}
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
	private IAccountDao accountDao;
	
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	public Account findAccountById(Integer id) {
		return accountDao.findAccountById(id);
	}
	
	@Override
	public void transfer(String sourceName, String targeName, Float money) {
		//1.根据名称查询两个账户
		Account source = accountDao.findAccountByName(sourceName);
		Account target = accountDao.findAccountByName(targeName);
		//2.修改两个账户的金额
		source.setMoney(source.getMoney()-money);//转出账户减钱
		target.setMoney(target.getMoney()+money);//转入账户加钱
		//3.更新两个账户
		accountDao.updateAccount(source);
		int i=1/0;
		accountDao.updateAccount(target);
	} 
}
  1. 编写 Dao 接口和实现类。
/**
* 账户的持久层接口
*/
public interface IAccountDao {
	/**
	* 根据 id 查询账户信息
	* @param id
	* @return
	*/
	Account findAccountById(Integer id);
	/**
	* 根据名称查询账户信息
	* @return
	*/
	Account findAccountByName(String name);
	/**
	* 更新账户信息
	* @param account
	*/
	void updateAccount(Account account);
}
/**
* 账户的持久层实现类
* 此版本 dao,只需要给它的父类注入一个数据源
*/
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
	@Override
	public Account findAccountById(Integer id) {
		List<Account> list = getJdbcTemplate().query("select * from account where id = ? ",new AccountRowMapper(),id);
		return list.isEmpty()?null:list.get(0);
	}
	
	@Override
	public Account findAccountByName(String name) {
		List<Account> list = getJdbcTemplate().query("select * from account where name = ? ",new AccountRowMapper(),name);
		if(list.isEmpty()){
			return null; 
		}
		if(list.size()>1){
			throw new RuntimeException("结果集不唯一,不是只有一个账户对象");
		}
		return list.get(0);
	}
	
	@Override
	public void updateAccount(Account account) {
		getJdbcTemplate().update("update account set money = ? where id = ? ",account.getMoney(),account.getId());
	} 
}
/**
* 账户的封装类 RowMapper 的实现类
*/
public class AccountRowMapper implements RowMapper<Account>{
	@Override
	public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
		Account account = new Account();
		account.setId(rs.getInt("id"));
		account.setName(rs.getString("name"));
		account.setMoney(rs.getFloat("money"));
		return account;
	} 
}
  1. 在配置文件中配置业务层和持久层。
 
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> 
	<property name="accountDao" ref="accountDao">property>
bean>

 
<bean id="accountDao" class="com.itheima.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:///spring_day04">property> 
	<property name="username" value="root">property> 
	<property name="password" value="1234">property>
bean>
6.2.1.2 配置步骤:
  1. 配置事务管理器。
 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	 
	<property name="dataSource" ref="dataSource">property>
bean>
  1. 配置事务的通知引用事务管理器。
 
<tx:advice id="txAdvice" transaction-manager="transactionManager">
tx:advice>
  1. 配置事务的属性。

属性:

  • read-only:是否是只读事务。默认 false,不只读。
  • isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
  • propagation:指定事务的传播行为。
  • timeout:指定超时时间。默认值为:-1。永不超时。
  • rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。没有默认值,任何异常都回滚。
  • no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值,任何异常都回滚。
 
<tx:attributes>
	<tx:method name="*" read-only="false" propagation="REQUIRED"/>
	<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
tx:attributes>
  1. 配置 AOP 切入点表达式。
 
<aop:config>
	 
	<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pt1"/>
aop:config>
  1. 配置切入点表达式和事务通知的对应关系。
 
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>

6.2.2 基于注解的配置方式:


6.2.2.1 环境搭建:
  1. 创建 spring 的配置文件导入约束并配置扫描的包。

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.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
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd">
 
	 
	<context:component-scan base-package="com.itheima">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_day02">property> 
		<property name="username" value="root">property> 
		<property name="password" value="1234">property>
	bean>
beans>
  1. 创建数据库表和实体类。
    与上面 xml 的一致。
  2. 创建业务层接口和实现类并使用注解让 spring 管理。
/**
* 账户的业务层实现类
*/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
	@Autowired
	private IAccountDao accountDao;
	//其余代码和基于 XML 的配置相同
}
  1. 创建 Dao 接口和实现类并使用注解让 spring 管理。
/**
* 账户的持久层实现类
*/
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	//其余代码和基于 XML 的配置相同
}
6.2.2.2 配置步骤:
  1. 配置事务管理器并注入数据源。
 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
	<property name="dataSource" ref="dataSource">property>
bean>
  1. 在业务层使用@Transactional 注解。
@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements IAccountService {
	@Autowired
	private IAccountDao accountDao;
	
	@Override
	public Account findAccountById(Integer id) {
		return accountDao.findAccountById(id);
	}
	
	@Override
	@Transactional(readOnly=false,propagation=Propagation.REQUIRED)
	public void transfer(String sourceName, String targeName, Float money) {
		//1.根据名称查询两个账户
		Account source = accountDao.findAccountByName(sourceName);
		Account target = accountDao.findAccountByName(targeName);
		//2.修改两个账户的金额
		source.setMoney(source.getMoney()-money);//转出账户减钱
		target.setMoney(target.getMoney()+money);//转入账户加钱
		//3.更新两个账户
		accountDao.updateAccount(source);
		//int i=1/0;
		accountDao.updateAccount(target);
	} 
}
  1. 在配置文件中开启 spring 对注解事务的支持。
 
<tx:annotation-driven transaction-manager="transactionManager"/>
  1. 不使用 xml 的配置方式。
@Configuration
@EnableTransactionManagement
public class SpringTxConfiguration {
//里面配置数据源,配置 JdbcTemplate,配置事务管理器。在之前的步骤已经写过了。
}

你可能感兴趣的:(ssm学习,spring,java,软件框架)