本文基于下述教程编写:【B站】ssm教程
在上一天只是实现了AOP
的简单配置,模拟了业务逻辑,并没有真正操作数据库修改数据。也就是没有真正地把AOP
用上场。
Spring的JdbcTemplate基本用法
先来学学简单使用Spring提供的简单数据库操作类:JdbcTemplate
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--Spring核心-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--Spring jdbcTemplate相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--Spring 事务相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
其中,JdbcTemplate
、DriverManagerDataSource
都是来自spring
的jdbc包下,和JdbcTemplate
一样,DriverManagerDataSource
是Spring提供的一个内置数据源,可以与JdbcTemplate
通过构造函数绑定使用,也可以直接调用setDataSource()
方法。
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class jdbcTemplateDemo {
public static void main(String[] args) {
//提供数据源
DriverManagerDataSource driverManagerDataSource=new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/ssmtest");
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setPassword("root");
//创建对象
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(driverManagerDataSource);
//执行方法
jdbcTemplate.execute("insert into spring_account(username,money) values('张三',100)");
}
}
结合AOP思想,可将JdbcTemplate
、DriverManagerDataSource
对象放进Spring容器中:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssmTest"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
测试代码即可简写成:
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jdbcTemplate=ac.getBean("jdbcTemplate",JdbcTemplate.class);
//自定义RowMapper形式
List<Account> accounts=jdbcTemplate.query("select * from spring_account where money<?",new AccountRowMapper(),10000f);
//使用Spring官方RowMapper形式
List<Account> accounts=jdbcTemplate.query("select * from spring_account where MONEY<?",
new BeanPropertyRowMapper<Account>(Account.class), 10000f);
//查询操作
//jdbcTemplate.execute("select * from spring_account");
//保存操作
//jdbcTemplate.update("insert into spring_account(USERNAME,money)values(?,?)","ABC",5555f);
//更新操作
//jdbcTemplate.update("update spring_account set username=?,money=? where id=?","李四",6666,7);
//删除
//jdbcTemplate.update("delete from spring_account where id=?",7);
}
class AccountRowMapper implements RowMapper{
@Override
public Object mapRow(ResultSet rs, int i) throws SQLException {
Account account=new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("username"));
account.setMoney(rs.getDouble("money"));
return account;
}
可以留意到,JdbcTemplate
是通过update()
方法实现增、删、改三大功能的,同时execute()
也是可以直接执行这三块操作。他们的参数都是带占位符的SQL语句以及可变参数组成。
最为复杂的是查询方法query()
,除了带占位符的SQL、可变参数,中间还需要一个RowMapper
对象,其实RowMapper
只是一个接口,要么自定义一个实现它的实现类,要么使用Spring为我们提供的BeanPropertyRowMapper
作为参数传入。两种用意都是帮我们封装查询结果信息到实体类对象。主要区别在于,自定义RowMapper
实现类要写好具体封装逻辑,而BeanPropertyRowMapper
不用,只需要给他对应的实体类class文件即可自动完成封装。
不知道是不是版本问题,BeanPropertyRowMapper
封装时抽风,偶尔的字段信息封装失败为NULL
。改数据库字段实体类成员属性都没办法解决,但最后又莫名正常了。
抽取Dao中变量声明及set方法
有一个显而易见的问题,如果有很多其他实体类的Dao
操作类,上面框框中的代码岂不是都要重复挂一遍?
为减少代码冗余,Spring为我们提供了一个叫JdbcDaoSupport
父类让我们继承,即可不用重复上述代码。
org.springframework.jdbc.core.support.JdbcDaoSupport;
该类内部封装了一个JdbcTemplate
,当Spring通过配置文件实例化DriverManagerDataSource
时会自动完成JdbcTemplate
对象的初始化,那样关于JdbcTemplate
对象的初始化以及其set方法
甚至DriverManagerDataSource
的set方法
都不需要写,Spring自动为我们写好了。只要继承该类,直接在xml配置文件中指定DriverManagerDataSource
即可。
但是如果通过注解配置直接继承该类是无法完成标注自动注入变量的,想简洁代码,只有靠自己写中间类实现与JdbcDaoSupport
类似逻辑。
思考了一下,虽然AccountDao
继承的父类JdbcDaoSupport
中只是声明了JdbcTemplate
,但是也存在setDataSource()
方法:
通过上面注入,AccountDao
一样可以用上Spring容器中的DataSource
,并由此初始化带有数据源的JdbcTemplate
,本质上来讲,SpringIOC的XML配置标签<property>
就是调用对应的set方法,将对应的Bean作为参数引入方法,该类有没有具体声明set方法的bean成员变量无所谓,spring不会去检查。此种写法本意上就是拿到参数调用方法。
总结:只需要配置AccountDao
中的DataSource
即可完成JdbcTemplate
的初始化。
IOC、AOP的CRUD应用
由于实现的事务控制需要通过Connection
的rollback()
方法的途径回滚事务,所以还是要用回Dbutils
和c3p0
操作数据库。模拟一个转账操作。大致流程如下:
表现层Service
操作类:
@Override
public void transfer(String sourceName, String targetName, Float money) {
Account source=accountDao.findAccountByName(sourceName);
Account target=accountDao.findAccountByName(targetName);
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
accountDao.updateAccount(source);
int i=1/0;
accountDao.updateAccount(target);
}
持久化层Dao
操作类: