Spring第三天:Spring的AOP的註解開發、Spring的宣告式事務、JdbcTemplate
阿新 • • 發佈:2018-12-16
目錄
3.2.1PlatformTransactionManager:平臺事務管理器
3.2.2TransactionDefinition :事務定義資訊
3.5Spring的事務管理:第一類:程式設計式事務(需要手動編寫程式碼)--瞭解
3.6Spring的事務管理:第二類:宣告式事務管理(通過配置實現)---AOP
1.Spring的AOP基於AspectJ的註解開發
1.1Spring基於ApsectJ的註解的AOP開發
1.1.1建立專案,引入jar包
1.1.2引入配置檔案
1.1.3編寫目標類並配置
目標類:
package com.albertyy.demo1;
/**
*
* 專案名稱:SpringDay03_AOP
* 類名稱:OrderDao
* 類描述:
* 建立人:yangyangyang
* 建立時間:2018年12月7日 下午2:58:44
* 修改人:yangyangyang
* 修改時間:2018年12月7日 下午2:58:44
* 修改備註:
* @version
*
*/
public class OrderDao {
public void save(){
System.out.println("儲存訂單...");
}
public void update(){
System.out.println("修改訂單...");
}
public String delete(){
System.out.println("刪除訂單...");
return "李君莫";
}
public void find(){
System.out.println("查詢訂單...");
// int d = 1/0;
}
}
在配置檔案配置中配置目標類:
<!-- 配置目標類 -->
<bean id="orderDao" class="com.albertyy.demo1.OrderDao">
</bean>
1.1.4編寫切面類並配置
public class MyAspectAnno {
public void before(){
System.out.println("前置增強.....");
}
}
<!-- 配置切面類 -->
<bean id="myAspect" class="com.albertyy.demo1.MyAspectAnno">
</bean>
1.1.5使用註解的AOP物件目標類進行增強
- 在配置檔案中開啟註解的AOP開發
<!-- 在配置檔案中開啟註解的AOP的開發 -->
<aop:aspectj-autoproxy/>
- 在切面類上使用註解
/**
*
* 專案名稱:SpringDay03_AOP
* 類名稱:MyAspectAnno
* 類描述:註解的切面類
* 建立人:yangyangyang
* 建立時間:2018年12月7日 下午3:00:55
* 修改人:yangyangyang
* 修改時間:2018年12月7日 下午3:00:55
* 修改備註:
* @version
*
*/
@Aspect
public class MyAspectAnno {
@Before(value="execution(* com.albertyy.demo1.OrderDao.save(..))")
public void before(){
System.out.println("前置增強.....");
}
}
1.1.6編寫測試類
package com.albertyy.demo1;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
* 專案名稱:SpringDay03_AOP
* 類名稱:SpringDemo1
* 類描述:Spring的AOP的註解開發
* 建立人:yangyangyang
* 建立時間:2018年12月7日 下午2:59:42
* 修改人:yangyangyang
* 修改時間:2018年12月7日 下午2:59:42
* 修改備註:
* @version
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo1 {
@Resource(name="orderDao")
private OrderDao orderDao;
@Test
public void demo1(){
orderDao.save();
orderDao.update();
orderDao.delete();
orderDao.find();
}
}
1.2Spring的註解的AOP的通知型別
[email protected] :前置通知
@Before(value="execution(* com.albertyy.demo1.OrderDao.save(..))")
public void before(){
System.out.println("前置增強.....");
}
[email protected] :後置通知
// 後置通知:
@AfterReturning(value="execution(* com.albertyy.demo1.OrderDao.delete(..))",returning="result")
public void afterReturning(Object result){
System.out.println("後置增強....."+result);
}
[email protected] :環繞通知
// 環繞通知:
@Around(value="execution(* com.albertyy.demo1.OrderDao.update(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("環繞前增強.....");
Object obj = joinPoint.proceed();
System.out.println("環繞後增強.....");
return obj;
}
[email protected] :異常丟擲通知
// 異常丟擲通知:
@AfterThrowing(value="execution(* com.albertyy.demo1.OrderDao.find(..))",throwing="e")
public void afterThrowing(Throwable e){
System.out.println("異常丟擲增強....."+e.getMessage());
}
[email protected] :最終通知
// 最終通知
@After(value="execution(* com.albertyy.demo1.OrderDao.find(..))")
public void after(){
System.out.println("最終增強.......");
}
1.3Spring的註解的AOP的切入點的配置
package com.albertyy.demo1;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
*
* 專案名稱:SpringDay03_AOP
* 類名稱:MyAspectAnno
* 類描述:註解的切面類
* 建立人:yangyangyang
* 建立時間:2018年12月7日 下午3:00:55
* 修改人:yangyangyang
* 修改時間:2018年12月7日 下午3:00:55
* 修改備註:
* @version
*
*/
@Aspect
public class MyAspectAnno {
@Before(value="MyAspectAnno.pointcut2()")
public void before(){
System.out.println("前置增強.....");
}
// 後置通知:
@AfterReturning(value="MyAspectAnno.pointcut4()",returning="result")
public void afterReturning(Object result){
System.out.println("後置增強....."+result);
}
// 環繞通知:
@Around(value="MyAspectAnno.pointcut3()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("環繞前增強.....");
Object obj = joinPoint.proceed();
System.out.println("環繞後增強.....");
return obj;
}
// 異常丟擲通知:
@AfterThrowing(value="MyAspectAnno.pointcut1()",throwing="e")
public void afterThrowing(Throwable e){
System.out.println("異常丟擲增強....."+e.getMessage());
}
// 最終通知
@After(value="MyAspectAnno.pointcut1()")
public void after(){
System.out.println("最終增強.......");
}
// 切入點註解:
@Pointcut(value="execution(* com.albertyy.demo1.OrderDao.find(..))")
private void pointcut1(){}
@Pointcut(value="execution(* com.albertyy.demo1.OrderDao.save(..))")
private void pointcut2(){}
@Pointcut(value="execution(* com.albertyy.demo1.OrderDao.update(..))")
private void pointcut3(){}
@Pointcut(value="execution(* com.albertyy.demo1.OrderDao.delete(..))")
private void pointcut4(){}
}
2.Spring的JDBC的模板的使用
2.1Spring的JDBC的模板
Spring是EE開發的一站式的框架,有EE開發的每層的解決方案。Spring對持久層也提供瞭解決方案:ORM模組和JDBC的模板。
Spring提供了很多的模板用於簡化開發:
2.1.1JDBC模板使用的入門
- 建立專案,引入jar包
- 引入基本開發包:
- 資料庫驅動
- Spring的JDBC模板的jar包
2.1.2建立資料庫和表
create database spring4_day03;
use spring4_day03;
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
2.1.3使用JDBC的模板:儲存資料
package com.albertyy.jdbc.demo1;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
/**
*
* 專案名稱:SpringDay03_JDBC
* 類名稱:JdbcDemo1
* 類描述:JDBC模板的使用
* 建立人:yangyangyang
* 建立時間:2018年12月7日 下午4:13:47
* 修改人:yangyangyang
* 修改時間:2018年12月7日 下午4:13:47
* 修改備註:
* @version
*
*/
public class JdbcDemo1 {
@Test
// jdbc模板的使用類似於Dbutils.
public void demo1(){
// 建立連線池:
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring4_day03");
dataSource.setUsername("root");
dataSource.setPassword("123456");
// 建立jdbc模板
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
jdbcTemplate.update("insert into account values (null,?,?)", "君陌",10000d);
}
}
2.2將連線池和模板交給Spring管理
2.2.1引入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"
xmlns:context="http://www.springframework.org/schema/context"
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/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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置Spring的內建的連線池======================== -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring4_day03"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置Spring的JDBC的模板========================= -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
2.2.2使用Jdbc的模板
- 引入spring_aop的jar包
package com.albertyy.jdbc.demo1;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
* 專案名稱:SpringDay03_JDBC
* 類名稱:JdbcDemo2
* 類描述: 將連線池和模板交給Spring管理
* 建立人:yangyangyang
* 建立時間:2018年12月7日 下午4:35:27
* 修改人:yangyangyang
* 修改時間:2018年12月7日 下午4:35:27
* 修改備註:
* @version
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcDemo2 {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
// 儲存操作
public void demo1(){
jdbcTemplate.update("insert into account values (null,?,?)", "張小花兒",10000d);
}
}
2.3使用開源的資料庫連線池:
2.3.1DBCP的使用
- 引入jar包
- 配置DBCP連線池
<!-- 配置DBCP連線池=============================== -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring4_day03"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置Spring的JDBC的模板========================= -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
2.3.2C3P0的使用
- 引入c3p0連線池jar包
- 配置c3p0連線池
<!-- 配置C3P0連線池=============================== -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///spring4_day03"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置Spring的JDBC的模板========================= -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
2.4抽取配置到屬性檔案
2.4.1定義一個屬性檔案jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring4_day03
jdbc.username=root
jdbc.password=123456
2.4.2在Spring的配置檔案中引入屬性檔案
- 第一種:
<!-- 第一種方式通過一個bean標籤引入的(很少用) -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"/>
</bean>
- 第二種:
<!-- 第二種方式通過context標籤引入的 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
2.4.3引入屬性檔案的值
<!-- 配置C3P0連線池=============================== -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
2.4.4測試
@Test
// 儲存操作
public void demo1(){
jdbcTemplate.update("insert into account values (null,?,?)", "十三先生",10000d);
}
2.5使用JDBC的模板完成CRUD的操作
2.5.1儲存操作
@Test
// 儲存操作
public void demo1(){
jdbcTemplate.update("insert into account values (null,?,?)", "陳皮皮",10000d);
}
2.5.2修改操作
@Test
// 修改操作
public void demo2(){
jdbcTemplate.update("update account set name = ? ,money = ? where id = ?", "葉紅魚",2000d,1);
}
2.5.3刪除操作
@Test
// 刪除操作
public void demo3(){
jdbcTemplate.update("delete from account where id = ?", 6);
}
2.5.4查詢操作
- 查詢某個屬性
@Test
// 查詢操作:
public void demo4(){
String name = jdbcTemplate.queryForObject("select name from account where id = ?", String.class, 5);
System.out.println(name);
}
@Test
// 統計查詢
public void demo5(){
Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
System.out.println(count);
}
- 查詢返回物件或集合
@Test
// 封裝到一個物件中
public void demo6(){
Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new MyRowMapper(), 5);
System.out.println(account);
}
@Test
// 查詢多條記錄
public void demo7(){
List<Account> list = jdbcTemplate.query("select * from account", new MyRowMapper());
for (Account account : list) {
System.out.println(account);
}
}
- 資料封裝
class MyRowMapper 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.getDouble("money"));
return account;
}
}
3.Spring的事務管理
3.1事務的基本概念
3.1.1什麼事務
- 事務:邏輯上的一組操作,組成這組操作的各個單元,要麼全都成功,要麼全都失敗。
3.1.2事務的特性
- 原子性:事務不可分割.
- 一致性:事務執行前後資料完整性保持一致。
- 隔離性:一個事務的執行不應該受到其他事務的干擾。
- 永續性:一旦事務結束,資料就持久化到資料庫。
3.1.3如果不考慮隔離性引發安全性問題
- 讀問題
- 髒讀 :一個事務讀到另一個事務未提交的資料
- 不可重複讀 :一個事務讀到另一個事務已經提交的update的資料,導致一個事務中多次查詢結果不一致
- 虛讀、幻讀 :一個事務讀到另一個事務已經提交的insert的資料,導致一個事務中多次查詢結果不一致。
- 寫問題
- 丟失更新
3.1.4解決讀問題
- 設定事務的隔離級別
- Read uncommitted :未提交讀,任何讀問題都解決不了。
- Read committed :已提交讀,解決髒讀,但是不可重複讀和虛讀有可能發生。
- Repeatable read :重複讀,解決髒讀和不可重複讀,但是虛讀有可能發生。
- Serializable :解決所有讀問題。
3.2Spring的事務管理的API
3.2.1PlatformTransactionManager:平臺事務管理器
- 平臺事務管理器:介面,是Spring用於管理事務的真正的物件。
- DataSourceTransactionManager :底層使用JDBC管理事務
- HibernateTransactionManager :底層使用Hibernate管理事務
3.2.2TransactionDefinition :事務定義資訊
- 事務定義:用於定義事務的相關的資訊,隔離級別、超時資訊、傳播行為、是否只讀
3.2.3TransactionStatus:事務的狀態
- 事務狀態:用於記錄在事務管理過程中,事務的狀態的物件。
3.2.4事務管理的API的關係:
Spring進行事務管理的時候,首先平臺事務管理器根據事務定義資訊進行事務的管理,在事務管理過程中,產生各種狀態,將這些狀態的資訊記錄到事務狀態的物件中。
3.3Spring的事務的傳播行為
- Spring中提供了七種事務的傳播行為:
保證多個操作在同一個事務中
PROPAGATION_REQUIRED
:預設值,如果A中有事務,使用A中的事務,如果A沒有,建立一個新的事務,將操作包含進來。
PROPAGATION_SUPPORTS
:支援事務,如果A中有事務,使用A中的事務。如果A沒有事務,不使用事務。
PROPAGATION_MANDATORY
:如果A中有事務,使用A中的事務。如果A沒有事務,丟擲異常。
保證多個操作不在同一個事務中
PROPAGATION_REQUIRES_NEW
:如果A中有事務,將A的事務掛起(暫停),建立新事務,只包含自身操作。如果A中沒有事務,建立一個新事務,包含自身操作。
PROPAGATION_NOT_SUPPORTED
:如果A中有事務,將A的事務掛起。不使用事務管理。
PROPAGATION_NEVER
:如果A中有事務,報異常。
巢狀式事務
PROPAGATION_NESTED
:巢狀事務,如果A中有事務,按照A的事務執行,執行完成後,設定一個儲存點,執行B中的操作,如果沒有異常,執行通過,如果有異常,可以選擇回滾到最初始位置,也可以回滾到儲存點。
3.4Spring的事務管理
- 建立Service的介面和實現類
package com.albertyy.tx.demo1;
/**
* 轉賬的業務層的實現類
* @author yxy
*
*/
public class AccountServiceImpl implements AccountService {
// 注入DAO:
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
/**
* from:轉出賬號 to:轉入賬號 money:轉賬金額
*/
public void transfer(final String from, final String to, final Double money) {
accountDao.outMoney(from, money);
int d = 1 / 0;
accountDao.inMoney(to, money);
}
}
- 建立DAO的介面和實現類
package com.albertyy.tx.demo1;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* 轉賬的DAO的實現類
* @author yxy
*
*/
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void outMoney(String from, Double money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?", money,from);
}
@Override
public void inMoney(String to, Double money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money ,to);
}
}
- 配置Service和DAO:交給Spring管理
<!-- 配置Service -->
<bean id="accountService" class="com.albertyy.tx.demo1.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 配置DAO -->
<bean id="accountDao" class="com.albertyy.tx.demo1.AccountDaoImpl">
</bean>
- 在DAO中編寫扣錢和加錢方法:
- 配置連線池和JDBC的模板
<!-- 配置連線池和JDBC的模板 -->
<!-- 第二種方式通過context標籤引入的 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置C3P0連線池=============================== -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
- 在DAO注入Jdbc的模板:
<!-- 配置DAO================= -->
<bean id="accountDao" class="com.albertyy.tx.demo1.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
- 測試
package com.albertyy.tx.demo1;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 測試轉賬的環境
* @author yxy
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx.xml")
public class SpringDemo1 {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void demo1(){
accountService.transfer("張丫丫", "莫山山", 100d);
}
}
3.5Spring的事務管理:第一類:程式設計式事務(需要手動編寫程式碼)--瞭解
3.5.1第一步:配置平臺事務管理器
<!-- 配置平臺事務管理器============================= -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
3.5.2第二步:配置事務管理的模板類
- 配置事務的管理的模板類
<!-- 配置事務管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>