一.基於xml的AOP開發:
(1)xml配置詳解:
切點表達式:
表達式語法:
execution([修飾符] 返回值類型 包名.類名.方法名(參數列表))
訪問修飾符可以省略
返回值類型、包名、類名、方法名可以使用星號 * 代替,代表任意
包名與類名之間一個點 . 代表當前包下的類,兩個點 .. 表示當前包及其子包下的類
參數列表可以使用兩個點 .. 表示任意個數,任意類型的參數列表
* 版本一:控制目標對象中,返回值類型void且public修飾的所有方法
execution(public void cn.itcast.service.impl.AccountServiceImpl.*(..))
* 版本二:控制目標對象中,任意修飾符任意返回值的所有方法
execution(* cn.itcast.service.impl.AccountServiceImpl.*(..))
* 版本三:控制service層所有對象的方法
execution(* cn.itcast.service..*.*(..))
切點表達式抽取:
(2)通知類型:
通知的配置語法:
<aop:通知類型 method="通知類中方法名" pointcut="切點表達式"></aop:通知類型>
四大通知:
名稱 | 標籤 | 說明 |
---|---|---|
前置通知 | aop:before | 在切入點方法之前執行 |
後置通知 | aop:afterReturning | 在切入點方法正常運行之後執行 |
異常通知 | aop:afterThrowing | 在切點方法發生異常的時候執行 |
最終通知 | aop:after | 無論切入點方法執行時是否有異常,都會執行 |
@Component
public class MyAdvice {
// 前置增強
public void before() {
System.out.println("前置通知...");
}
// 後置增強
public void afterReturning(){
System.out.println("後置通知...");
}
// 異常增強
public void afterThrowing(){
System.out.println("異常通知...");
}
// 最終增強
public void after(){
System.out.println("最終通知...");
}
}
注意: 一般情況下,我們不會同時使用四大通知, 因爲xml配置順序可能會打亂我們的執行計劃。
重要: 四大通知一般單獨使用:
環繞通知:
名稱 | 標籤 | 說明 |
---|---|---|
環繞通知 | aop:around | 可以靈活實現四大通知的所有效果 |
環繞通知的代碼編寫,更貼近於動態代理的底層代碼
注意:測試環繞通知,需要註釋掉四大通知:
// 環繞通知
// Proceeding(運行)JoinPoint(連接點) = 切點
public void around(ProceedingJoinPoint pjp){
try {
System.out.println("前置通知...");
// 執行切點(調用目標對象原有的方法...)
pjp.proceed();
System.out.println("後置通知...");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("異常通知...");
} finally {
System.out.println("最終通知...");
}
}
<aop:around method="around" pointcut-ref="myPointcut"></aop:around>
總結:
* aop織入的配置
<aop:config>
<aop:aspect ref=“通知類”>
<aop:before method=“通知方法名稱” pointcut=“切點表達式"></aop:before>
</aop:aspect>
</aop:config>
* 通知的類型
前置通知、後置通知、異常通知、最終通知
環繞通知
* 切點表達式
execution([修飾符] 返回值類型 包名.類名.方法名(參數列表))
(2)基於xml的AOP開發
MyAdvice:
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
public void before(){
System.out.println("前置通知before");
}
public void afterRutruning(){
System.out.println("後置通知afterRutruning");
}
public void afterThrowing(){
System.out.println("異常通知afterThrowing");
}
public void after(){
System.out.println("最後通知after");
}
public void around(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("前置通知before");
proceedingJoinPoint.proceed();
System.out.println("後置通知afterRutruning");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("異常通知afterThrowing");
}finally {
System.out.println("最後通知after");
}
}
}
@Service
public class AccountServiceImpl implements AccountService {
@Override
public void transfer() {
System.out.println("轉賬了");
}
}
applicationContext.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"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!--目標對象,交給ioc-->
<bean id="accountService" class="com.wsl.sercice.impl.AccountServiceImpl"></bean>
<!--通知對象,交給ioc-->
<bean id="myAdvice" class="com.wsl.advice.MyAdvice"></bean>
<!--aop配置-->
<aop:config>
<aop:pointcut id="mypoint" expression="execution(* com.wsl.sercice..*.*(..))"></aop:pointcut>
<aop:aspect ref="myAdvice">
<!--<aop:before method="before" pointcut-ref="mypoint"></aop:before>-->
<!--<aop:after-returning method="afterRutruning" pointcut-ref="mypoint"></aop:after-returning>-->
<!--<aop:after-throwing method="afterThrowing" pointcut-ref="mypoint"></aop:after-throwing>-->
<!--<aop:after method="after" pointcut-ref="mypoint"></aop:after>-->
<aop:around method="around" pointcut-ref="mypoint"></aop:around>
</aop:aspect>
</aop:config>
</beans>
測試:
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {
@Autowired
AccountService accountService;
@Test
public void test001(){
accountService.transfer();
}
}
二.基於註解的AOP的開發:
(1)項目結構:
開啓spring的aop註解支持
MyAdvice將通知對象升級爲切面
通知+切點=切面
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAdvice {
// @Before("execution(* com.wsl.service..*.*(..))")
// public void before(){
// System.out.println("前置通知before");
// }
//
// @AfterReturning("execution(* com.wsl.service..*.*(..))")
// public void afterRutruning(){
// System.out.println("後置通知afterRutruning");
// }
//
// @AfterThrowing("execution(* com.wsl.service..*.*(..))")
// public void afterThrowing(){
// System.out.println("異常通知afterThrowing");
// }
//
// @After("execution(* com.wsl.service..*.*(..))")
// public void after(){
// System.out.println("最後通知after");
// }
@Pointcut("execution(* com.wsl.service..*.*(..))")
public void myPontCut(){}
@Around("MyAdvice.myPontCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("前置通知before");
proceedingJoinPoint.proceed();
System.out.println("後置通知afterRutruning");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("異常通知afterThrowing");
}finally {
System.out.println("最後通知after");
}
}
}
@Service
public class AccountServiceImpl implements AccountService {
@Override
public void transfer() {
// int i = 9/0;
System.out.println("anno 轉賬了");
}
}
<?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"
xmlns:p="http://www.springframework.org/schema/p"
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.wsl"></context:component-scan>
<!--aop自動註解配置-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
import com.wsl.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {
@Autowired
AccountService accountService;
@Test
public void Test001(){
accountService.transfer();
}
}
(2)註解配置詳解:
切點表達式: 語法與xml一致
如何切面類中,抽取切點表達式:
通知類型:
四大通知:
名稱 | 標籤 | 說明 |
---|---|---|
前置通知 | @Before | 在切入點方法之前執行 |
後置通知 | @AfterReturning | 在切入點方法正常運行之後執行 |
異常通知 | @AfterThrowing | 在切點方法發生異常的時候執行 |
最終通知 | @After | 無論切入點方法執行時是否有異常,都會執行 |
注意:使用註解時,四大通知同時開啓的順序:
@Before -- > @After --> @AfterReturning(@AfterThrowing)
註解版本的四大通知,單獨使用...
環繞通知:
名稱 | 標籤 | 說明 |
---|---|---|
環繞通知 | @Around | 可以靈活實現四大通知的所有效果 |
(3)純註解配置:
不適用配置文件:
@Component
@Aspect
public class MyAdvice {
// @Pointcut("execution(* com.wsl.service..*.*(..))")
@Pointcut("execution(* com.wsl.service..*.*(..))")
public void myPontCut(){}
// @Before("execution(* com.wsl.service..*.*(..))")
// public void before(){
// System.out.println("前置通知before");
// }
//
// @AfterReturning("execution(* com.wsl.service..*.*(..))")
// public void afterRutruning(){
// System.out.println("後置通知afterRutruning");
// }
//
// @AfterThrowing("execution(* com.wsl.service..*.*(..))")
// public void afterThrowing(){
// System.out.println("異常通知afterThrowing");
// }
//
// @After("execution(* com.wsl.service..*.*(..))")
// public void after(){
// System.out.println("最後通知after");
// }
@Around("MyAdvice.myPontCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("前置通知before");
proceedingJoinPoint.proceed();
System.out.println("後置通知afterRutruning");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("異常通知afterThrowing");
}finally {
System.out.println("最後通知after");
}
}
}
配置文件
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.wsl")
@EnableAspectJAutoProxy
public class SpringConfig {
}
@Service
public class AccountServiceImpl implements AccountService {
@Override
public void transfer() {
// int i = 9/0;
System.out.println("anno 轉賬了");
}
}
import com.wsl.config.SpringConfig;
import com.wsl.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountTest {
@Autowired
AccountService accountService;
@Test
public void test001(){
accountService.transfer();
}
}
總結:
* 使用@Aspect註解,標註切面類
* 使用@Before等註解,標註通知方法
* 使用@Pointcut註解,抽取切點表達式
* 配置aop自動代理 <aop:aspectj-autoproxy/> 或 @EnableAspectJAutoProxy
三.AOP優化轉賬案例:
(1)xml實現
在原來項目基礎上,添加這個依賴:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.15</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
相關類:
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner queryRunner;
@Autowired
private ConnectionUtils connectionUtils;
@Override
public void outUser( String outUser, Double money) {
String sql = "update account set money= money-? where name=?";
try {
Connection threadConnection = connectionUtils.getThreadConnection();
queryRunner.update(threadConnection,sql,money,outUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void inUser(String inUser, Double money) {
String sql = "update account set money= money+? where name=?";
try {
Connection threadConnection = connectionUtils.getThreadConnection();
queryRunner.update(threadConnection,sql,money,inUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String outUser, String inUser, Double money) {
accountDao.outUser(outUser,money);
accountDao.inUser(inUser,money);
}
}
@Component
public class ConnectionUtils {
@Autowired
private DataSource dataSource;
private static final ThreadLocal<Connection> t1= new ThreadLocal<>();
public Connection getThreadConnection(){
Connection connection = t1.get();
if (connection==null){
try {
connection = dataSource.getConnection();
t1.set(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
public void removeThreadConnection(){
t1.remove();
}
}
通知對象:環繞通知:
@Component
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void release(){
try {
connectionUtils.getThreadConnection().setAutoCommit(true);
connectionUtils.getThreadConnection().close();
connectionUtils.removeThreadConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
// 環繞通知
public void aroundTx(ProceedingJoinPoint pjp){
try {
beginTransaction();
pjp.proceed();
commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
rollback();
} finally {
release();
}
}
}
<?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"
xmlns:p="http://www.springframework.org/schema/p"
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.wsl"/>
<!--加載第三方配置-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--druid連接交給ioc容器-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--queryRunner交給ioc容器-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--aop配置-->
<aop:config>
<!--切面-->
<aop:aspect ref="transactionManager">
<!--織入 環繞通知-->
<aop:around method="aroundTx" pointcut="execution(* com.wsl.service..*.*(..))"></aop:around>
</aop:aspect>
</aop:config>
</beans>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db
jdbc.username=root
jdbc.password=root
測試
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {
@Autowired
private AccountService accountService;
@Test
public void test001(){
accountService.transfer("tom","jerry",100d);
}
}
(2)常用註解實現版本:
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner queryRunner;
@Autowired
private ConnectionUtils connectionUtils;
@Override
public void outUser( String outUser, Double money) {
String sql = "update account set money= money-? where name=?";
try {
Connection threadConnection = connectionUtils.getThreadConnection();
queryRunner.update(threadConnection,sql,money,outUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void inUser(String inUser, Double money) {
String sql = "update account set money= money+? where name=?";
try {
Connection threadConnection = connectionUtils.getThreadConnection();
queryRunner.update(threadConnection,sql,money,inUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String outUser, String inUser, Double money) {
accountDao.outUser(outUser,money);
accountDao.inUser(inUser,money);
}
}
環繞通知:
package com.wsl.tx;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
@Component
@Aspect
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void release(){
try {
connectionUtils.getThreadConnection().setAutoCommit(true);
connectionUtils.getThreadConnection().close();
connectionUtils.removeThreadConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
// 環繞通知
@Around("execution(* com.wsl.service..*.*(..))")
public void aroundTx(ProceedingJoinPoint pjp){
try {
beginTransaction();
pjp.proceed();
commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
rollback();
} finally {
release();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.wsl"></context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {
@Autowired
AccountService accountService;
@Test
public void test001(){
accountService.transfer("jerry","tom",100d);
}
}
四。Spring的JdbcTemplate
(1)JdbcTemplate是什麼?
JdbcTemplate是Spring的一款用於簡化Dao代碼的工具包,它底層封裝了JDBC技術。
**核心對象**
JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);
核心方法
int update(); 執行增、刪、改語句
List<T> query(); 查詢多個
T queryForObject(); 查詢一個
RowMapper<>(); ORM映射接口
new BeanPropertyRowMapper<>(); 實現ORM映射封裝子類
例如:
查詢數據庫所有賬戶信息到Account實體中
public class JdbcTemplateTest {
@Test
public void testFindAll() throws Exception {
// 創建核心對象
JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
// 編寫sql
String sql = "select * from account";
// 執行sql
List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
}
}
(2)入門案例:
依賴座標:mysql驅動、druid連接池、spring-jdbc、spring-context、junit、spring-junit
<!--依賴管理-->
<dependencies>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--druid連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.15</version>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--spring核心-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--spring整合junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
工具類連接池:
public class JdbcUtils {
private static DruidDataSource dc = new DruidDataSource();
static {
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driverClass = bundle.getString("jdbc.driver");
String jdbcUrl = bundle.getString("jdbc.url");
String username = bundle.getString("jdbc.username");
String password = bundle.getString("jdbc.password");
dc.setDriverClassName(driverClass);
dc.setUrl(jdbcUrl);
dc.setUsername(username);
dc.setPassword(password);
}
public static Connection getConnection() throws SQLException {
return dc.getConnection();
}
public static DataSource getDataSource(){
return dc;
}
}
新測試代碼:
public class JdbcTemplateTest {
// 新增
@Test
public void test01() throws Exception {
// 1.創建JdbcTemplate核心對象
JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
// 2.編寫sql
String sql = "insert into account(name,money) values(?,?)";
// 3.執行sql
int i = jdbcTemplate.update(sql, "哈哈頂", 1000d);
}
}
查詢:
測試案例:
// id查詢
@Test
public void test02() throws Exception {
// 1.創建JdbcTemplate核心對象
JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
// 2.編寫sql
String sql = "select * from account where id = ?";
// 3.執行sql
Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), 4);
System.out.println(account);
}
(3)Spring整合JdbcTemplate 以及轉賬案例
編寫AccountDao
import com.wsl.dao.AccountDao;
import com.wsl.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<Account> findAll() {
String sql = "select * from account";
List<Account> accountList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
return accountList;
}
@Override
public int add(Account account) {
String sql ="insert into account(name,money) values(?,?)";
int update = jdbcTemplate.update(sql, account.getName(), account.getMoney());
return update;
}
@Override
public int delete(Integer aid) {
String sql ="delete from account where id = ? ";
int update = jdbcTemplate.update(sql, aid);
return update;
}
@Override //update score set chinese=60 where id=?
public int update(Account account) {
String sql = "update account set name= ? , money = ? where id= ? ";
int update = jdbcTemplate.update(sql, account.getName(), account.getMoney(), account.getId());
return update;
}
@Override
public Account findById(Integer aid) {
String sql = "select * from account where id = ?";
Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), aid);
return account;
}
@Override
public void outUser(String outUser, Double money) {
String sql ="update account set money = money - ? where name = ?";
jdbcTemplate.update(sql,money,outUser);
}
@Override
public void inUser(String inUser, Double money) {
String sql ="update account set money = money + ? where name = ?";
jdbcTemplate.update(sql,money,inUser);
}
}
編寫AccountService
import com.wsl.dao.AccountDao;
import com.wsl.domain.Account;
import com.wsl.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AccountDao accountDao;
@Autowired
private PlatformTransactionManager transactionManager;
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
@Override
public int add(Account account) {
return accountDao.add(account);
}
@Override
public int delete(Integer aid) {
return accountDao.delete(aid);
}
@Override
public int update(Account account) {
return accountDao.update(account);
}
@Override
public Account findById(Integer aid) {
return accountDao.findById(aid);
}
@Override
public void transfer(String outUser, String inUser, Double money) {
DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
defaultTransactionDefinition.setReadOnly(false);
defaultTransactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);
try{
accountDao.outUser(outUser,money);
accountDao.inUser(inUser,money);
transactionManager.commit(status);
}catch (Exception e){
e.printStackTrace();
transactionManager.rollback(status);
}
}
}
編寫Spring配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.wsl"></context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--<aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
</beans>
測試:
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {
@Autowired
AccountService accountService;
@Test
public void test001(){
//查詢所有
// List<Account> accountList = accountService.findAll();
// System.out.println(accountList);
//查詢id=1的用戶
// Account account = accountService.findById(1);
// System.out.println(account);
//新增用戶
// Account account1 = new Account();
// account1.setName("beijing");
// account1.setMoney(1200.0);
// accountService.add(account1);
//修改信息
// Account account2 = new Account();
// account2.setName("beijing");
// account2.setMoney(9100.0);
// account2.setId(8);
// accountService.update(account2);
// accountService.delete(10);
accountService.transfer("tom","jerry",200d);
}
}
使用JdbcTemplate,無法操作自定義的事務管理器....
因爲spring當時在設計這套工具包的時候,就要求事務交給spring控制
五.Spring的事務:
Spring的事務控制可以分爲編程式事務控制和聲明式事務控制。
編程式事務
-
就是將業務代碼和事務代碼放在一起書寫,它的耦合性太高,開發中不使用
聲明式事務
-
其實就是將事務代碼(spring內置)和業務代碼隔離開發, 然後通過一段配置讓他們組裝運行, 最後達到事務控制的目的.
聲明式事務就是通過AOP原理實現的.
(1)編程式事務:
PlatformTransactionManager
spring事務管理器的頂級接口,裏面提供了我們常用的操作事務的方法
- TransactionStatus getTransaction(TransactionDefinition definition);
功能:獲取事務的狀態信息
- void commit(TransactionStatus status);
功能:提交事務
- void rollback(TransactionStatus status);
功能:回滾事務
spring事務管理器先定義好規範,真正的執行者需要實現類完成,導入一個座標 spring-orm
- JpaTransactionManager
會使用sun公司提供的jpa規範(SpringData-jpa)框架事務管理器
- DataSourceTransactionManager
使用 mybatis、jdbc原生、DbUtils、JdbcTemplate框架事務管理
- HibernateTransactionManager
使用hibernate框架事務管理器
TransactionDefinition
spring事務定義參數的接口,比如定義:事務隔離級別、事務傳播行爲等等
① 事務隔離級別
isolation:事務的隔離級別
* ISOLATION_DEFAULT 使用數據庫默認級別
MySQL:ISOLATION_REPEATABLE_READ 可重複讀
Oracle:ISOLATION_READ_COMMITTED 讀已提交
* ISOLATION_READ_UNCOMMITTED 讀未提交
* ISOLATION_READ_COMMITTED 讀已提交
* ISOLATION_REPEATABLE_READ 可重複讀
* ISOLATION_SERIALIZABLE 串行化
② 事務傳播行爲
事務傳播行爲指的就是當一個業務方法【被】另一個業務方法調用時,應該如何進行事務控制。
比如:
方法A、方法B【主語】
方法A去調用方法B
** REQUIRED(默認傳播行爲)
如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。
如果單獨調用方法B時,沒有事務,spring就給當前方法創建一個新事物
如果方法A中已經存在了事務,調用方法B時,方法B加方法A的事務中....
** SUPPORTS
支持當前事務,如果當前沒有事務,就以非事務方式執行
如果單獨調用方法B時沒有事務,咱們就以非事務方法運行
如果方法A中已經存在了事務,調用方法B時,方法B加方法A的事務中....
--------------------------------------------------------------------------------------
* MANDATORY
使用當前的事務,如果當前沒有事務,就拋出異常
* REQUERS_NEW
新建事務,如果當前在事務中,把當前事務掛起
* NOT_SUPPORTED
以非事務方式執行操作,如果當前存在事務,就把當前事務掛起
* NEVER
以非事務方式運行,如果當前存在事務,拋出異常
* NESTED
如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行 REQUIRED 類似的操作
③ 是否只讀
* read-only 只讀事務(增 刪 改不能使用,只能查詢使用)
④ 超時時間
* timeout 默認值是-1,沒有超時限制。如果有,以秒爲單位進行設置
TransactionStatus
獲取spring當前事務運行的狀態。
總結:
Spring中的事務控制主要就是通過這三個API實現的
* PlatformTransactionManager 負責事務的管理,它是個接口,其子類負責具體工作
* TransactionDefinition 定義了事務的一些相關參數
* TransactionStatus 代表事務運行的一個實時狀態
可以簡單的理解三者的關係:事務管理器通過讀取事務定義參數進行事務管理,然後會產生一系列的事務狀態。
(2)使用編程式事務
配置事務管理器:
<!--事務管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
修改service層代碼:
@Service
public class xxxServiceImpl implements xxxService{
@Autowired
private PlatformTransactionManager transactionManager;
public void method() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 設置是否只讀,爲false才支持事務
def.setReadOnly(false);
// 設置隔離級別
def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
// 設置事務的傳播行爲
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 對事務管理器進行配置
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 業務操作
// 提交事務
transactionManager.commit(status);
} catch (Exception e) {
e.printStackTrace();
// 回滾事務
transactionManager.rollback(status);
}
}
}