一、Spring JDBC环境搭建
1.引入依赖(添加坐标)
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring 测试环境 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- spring 框架坐标依赖添加 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!-- mysql 驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
<!-- c3p0 连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- spring事物 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
2.添加db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_jdbc?useUnicode=true&characterEncoding=utf8
jdbc.user=root
jdbc.password=123456
3.添加spring.xml文件
1.设置扫描器范围
2.加载properties配置文件
3.配置成c3p0数据源
4.配置JdbcTemplate 模板
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--设置扫描器范围-->
<context:component-scan base-package="com.shsxt"/>
<!-- 加载properties 配置文件 -->
<context:property-placeholder location="db.properties"/>
<!-- 配置c3p0 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- jdbcTemplate 配置 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
4.测试JDBC
@Test
public void testQueryCount() {
// 得到Spring上下文环境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 得到模板类 JdbcTemplate对象
JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");
Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);
System.out.println(count+"-------"+count1);
}
简易版本:
使用注解方式
@RunWith(SpringUnit4ClassRunner.class)
将junit测试加到spring环境中
@ContextConfiguration(locations={“classpath:spring.xml”})
设置加载的资源文件
1.定义父类
@RunWith(SpringJUnit4ClassRunner.class) // 将junit测试加到spring环境中
@ContextConfiguration(locations = {"classpath:spring.xml"}) // 设置要加载的资源文件
public class TestBase {
}
2.继承父类
public class SpringJdbcTest extends TestBase {
@Resource
private JdbcTemplate jdbcTemplate;
@Test
public void testQueryCount() {
Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);
System.out.println(count+"-------"+count1);
}
二、Spring JDBC操作
1.添加操作
1.1定义接口和操作方法
public interface IAccountDao {
/*
1. 添加操作
1.1 添加单条记录,返回受影响的行数
1.2 添加单条记录,返回主键
1.3 添加多条记录,返回受影响的行数
*/
/**
* 添加单条记录,返回受影响的行数
* @param account
* @return
*/
public int addAccount(Account account);
/**
* 添加单条记录,返回主键
* @param account
* @return
*/
public int addAccountHasKey(Account account);
/**
* 添加多条记录,返回受影响的行数
* @param accounts
* @return
*/
public int addAccountBatch(List<Account> accounts);
}
1.2实现接口重写方法
/**
* 添加单条记录,返回受影响的行数
* @param account
* @return
*/
@Override
public int addAccount(Account account) {
String sql = "insert into account(accountName,accountMoney,accountRemark,userId,createTime,updateTime) values (?,?,?,?,now(),now())";
Object[] objs = {account.getAccountName(),account.getAccountMoney(),account.getAccountRemark(),account.getUserId()};
return jdbcTemplate.update(sql,objs);
}
/**
* 添加单条记录,返回主键
* @param account
* @return
*/
@Override
public int addAccountHasKey(Account account) {
String sql = "insert into account(accountName,accountMoney,accountRemark,userId,createTime,updateTime) values (?,?,?,?,now(),now())";
// 定义keyHolder 对象 用户获取记录主键值
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
// 预编译sql语句,并设置返回主键
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// 设置参数
ps.setString(1,account.getAccountName());
ps.setDouble(2,account.getAccountMoney());
ps.setString(3,account.getAccountRemark());
ps.setInt(4,account.getUserId());
return ps;
}
},keyHolder);
// 得到返回的主键
Integer key = keyHolder.getKey().intValue();
return key;
}
/**
* 添加多条记录,返回受影响的行数
* @param accounts
* @return
*/
@Override
public int addAccountBatch(final List<Account> accounts) {
String sql = "insert into account(accountName,accountMoney,accountRemark,userId,createTime,updateTime) values (?,?,?,?,now(),now())";
int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
// 设置参数
preparedStatement.setString(1,accounts.get(i).getAccountName());
preparedStatement.setDouble(2,accounts.get(i).getAccountMoney());
preparedStatement.setString(3,accounts.get(i).getAccountRemark());
preparedStatement.setInt(4,accounts.get(i).getUserId());
}
@Override
public int getBatchSize() {
return accounts.size();
}
}).length;
return rows;
}
1.3测试类
@Test
public void testAddAccount() {
Account account = new Account("wangwu",500.0,2,"第一桶金");
int row = accountDao.addAccount(account);
System.out.println(row);
}
@Test
public void testAddAccountHasKey() {
Account account = new Account("zhaoliu",5000.0,2,"早餐费");
int key = accountDao.addAccountHasKey(account);
System.out.println(key);
}
@Test
public void testAddAccountBatch() {
List<Account> list = new ArrayList<>();
for(int i = 1; i <= 5; i++) {
Account account = new Account("用户"+i,1000.0,2,"小费"+i);
list.add(account);
}
int rows = accountDao.addAccountBatch(list);
System.out.println(rows);
}
2.查询操作
2.1定义方法
/*
2、查询操作
2.1 查询总数量
2.2 查询对象
2.3 查询集合
*/
/**
* 通过用户ID查询账户总记录数
* @param userId
* @return
*/
public int queryTotalCount(int userId);
/**
* 通过主键查询账户对象
* @param accountId
* @return
*/
public Account queryAccount(int accountId);
/**
* 通过指定条件分页查询指定用户下的账户列表
* @param userId
* @param accountName
* @param createTime
* @param pageNum
* @param pageSize
* @return
*/
public List<Account> queryAccountList(int userId,String accountName,String createTime,int pageNum,int pageSize);
2.2实现方法
/**
* 通过用户ID查询账户总记录数
* @param userId
* @return
*/
@Override
public int queryTotalCount(int userId) {
String sql = "select count(1) from account where userId = ?";
int count = jdbcTemplate.queryForObject(sql,Integer.class,userId);
return count;
}
/**
* 通过主键查询账户对象
* @param accountId
* @return
*/
@Override
public Account queryAccount(int accountId) {
String sql = "select accountId,accountName,accountMoney,accountRemark,createTime,updateTime,userId from account where accountId = ?";
Account account = jdbcTemplate.queryForObject(sql, new Object[]{accountId}, new RowMapper<Account>() {
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account acc = new Account();
acc.setAccountId(resultSet.getInt("accountId"));
acc.setAccountMoney(resultSet.getDouble("accountMoney"));
acc.setAccountName(resultSet.getString("accountName"));
acc.setAccountRemark(resultSet.getString("accountRemark"));
acc.setCreateTime(resultSet.getDate("createTime"));
acc.setUpdatetime(resultSet.getDate("updateTime"));
acc.setUserId(resultSet.getInt("userId"));
return acc;
}
});
return account;
}
/**
* 通过指定条件分页查询指定用户下的账户列表
* @param userId
* @param accountName
* @param createTime
* @param pageNum
* @param pageSize
* @return
*/
@Override
public List<Account> queryAccountList(int userId, String accountName, String createTime, int pageNum, int pageSize) {
String sql = "select accountId,accountName,accountMoney,accountRemark,createTime,updateTime,userId from account where userId = ? ";
List<Object> params = new ArrayList<>();
params.add(userId);
// 判断是否有条件查询
if (StringUtils.isNotBlank(accountName)) {
sql += " and accountName like concat('%',?,'%') ";
params.add(accountName);
}
if (StringUtils.isNotBlank(createTime)) {
sql += " and createTime > ? ";
params.add(createTime);
}
// 分页
sql += " limit ?,? ";
params.add((pageNum-1)*pageSize);
params.add(pageSize);
// 将集合转换成数组
Object[] objs = params.toArray();
List<Account> accountList = jdbcTemplate.query(sql, objs, new RowMapper<Account>() {
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account acc = new Account();
acc.setAccountId(resultSet.getInt("accountId"));
acc.setAccountMoney(resultSet.getDouble("accountMoney"));
acc.setAccountName(resultSet.getString("accountName"));
acc.setAccountRemark(resultSet.getString("accountRemark"));
acc.setCreateTime(resultSet.getDate("createTime"));
acc.setUpdatetime(resultSet.getDate("updateTime"));
acc.setUserId(resultSet.getInt("userId"));
return acc;
}
});
return accountList;
}
2.3测试类
@Test
public void testQueryTotalCount() {
System.out.println(accountDao.queryTotalCount(1));
}
@Test
public void testQueryAccount() {
System.out.println(accountDao.queryAccount(1));
}
@Test
public void testQueryAccountList() {
System.out.println(accountDao.queryAccountList(2,"","",1,5).size());
System.out.println(accountDao.queryAccountList(2,"u","2019-12-20",1,5));
}
3.查询操作
3.3定义方法
/*
3、修改操作
3.1 修改单条记录,返回受影响的行数
3.2 修改多条记录,返回受影响的行数
*/
/**
* 修改单条记录,返回受影响的行数
* @param account
* @return
*/
public int updateAccount(Account account);
/**
* 修改多条记录,返回受影响的行数
* @param accounts
* @return
*/
public int updateAccountBatch(List<Account> accounts);
3.2实现方法
/**
* 修改账户信息
* @param account
* @return
*/
@Override
public int updateAccount(Account account) {
String sql = "update account set accountName = ?, accountMoney = ? ,accountRemark = ?,userId = ? ,updateTime = now() where accountId = ? ";
Object[] objs = {account.getAccountName(),account.getAccountMoney(),account.getAccountRemark(),account.getUserId(),account.getAccountId()};
return jdbcTemplate.update(sql,objs);
}
/**
* 批量修改账户信息
* @param accounts
* @return
*/
@Override
public int updateAccountBatch(List<Account> accounts) {
String sql = "update account set accountName = ?, accountMoney = ? ,accountRemark = ?,userId = ? ,updateTime = now() where accountId = ? ";
int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
// 设置参数
ps.setString(1,accounts.get(i).getAccountName());
ps.setDouble(2,accounts.get(i).getAccountMoney());
ps.setString(3,accounts.get(i).getAccountRemark());
ps.setInt(4,accounts.get(i).getUserId());
ps.setInt(5,accounts.get(i).getAccountId());
}
@Override
public int getBatchSize() {
return accounts.size();
}
}).length;
return rows;
}
3.3测试类
@Test
public void testupdateAccount() {
Account account = new Account("adminManager",200.0,1,"最后一桶金");
account.setAccountId(1);
int row = accountDao.updateAccount(account);
System.out.println(row);
}
@Test
public void testUpdateAccountBatch() {
List<Account> list = new ArrayList<>();
for(int i = 6; i <= 10; i++) {
Account account = new Account("账号"+i,10000.0,2,"工资"+i);
account.setAccountId(i);
list.add(account);
}
int rows = accountDao.updateAccountBatch(list);
System.out.println(rows);
}
4.删除操作
4.1定义方法
/*
4、删除操作
4.1 删除单条记录,返回受影响的行数
4.2 删除多条记录,返回受影响的行数
*/
/**
* 删除单条记录,返回受影响的行数
* @param accountId
* @return
*/
public int deleteAccount(int accountId);
/**
* 删除多条记录,返回受影响的行数
* @param ids
* @return
*/
public int deleteAcccountBatch(int[] ids);
4.2实现方法
/**
* 通过账户ID删除账户记录
* @param accountId
* @return
*/
@Override
public int deleteAccount(int accountId) {
String sql = "delete from account where accountId= ? ";
Object[] objs = {5};
return jdbcTemplate.update(sql,objs);
}
/**
* 批量删除账户
* @param ids
* @return
*/
@Override
public int deleteAcccountBatch(int[] ids) {
String sql = "delete from account where accountId = ?";
int row = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setInt(1,ids[i]);
}
@Override
public int getBatchSize() {
return ids.length;
}
}).length;
return row;
}
4.3测试类
@Test
public void testDeleteAccount() {
System.out.println(accountDao.deleteAccount(5));
}
@Test
public void testDeleteAccountBatch() {
System.out.println(accountDao.deleteAcccountBatch(new int[]{6,7,8,9,10}));
}
5.转账场景代码模拟
public class AccountService {
@Resource
private AccountDao accountDao;
/**
* 张三用户给李四用户转账100
* 张三的金额 - 100
* 李四的金额 + 100
* @return
*/
//事务处理
//@Transactional(propagation= Propagation.REQUIRED)
public void toupdateTransMoney() {
// 张三用户
Account zhangsan = accountDao.queryAccount(3);
// 李四用户
Account lisi = accountDao.queryAccount(2);
// 张三的金额 - 100
zhangsan.setAccountMoney(zhangsan.getAccountMoney()-100);
// 修改张三的金额
int r1 = accountDao.updateAccount(zhangsan);
// int i = 1/0;
// 李四的金额 + 100
lisi.setAccountMoney(lisi.getAccountMoney()+100);
// 修改李四的金额
int r2 = accountDao.updateAccount(lisi);
if (r1 == 1 && r2 == 1) {
System.out.println("转账成功!");
} else {
System.out.println("转账失败!");
}
}
}
测试类
@Test
public void testTransMoney() {
accountService.toupdateTransMoney();
}
三、Spring事务控制配置两种方式
1.xml事务配置
1.1 修改xml命名空间,添加
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
1.2 aop代理和aop配置(切入点、通知)
<!--设置扫描器范围-->
<context:component-scan base-package="com.shsxt"/>
<!-- 开启AOP代理 -->
<aop:aspectj-autoproxy />
<!--配置aop(切入点、通知)-->
<aop:config>
<!--设置切入点 设置需要被拦截的方法-->
<aop:pointcut id="cut" expression="execution(* com.shsxt.service..*.*(..))"/>
<!--设置通知 事务通知-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="cut"/>
</aop:config>
1.3 配置事务管理器
<!-- 事务管理器定义 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
1.4 配置事务相关通知
<!-- 配置事务通知 transaction-manager表示事务通知是某个事务管理器管理的-->
<!--
tx:method的属性:
* name 是必须的,表示与事务属性关联的方法名(业务方法名),对切入点进行细化。通配符(*)可以用来指定一批关联到相同的事务属性的方法。
如:'get*'、'handle*'、'on*Event'等等.
propagation 不是必须的 ,默认值是REQUIRED
表示事务传播行为, 包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED
isolation 不是必须的 默认值DEFAULT
表示事务隔离级别(数据库的隔离级别)
timeout 不是必须的 默认值-1(永不超时)
表示事务超时的时间(以秒为单位)
read-only 不是必须的 默认值false不是只读的
表示事务是否只读
rollback-for 不是必须的
表示将被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException'
no-rollback-for 不是必须的
表示不被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException'
任何 RuntimeException 将触发事务回滚
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--对以add update delete query开头饿所有方法进行事务处理-->
<tx:attributes>
<!--定义什么方法需要使用事务 name代表的是方法名(或方法匹配)-->
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="query*" read-only="true" />
</tx:attributes>
</tx:advice>
1.5 配置注解支持
<!--配置事务注解支持-->
<tx:annotation-driven transaction-manager="txManager"/>
2.注解事务配置
2.1 配置事务管理器
<!-- 事务管理器定义 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
2.2 配置注解支持
<!--配置事务注解支持-->
<tx:annotation-driven transaction-manager="txManager"/>
2.3 Service方法上在需要添加事务的方法上加入事物注解
注解: @Transactional(propagation= Propagation.REQUIRED)
public void toupdateTransMoney() {
// 张三用户
Account zhangsan = accountDao.queryAccount(3);
// 李四用户
Account lisi = accountDao.queryAccount(2);
// 张三的金额 - 100
zhangsan.setAccountMoney(zhangsan.getAccountMoney()-100);
// 修改张三的金额
int r1 = accountDao.updateAccount(zhangsan);
// int i = 1/0;
// 李四的金额 + 100
lisi.setAccountMoney(lisi.getAccountMoney()+100);
// 修改李四的金额
int r2 = accountDao.updateAccount(lisi);
if (r1 == 1 && r2 == 1) {
System.out.println("转账成功!");
} else {
System.out.println("转账失败!");
}
}