Spring03
回顾
IOC常用注解
若使用注解,就需要在bean.xml中开启组件扫描
<context:component-scan base-package="com.itheima"/>
作用在类上
@Component 将此类产生的对象交给spring容器管理
@Controller
@Service
@Repository
@Scope("prototype|singleton") //一般不用,使用的就是默认值singleton
作用在方法上(了解)
@PostConstruct
@PreDestory
作用在成员变量上(做注入),可以不提供set方法
@Value
@Autowired:
先按照类型自动注入,只有一个此类对象的时候直接注入.
若有多个,就会把变量名作为名字去容器中找,找到了直接注入,找不到报错;
可以搭配@Qualifier("名字") 使用名字+类型完成注入 开发中一般不用@Qualifier
Spring新注解(理解或者了解)
@Configuration:声明当前类是一个spring的配置类 == beans.xml
@Bean:将非自定义对象交给IOC容器,作用在方法上,将方法的返回值(对象)交给容器管理
@PropertySource:加载properties配置文件
@ComponentScan:组件扫描
@Import :导入其他注解类
整合junit:
1.导入依赖(spring-test和junit)
2.在测试类添加俩注解
@ContextConfiguration() 指定配置文件或者配置类
@RunWith() 不实用junit的测试类,改用SpringJunit4ClassRunner.class
3.在需要测试的成员变量上使用@Autowired注入
转账案例:
事务添加在service层.要想业务层和持久层处在同一个事务中,需要保证使用同一个connection连接对象?用ThreadLocal绑定conn对象
在ConnectionManager中的两个方法
从当前线程获取连接
解绑当前线程且归还连接
在TransactionManager中的四个方法
开启事务
提交事务
回滚事务
释放资源
注意:在dao中务必使用线程中绑定的连接来操作数据库.
内容介绍
aop:面向切面编程,使用动态代理技术实现代码和一些操作解耦合
- xml方式
- 注解方式
spring的dao层的解决方案:
- JdbcTemplate:几乎和DBUtils一样
使用spring+springJDBCTemplate搭建转账环境(不考虑事务)
一 转账案例-解耦合
昨日案例存在的问题:
- 不但转账方法需要添加事务管理,其他的操作也需要添加事务.—代码重复
- 业务的代码和事务代码耦合在一起
解决方案:
- 我们可以通过动态代理的方式,对service中的方法进行增强,目标就是:添加事务。这样就不会对业务层产生影响,解决了耦合性的问题和代码复用的问题
1 常用的动态代理技术
jdk动态代理(理解)
需求:在OrderService的save方法执行之前执行自己编写的类中打印时间的方法
public interface OrderService {
void save();
void findAll();
}
public class AopClass {
public void printTime(){
System.out.println("当前时间为:"+new Date());
}
}
public class TestJdkProxy {
@Test
public void test0() {
//1.创建目标对象
OrderService orderService = new OrderServiceImpl();
//2.使用动态代理创建代理对象,对save方法进行增强
OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(
TestJdkProxy.class.getClassLoader(),//类加载器,使用任何一个自己编写的类获取即可
orderService.getClass().getInterfaces(),//代理对象需要实现的接口,和目标对象实现的接口一样
//增强的逻辑
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断方法是否为save方法
if ("save".equals(method.getName())) {
//执行AopClass中的printTime方法
AopClass aopClass = new AopClass();
aopClass.printTime();
}
//方法执行原来的逻辑
Object result = method.invoke(orderService, args);
return result;
}
}
);
orderServiceProxy.save();
System.out.println("-------------");
orderServiceProxy.findAll();
}
}
CGLIB代理(了解)
使用的时候就需要导入cglib的jar包(spring内置了)
public class CategoryService {
public void save(){
System.out.println("save方法执行了");
}
public void findAll(){
System.out.println("findAll方法执行了");
}
}
导入spring-context就有cglib的包
package com.itheima.test;
import com.itheima.aop.AopClass;
import com.itheima.service.CategoryService;
import org.junit.Test;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TestCGLIBProxy {
@Test
public void test0() {
//1.创建目标对象
CategoryService categoryService = new CategoryService();
//2.创建代理对象
CategoryService categoryServiceProxy = (CategoryService) Enhancer.create(CategoryService.class, new MethodInterceptor() {
@Override
/*
method:当前执行的目标方法对象
args:当前执行方法的参数列表
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//判断方法是否为save方法
if ("save".equals(method.getName())) {
//执行AopClass中的printTime方法
AopClass aopClass = new AopClass();
aopClass.printTime();
}
//方法执行原来的逻辑
Object result = method.invoke(categoryService, args);
return result;
}
});
categoryServiceProxy.save();
System.out.println("------");
categoryServiceProxy.findAll();
}
}
2 使用动态代理优化代码
需求:
使用动态代理技术,优化service层的代码,在service中所有业务方法执行之前开启事务,成功之后要提交事务,失败就要回滚事务,最终都要释放资源.
步骤分析:
- 复制我们昨天的有事务的模块,重命名为spring03_1_account_proxy
- 修改service中的代码,只剩下核心业务代码
- 编写一个类AccountServiceProxyFactory,在里面添加一个方法createProxy().
- 在类上添加一个@Component注解,在方法上添加@Bean(name)注解
- 注入TransactionManager对象,注入AccountService对象
- 在方法中创建代理对象,添加事务代码
- 编写测试类
- 从spring容器中获取service的代理对象
代码实现:
- AccountService中删除事务逻辑代码
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String fromUser, String toUser, int money) throws Exception {
//转出操作
int i = accountDao.accountOut(fromUser, money);
if(i != 1){
throw new RuntimeException("转出失败");
}
//转入操作
i = accountDao.accountIn(toUser,money);
if(i != 1){
throw new RuntimeException("转入失败");
}
}
}
- 编写一个创建AccountService代理对象的工厂类
package com.itheima.tx;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
@Component //加入spring管理
public class AccountServiceProxyFactory {
//目标对象
@Autowired
AccountService accountService;
@Autowired
TransactionManager transactionManager;
//创建代理对象,加入spring容器
@Bean("accountServiceProxy")
public AccountService createProxy(){
return (AccountService) Proxy.newProxyInstance(
AccountServiceProxyFactory.class.getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
//开启事务
transactionManager.beginTransaction();
//目标方法执行原来的逻辑
result = method.invoke(accountService, args);
//执行之后,提交事务
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
//若有异常,回滚事务
transactionManager.rollback();
//通知调用者
throw e;
} finally {
//无论如何,释放资源
transactionManager.releaseConn();
}
return result;
}
}
);
}
}
- 编写测试类,注入service的代理对象
二 SpringAOP简介
aop:面向切面编程,他不是一种技术而是一种思想,解决:在不破坏源代码的情况下,实现对业务方法的增强.可以减少重复代码,提高代码重用性,让我们开发者只关心核心业务逻辑的代码
常见的应用场景:
- 日志
- 测试代码性能
- 事务控制
- …
aop思想底层实现技术:JDK、CGLIB,
根据是否有接口选择使用其中一种技术.
相关术语
* target:目标对象
* proxy:代理对象
* joinPoint:连接点,目标对象的所有方法
* pointcut:切点,需要被增强的方法
* advice:通知 (增强的方法)
* aspect:切面, 切点+通知(声明对那些方法在什么时候执行那些操作)
* weaving:织入,动词,将通知织入到切点的过程
三 基于XML的AOP开发
1 快速入门
需求:在执行UserService中save方法之前,执行通知(增强)类中的beforeBeginTransaction方法
步骤分析:
- 创建模块,导入依赖:spring-context(已经包含了spring-aop),spring-test,junit,aspectjweaver(aspectj框架)
- 创建UserService及其实现类,添加save方法
- 在spring配置文件中加入spring容器
- 创建TransactionManager(通知类),添加beforeBeginTransaction方法
- 在spring配置文件中加入spring容器
- 在spring配置文件中配置aop
- 配置切面(在切入点执行之前执行通知类中某个方法)
- 测试
代码实现
UserService
package com.itheima.service;
public interface UserService {
void save();
void findAll();
}
UserServiceImpl
package com.itheima.service.impl;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("userService 中的save方法执行了");
}
public void findAll() {
System.out.println("userService 中的findAll方法执行了");
}
}
TransactionManager 通知类
package com.itheima.aop;
import org.springframework.stereotype.Component;
//通知(增强)类
@Component
public class TransactionManager {
public void beforeBeginTransaction(){
System.out.println("开启事务");
}
}
beans.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--aop配置-->
<aop:config>
<!--
配置切面:切入点+通知
aspect:切面
ref属性:指定增强(通知)类的对象
-->
<aop:aspect ref="transactionManager">
<!--
before:前置通知,在切入点执行之前
method:指定通知类中通知(增强方法)
pointcut:切入点
-->
<aop:before method="beforeBeginTransaction" pointcut="execution(public void com.itheima.service.impl.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
</beans>
测试类
@ContextConfiguration("classpath:beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TestAOP {
@Autowired
UserService userService;
@Test
public void test0() {
userService.save();
System.out.println("----");
userService.findAll();
}
}
1 切入点(切点)表达式
execution([修饰符] 返回值 包名.类名.方法名(参数类型列表))
例:
execution(void com.itheima.service.impl.AccountServiceImpl.transfer(String,String,int))
提示: 表达式中可以使用 * 和 ..
* 任意
.. 可以作用在包上和参数上
包上:当前包及其子包
参数上:参数任意
例如:
-
void com.itheima.service.impl.UserServiceImpl.save(User) :
-
* com.itheima.service.impl.UserServiceImpl.*() :
-
* com.itheima.service.impl.UserServiceImpl.*(…) :
-
* com.itheima.service.impl.*.*(…) :
-
* com.itheima.service…*.*(…) :
-
* com.itheima.service…*.find*(…) :
-
void com.itheima.service.impl.UserServiceImpl.save(User) :指定类中的指定方法,返回值为void,参数为User类型
-
* com.itheima.service.impl.UserServiceImpl.*() : 指定类中任意无参方法,返回值任意
-
* com.itheima.service.impl.UserServiceImpl.*(…) : 指定类中任意方法,返回值任意,参数任意
-
* com.itheima.service.impl.*.*(…) : 指定包下的任意类中的任意方法
-
* com.itheima.service…*.*(…) : 指定包及其子包下任意类中的任意方法
-
* com.itheima.service…*.find*(…) : 指定包及其子包下任意类中的以find开头的方法
2 通知类型
before:前置通知,在切入点执行之前
after-returning:后置通知,在切入点执行成功之后
after-throwing:异常通知,在切入点执行失败之后(发生异常)
after:最终通知,无论如何都要执行
around:环绕通知,以上通知的任意组合
//通知(增强)类
public class TransactionManager {
public void beforeBeginTransaction(){
System.out.println("开启事务");
}
public void afterReturningCommit(){
System.out.println("提交事务");
}
public void afterThrowingRollback(){
System.out.println("回滚事务");
}
public void afterRelease(){
System.out.println("释放资源");
}
}
<aop:aspect ref="transactionManager">
<!--在切入点执行的什么时候 执行通知类中那个方法-->
<!--前置通知-->
<aop:before method="beforeBeginTransaction" pointcut="execution(public void com.itheima.service.impl.UserServiceImpl.save())"/>
<!--后置通知-->
<aop:after-returning method="afterReturningCommit" pointcut="execution(* com.itheima.service..*.save(..))"/>
<!--异常通知-->
<aop:after-throwing method="afterThrowingRollback" pointcut="execution(* com.itheima.service..*.save(..))"/>
<!--最终通知-->
<aop:after method="afterRelease" pointcut="execution(* com.itheima.service..*.save(..))"/>
</aop:aspect>
- 后置通知和异常通知不会同时出现
- 若一个方法执行的时候,需要加两种(含)之上的通知的话,建议大家使用环绕通知
3 环绕通知
在通知类中添加的方法
/*
ProceedingJoinPoint:获取切入点(目标方法)
注意:切入点执行时的返回值要返回回去
*/
public Object aroundTX(ProceedingJoinPoint pjp) throws Throwable {
Object result = null;
try {
//开启事务
beforeBeginTransaction();
//切入点执行
result = pjp.proceed();
//提交事务
afterReturningCommit();
} catch (Throwable throwable) {
throwable.printStackTrace();
//回滚事务
afterThrowingRollback();
//通知调用者
throw throwable;
} finally {
//释放资源
afterRelease();
}
return result;
}
beans.xml
一个逻辑就使用一种通知即可
- 若一种通知能解决逻辑的话就使用指定的通知即可
- 若需要多种通知才能解决逻辑的话 就使用环绕通知,不要使用多种通知的组合了.
4 抽取切入点表达式
四 基于注解的AOP开发
1 快速入门(注解+XML)
需求:在执行UserService中save方法的时候,添加环绕通知
步骤分析:
- 复制刚才模块,重命名为spring03_3_aop_xml_anno
- 在通知类上添加一个注解 @Aspect:声明当前类是一个通知类
- 修改beans.xml
- 开启组件扫描
- 开启aop注解支持(自动代理)
- 在通知类中的方法上使用通知的注解方式声明.
- 测试
代码实现:
通知类的代码:
beans.xml
<!--开启组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--开启aop注解支持(aop自动代理)-->
<aop:aspectj-autoproxy/>
2 抽取切入点
3 纯注解
步骤分析
- 复制spring03_aop_xml_anno,重命名为spring03_aop_anno
- 提供一个spring的配置类(例如:SpringConfig类)
- 开启组件扫描的注解
- 开启aop的注解支持(自动代理)
@ContextConfiguration(classes = SpringConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class TestAOPHello {
@Autowired
UserService userService;
@Test
public void testHello(){
userService.save();
userService.findAll();
}
}
五 使用AOP优化转账案例
使用xml+注解方式优化
步骤分析
- 复制spring03_1_account_proxy,重命名为:spring03_5_account_aop
- 添加依赖 aspectjweaver
- 删除AccountServiceProxyFactory类
- 把TransactionManager变成通知类
- @Aspect
- 在通知类中编写一个方法:aroundTX,作为环绕通知
- @Around(“execution(* com.itheima.service…*.*(…))”)
- 修改beans.xml
- 添加开启组件扫描
- 添加开启aop注解支持(自动代理)
代码实现
TransactionManage
//事务管理器
@Component
@Aspect //声明自己是一个通知类
public class TransactionManager {
@Autowired
private ConnectionManager connectionManager;
@Around("execution(* com.itheima.service..*.*(..))")//环绕通知
public Object aroundTX(ProceedingJoinPoint pjp) throws Throwable {
Object result = null;
try {
beginTransaction();
//执行原来的逻辑
result = pjp.proceed();
commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
rollback();
//通知调用层
throw throwable;
} finally {
release();
}
return result;
}
//开启事务的方法
public void beginTransaction() throws SQLException {
//获取线程上的连接,开启事务
connectionManager.getThreadConnection().setAutoCommit(false);
}
//提交事务的方法
public void commit() throws SQLException {
//获取线程上的连接,提交事务
connectionManager.getThreadConnection().commit();
}
//回滚事务的方法
public void rollback() {
//获取线程上的连接,回滚事务
try {
connectionManager.getThreadConnection().rollback();
} catch (SQLException e) {
}
}
//释放资源的方法
public void release() {
//获取线程上的连接,重置事务状态
try {
connectionManager.getThreadConnection().setAutoCommit(true);
connectionManager.release();
} catch (SQLException e) {
}
}
}
beans.xml添加aop注解支持
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--加载properties-->
<context:property-placeholder location="classpath:db.properties"/>
<!--开启组件扫描-->
<context:component-scan base-package="com.itheima"/>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置queryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!--开启aop注解支持:自动代理-->
<aop:aspectj-autoproxy/>
</beans>
测试类
@ContextConfiguration("classpath:beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TestService {
@Autowired
AccountService accountService;
@Test
public void testTransfer() throws Exception {
accountService.transfer("tom","jackson",100);
}
}
六 Spring的JdbcTemplate
1 JdbcTemplate概述
JdbcTemplate是Spring中的一款工作在Dao层的轻量级框架.
它的使用与DBUtils类似
核心对象
new JdbcTemplate(DataSource ds)
核心方法
方法名 | 作用 |
---|---|
int update(sql,Object … args) | 主要执行DML语句 |
T queryForObject(sql,Class resultType,Object … args) | 执行DQL语句,返回简单类型 |
T queryForObject(sql,RowMapper<>(resultType.class),Object … args) | 执行DQL语句,返回指定的对象 使用BeanPropertyRowMapper类映射 |
List<T> query(sql,RowMapper<>(resultType.class),Object … args) | 执行DQL语句,返回对象列表 |
int[] batchUpdate(sql,List<Object[]> args) | 批量执行DML语句 |
api路径:解压今日资料下的压缩包
快速入门-保存
向account添加一条记录
步骤分析:
- 新建模块,导入依赖(mysql驱动,druid,spring-context,spring-jdbc,spring-test和junit)
- 复制前天的JdbcUtils类和db.properties
- 创建DemoDao
- 在类中提供save()方法
- 在save方法中
- 创建一个JdbcTemplate对象(数据源)
- 调用update方法
- 测试一下
pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2 Spring整合JdbcTemplate
步骤分析
- 创建AccountDao及其实现类 ,添加注解加入spring管理
- 编写save,findById,findAll,findTotalCount,batchInsert方法
- 编写beans.xml
- 加载properties文件
- 开启组件扫描
- 配置数据源
- 配置jdbcTemplate
- 测试
代码实现
pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
beans.xml
<context:property-placeholder location="classpath:db.properties"/>
<context:component-scan base-package="com.itheima"/>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
AccoountDaoImpl
@Repository
public class AccountDaoImpl implements AccountDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int save(Account account) {
return jdbcTemplate.update("insert into account values(null,?,?)",account.getName(),account.getMoney());
}
@Override
public Account findById(int id) {
return jdbcTemplate.queryForObject("select * from account where id = ?",new BeanPropertyRowMapper<>(Account.class),id);
}
@Override
public List<Account> findAll() {
return jdbcTemplate.query("select * from account",new BeanPropertyRowMapper<>(Account.class));
}
@Override
public int findTotalCount() {
return jdbcTemplate.queryForObject("select count(1) from account",int.class);
}
@Override
public int[] batchInsert(List<Object[]> list) {
return jdbcTemplate.batchUpdate("insert into account values(null,?,?)",list);
}
}
测试类
@ContextConfiguration("classpath:beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TestAccountDao {
@Autowired
AccountDao accountDao;
@Test
public void testSave(){
Account account = new Account();
account.setName("肉丝儿");
account.setMoney(60);
accountDao.save(account);
}
@Test
public void testFindById(){
System.out.println(accountDao.findById(3));//使用queryForObject方法必须能找到唯一的结果,否则就会报错
}
@Test
public void testFindAll(){
List<Account> list = accountDao.findAll();
if (list!=null) {
for (Account account : list) {
System.out.println(account);
}
}
}
@Test
public void testFindTotal(){
System.out.println(accountDao.findTotalCount());
}
@Test
public void testBatchInsert(){
List<Object[]> list = new ArrayList<>();
list.add(new Object[]{"jerry",1000});
list.add(new Object[]{"rose",1000});
int[] arr = accountDao.batchInsert(list);
System.out.println(Arrays.toString(arr));
}
}
七 使用JdbcTemplate搭建转账环境
需求:使用JdbcTemplate来搭建,使用xml+注解配置的方式(不考虑事务)
步骤分析:
- 新建模块,导入依赖:(mysql驱动,druid,spring-context,spring-jdbc,spring-test和junit),下次上课的时候还需要导入aspectjweaver
- 复制db.properties
- 创建AccountDao及其实现类,添加注解@Repository
- 注入JdbcTemplate
- 编写两个方法 accountOut和accountIn
- 创建AccountService及其实现类,添加注解@Service
- 注入AccountDao
- 编写transfer方法
- 编写beans.xml
- 加载properties文件
- 开启组件扫描
- 配置数据源
- 配置jdbcTemplate
- 测试
代码实现:
AccountDaoImpl
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public int accountOut(String fromUser, int money) {
return jdbcTemplate.update("update account set money = money - ? where name = ?",money,fromUser);
}
@Override
public int accountIn(String toUser, int money) {
return jdbcTemplate.update("update account set money = money + ? where name = ?",money,toUser);
}
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AccountDao accountDao;
@Override
public void transfer(String fromUser, String toUser, int money) {
//转出操作
int i = accountDao.accountOut(fromUser, money);
if(i != 1){
throw new RuntimeException("转出失败");
}
//转入操作
i = accountDao.accountIn(toUser, money);
if(i != 1){
throw new RuntimeException("转入失败");
}
}
}
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:db.properties"/>
<context:component-scan base-package="com.itheima"/>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
@ContextConfiguration("classpath:beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TestTransfer {
@Autowired
AccountService accountService;
@Test
public void testTransfer(){
accountService.transfer("tom","jack",100);
}
}