SSM Spring IOC(控制反轉)& DI(依賴註入) IOC是容器,用來裝Spring創建的Bean對象。 Bean註入方式 Set方法註入 構造器註入 @Autowared自動註入 <!-- SpringBean的註入方式 --> <!-- 手動裝配 --> <!-- == set方法註入 ...
SSM
目錄Spring
IOC(控制反轉)& DI(依賴註入)
IOC是容器,用來裝Spring創建的Bean對象。
Bean註入方式
- Set方法註入
- 構造器註入
- @Autowared自動註入
<!-- SpringBean的註入方式 -->
<!-- 手動裝配 -->
<!-- == set方法註入 -->
<bean id="hello" class="com.dzqc.smbms.entity.SUser">
<property name="userCode" value="hello"></property>
<property name="userName" value="Spring"></property>
<property name="role">
<bean class="com.dzqc.smbms.entity.SRole">
<property name="roleCode" value="這裡是註入的實例對象"></property>
<property name="roleName" value="這裡是註入的實例對象的值"></property>
</bean>
</property>
<property name="role" ref="myRole"></property>
</bean>
<bean name="myRole" class="com.dzqc.smbms.entity.SRole">
<property name="roleName" value="管理員"></property>
<property name="roleCode" value="SMBMS_ADMIN"></property>
</bean>
<!-- == 帶參構造器註入 -->
<bean name="constructor" class="com.dzqc.smbms.entity.SUser">
<constructor-arg name="userName" value="這是通過構造器註入的userName"></constructor-arg>
<constructor-arg name="userCode" value="這是通過構造器註入的userCode"></constructor-arg>
</bean>
<!-- 自動裝配 -->
<!-- == 配置包掃描,並使用註解進行註入 -->
<context:component-scan base-package="com.dzqc.smbms">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
IOC和DI的區別:
IOC是什麼:是控制反轉,將new對象的權力由調用程式交給第三方IOC容器,控制權被轉移給了IOC容器,在調用程式看來,new對象的動作由主動變成了被動,所以叫做控制反轉。
DI是什麼:依賴註入,將IOC容器中new出來的對象註入進調用程式。
IOC和ID的區別:IOC和DI是一體的,DI是IOC的另一種表現形式,是同一事件不同層面的解讀。
AOP(面向切麵編程)
一、AOP編程思想
1.1 什麼是AOP
AOP (Aspect Orient Programming),直譯過來就是 面向切麵編程。AOP是一種編程思想,基於面向對象編程思想(OOP)的補充。面向對象編程將程式抽象成了各個切麵。
1.2 為什麼需要AOP
在開發過程中,存在某段多次重覆的代碼,以面向過程編程方式的話,我們會將這段代碼抽象成一個方法,在需要的位置調用該方法。當這段代碼需要修改時,我們就只需要改變這一個方法就夠了。
但需求不是一成不變的,如果後期需要新增一個需求,又需要在多處進行修改,就需要再抽象一個方法,然後在需要的位置再分別調用這個方法。又或者,我們在業務過程中刪除這個方法,我們就需要刪除掉每一個地方的調用。
在這種情況之下,我們就可以通過使用AOP來解決。
1.3 AOP實現方法分類
首先我們要知道AOP的目的,AOP要達到在開發者不修改源代碼的前提下,去為系統中的業務添加某種通用的功能。
AOP的實現方式大致可以分為兩類:
- 靜態AOP實現
- 靜態AOP實現是在編譯階段就對程式源代碼進行修改,生成了靜態的AOP代理類,即生成的
*.class
文件已經被修改,需要使用特定的編譯方式進行編譯。代表性的靜態AOP實現方式如:AspectJ
- 靜態AOP實現是在編譯階段就對程式源代碼進行修改,生成了靜態的AOP代理類,即生成的
- 動態AOP實現
- AOP框架在運行階段動態生成代理對象。即在記憶體中以JDK動態代理的方式,或CGlib動態生成AOP代理類,典型的技術如:SpringAOP。
類別 | 機制 | 原理 |
---|---|---|
靜態AOP | 靜態織入 | 在編譯前,切麵直接以位元組碼的形式編譯入目標代碼的位元組碼文件中,對性能無影響,但靈活性不夠。 |
動態AOP | JDK動態代理 | 在運行期間,目標類載入之後為介面動態生成代理類,將切麵織入到代理類中,相對於靜態AOP更靈活一些。但切入點需要實現介面,對系統的性能有一定影響。 |
動態位元組碼生成 | CGlib | 在運行期間,目標類載入之後,動態生成目標類的子類,然後將切麵的邏輯加入到子類中,相對於JDK動態代理的方式,CGlib不需要介面也可以實現織入,但是當擴展類的實例方法使用final修飾時無法進行織入。 |
二、AOP的專有名詞
AOP中的特性術語大致有一下幾種:
- 增強|建議(Advice):AOP框架中的增強處理,通知表述了切麵在何時執行以及如何執行增強處理。
- 連接點(join point) :連切點表示應用程式執行過程中能夠插入切麵的一個點,這個點可以是方法的調用、異常的拋出。在SpringAOP中,連接點總是方法的調用。
- 切點(PointCut):可以插入增強處理的連接點。
- 切麵(Aspect):切麵是增強和切點的結合。
- 引入(Introduction):引入允許我們想現有的類添加新的方法或屬性。
- 織入(Weaving):將增強處理添加到目標對象中,並創建一個被增強對象,這個過程就是織入。
- 目標對象(Target):增強的目標對象,原始對象。
- 代理對象(Proxy):加入了增強的對象,是由AOP增強後生成的代理對象。
SpringAOP在AOP的基礎上又多出幾個延伸概念:
- 前置增強(MethodBeforeAdvice):在連接點執行前進行增強。
- 環繞增強(MethodInterceptor):在連接點前後都執行增強。
- 異常增強(ThrowsAdvice):連接點拋出異常時進行增強。
- 返回值增強(AfterReturingAdvice):連接點產生返回值後進行增強。
三、認識SpringAOP
3.1 SpringAOP的特點
AOP的框架有很多,實現方式各不相同,Spring的AOP則是通過動態代理進行實現的。下麵簡單對代理模式進行介紹。
3.2 SpringAOP使用與實現
- Spring原生API:實現 MethodBeforeAdvice介面、AfterReturningAdvice介面,重寫對應的方法,並配置增強類與切入點類,可以實現AOP操作。
- 自定義切麵類:自定義切麵類與增強方法,使用配置聲明切麵類並關聯切點,實現AOP
- 註解實現AOP:使用@Aspect聲明切麵類,@Pointcut聲明切入點,使用@Before@After....註解實現增強方法,並配置自動代理,來實現AOP。
Execution表達式:
聲明具體方法的位置的表達式 :返回值 包路徑.類名.方法名(參數列表)
代理模式
代理模式分為兩種:
- 靜態代理:需要為每個角色創建對應的代理對象。
- 動態代理:自動為每個目標角色生成對應的代理對象。
靜態代理
被代理類介面:
package com.dzqc.smbms.proxy;
/**
* 游戲介面
*/
public interface Game {
public void gameStart();
public void gameOver();
}
被代理類:
package com.dzqc.smbms.proxy;
public class Mario implements Game{
@Override
public void gameStart() {
System.out.println("超級馬利奧,游戲開始!");
}
@Override
public void gameOver() {
System.out.println("夠不著旗桿,游戲結束");
}
}
代理類:
package com.dzqc.smbms.proxy;
/**
* 本類為 靜態代理 類
* 代理類 需要 持有 目標對象
*/
public class GameStaticProxy implements Game{
private Game targetGame;
public Game getProxy(Game targetGame){
this.targetGame = targetGame;
return this;
}
@Override
public void gameStart() {
System.out.println("游戲機啟動");
System.out.println("游戲啟動!");
targetGame.gameStart();
System.out.println("游戲進行中,mario他跳起來了");
}
@Override
public void gameOver() {
System.out.println("mario 打敗了魔王,找到了旗桿,要爬旗桿。 ");
targetGame.gameOver();
System.out.println("真菜,不玩了,破游戲什麼時候關服!");
}
}
測試類:
package com.dzqc.smbms.proxy;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class ProxyTest {
@Test
public void staticProxy(){
Mario mario = new Mario();
GameStaticProxy gameStaticProxy = new GameStaticProxy();
Game marioProxy = gameStaticProxy.getProxy(mario);
marioProxy.gameStart();
marioProxy.gameOver();
}
}
測試結果:
游戲機啟動
游戲啟動!
超級馬利奧,游戲開始! //原始內容
游戲進行中,mario他跳起來了
mario 打敗了魔王,找到了旗桿,要爬旗桿。
夠不著旗桿,游戲結束 //原始內容
真菜,不玩了,破游戲什麼時候關服!
JDK動態代理
JDK動態代理的實現邏輯大致如下:
介面與被代理類與上述一致。
代理類:
package com.dzqc.smbms.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* JDK代理類
*/
public class GameJDKProxy implements InvocationHandler {
private Game targetGame;
public GameJDKProxy(Game targetGame){
this.targetGame = targetGame;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (name.equals("gameStart")){
Object o = gameStart(method, args);
}else {
Object o = gameOver(method, args);
}
return null;
}
private Object gameStart(Method method , Object[] args) throws Throwable{
System.out.println("mario游戲啟動了");
Object invoke = method.invoke(targetGame, args);
System.out.println("正在操作mario通水管");
return null;
}
private Object gameOver(Method method , Object[] args) throws Throwable{
System.out.println("水管打不通");
Object invoke = method.invoke(targetGame, args);
System.out.println("什麼破游戲!");
return null;
}
}
測試類:
package com.dzqc.smbms.proxy;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class ProxyTest {
@Test
public void jdkProxy(){
Mario mario = new Mario();
GameJDKProxy gameJDKProxy = new GameJDKProxy(mario);
// 使用JDK動態代理類獲取代理對象
Game marioProxy = (Game) Proxy.newProxyInstance(
this.getClass().getClassLoader(), //類載入器
new Class[]{Game.class}, // 代理介面的位元組碼文件
gameJDKProxy// 自己的代理類,進行目標方法擴充
);
marioProxy.gameStart();
marioProxy.gameOver();
}
}
測試結果
mario游戲啟動了
超級馬利奧,游戲開始! //原始內容
正在操作mario通水管
水管打不通
夠不著旗桿,游戲結束 //原始內容
什麼破游戲!
由上述代碼不難看出,動態代理的實現需要以下幾點:
- JDK動態代理需要聲明介面。想要創建一個動態代理類,就必須給這個類聲明一個介面,否則無法在Proxy.newProxyInstance時傳入對應的介面類位元組碼文件。
- 在代理類中需要通過構造傳入原有的
bean
,因為處理完附加功能外,需要執行原有bean
中的方法,以實現代理的目的。
CGlib動態代理
代理類:
package com.dzqc.smbms.proxy;
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 GameCglibProxy implements MethodInterceptor {
private Game targetGame;
public GameCglibProxy(Game targetGame){
this.targetGame = targetGame;
}
/**
* 獲取代理對象
* @return
*/
public Game getProxy(){
// 獲取cglib操作類
Enhancer enhancer = new Enhancer();
// 指定代理類 的 父類類型,要代理哪個類就使用哪個類做代理類的父類
enhancer.setSuperclass(Mario.class);
// 指定代理對象,把當前代理類作為代理對象
enhancer.setCallback(this);
// 創建並返回代理對象
return (Game) enhancer.create();
}
/**
* 代理增強的實際操作處
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
String name = method.getName();
if (name.equals("gameStart")){
gameStart(method , objects);
}else {
gameOver(method , objects);
}
return null;
}
private Object gameStart(Method method , Object[] args) throws Throwable{
System.out.println("魂鬥羅啟動!!!!");
Object invoke = method.invoke(targetGame, args);
System.out.println("正在操作Mario和魂鬥羅兄弟決鬥");
return null;
}
private Object gameOver(Method method , Object[] args) throws Throwable{
System.out.println("Mario挖了下水道,坑了魂鬥羅兄弟");
Object invoke = method.invoke(targetGame, args);
System.out.println("不要串台!");
return null;
}
}
測試類:
package com.dzqc.smbms.proxy;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class ProxyTest {
@Test
public void cglibProxy(){
Mario mario = new Mario();
GameCglibProxy gameCglibProxy = new GameCglibProxy(mario);
Game proxy = gameCglibProxy.getProxy();
proxy.gameStart();
proxy.gameOver();
}
}
測試結果:
魂鬥羅啟動!!!!
超級馬利奧,游戲開始! //原始內容
正在操作Mario和魂鬥羅兄弟決鬥
Mario挖了下水道,坑了魂鬥羅兄弟
夠不著旗桿,游戲結束 //原始內容
不要串台!
由上述代碼不難看出,CGlib在進行動態代理的過程中,對被實現的類要求較少,而且更為靈活,使用者可以根據實際情況進行選擇。
SPring MVC
MVC是什麼?MVC是設計模式,包含Model(模型)、View(視圖)、Controller(控制器)。
SpringMVC就是基於MVC設計模式創建的一個控制層框架,用來簡化視圖與後臺的數據交互工作,並且規範開發方式。SpringMVC是對Servlet的封裝,可將SpringMVC的本質看作Servlet。
SpringMVC核心組件
- 前端控制器|中央處理器(DispatcherServlet):用來調度整個MVC的請求走向。
- 處理器適配器(HandlerAdapter):用來適配請求路徑對應的處理器
- 處理器映射器(HandlerMapping):用來查找請求路徑對應的處理器方法
- 處理器(Controller | Handler):處理請求的方法
- 視圖解析器(ViewResolver):解析視圖、返迴路徑、請求轉發與響應的解析器
- 視圖(View):頁面
- 攔截器(Interceptor):攔截請求
SpringMVC第一個程式
處理請求以及響應
參數傳遞
類需要使用@Controller註解,在類中聲明一個Handler方法,在方法的參數列表中聲明需要的參數,在Handler方法被調用時,就可以正確傳入對應的參數。
- 常用簡單數據類型:String,Integer,Char,Boolean,Byte,Short,Long,Float,Double。
- 複雜數據類型:
- 對象:對象在進行參數傳遞時,參數列表中聲明的為對象,在傳參的過程中,傳遞的是對象中聲明的屬性名。
- 數組:數組在進行傳參時,數組的參數名需要多次出現,每次出現給數組賦一個 值。
- List集合:在進行參數傳遞時,因為List集合是個介面,所以需要在List類型的參數前加上@RequestParam註解,在參數傳遞時,與數組傳參方式一致。
- map集合:在進行參數傳遞時,需要加上@RequestParam註解,參數在拼接時,只需將key和value的值按照普通傳參的方式拼接在地址欄或參數列表中。
響應 頁面跳轉
- 只響應數據:數據介面,在Handler方法上需要添加@ResponseBody註解,添加完成後,返回值不會被視圖解析器進行解析,在返回時可以按照正常的數據進行返回。
- 預設頁面返回:預設的響應方式,本質上是請求轉發,在Handler方法進行return後,會將返回的數據轉發至DispatcherServlet,然後由DispatcherServlet調度視圖解析器,解析返回值中的數據與視圖並響應。
- 重定向:重定向可以重定向到任意路徑,只需要在返回值上、ModelAndView的視圖名前 加上 redirect,這裡的重定向與Servlet的重定向功能保持一致,流程有變動。在Handler(處理器)方法進行返回後,將請求轉發至DispatcherServlet(中央處理器),由DispatcherServlet(中央處理器)調度HttpServletResponse(響應),由HttpServletResponse(響應)進行重定向操作。
SpringMVC基本配置
在web.xml種配置 dispatcherServlet
<!-- 配置中央處理器,與Servlet一起初始化 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置SpringIOC容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
在Spring文件當中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!-- 配置包掃描,掃描所有使用Controller註解的類 -->
<context:component-scan base-package="com.dzqc.smbms">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 處理器適配器 -->
<bean name="handlerAdapter"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
<!-- 處理器映射器 -->
<bean name="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!-- 視圖解析器 -->
<bean name="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置靜態資源過濾器 -->
<mvc:default-servlet-handler/>
<mvc:resources location="/statics/" mapping="/static/*"></mvc:resources>
<!-- 文件上傳解析器 -->
<bean name="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大支持文件大小,此處配置50MB大小限制 -->
<property name="maxUploadSize" value="52428800"></property>
</bean>
</beans>
SpringMVC文件上傳
依賴:
<!-- 文件上傳 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
Restful風格介面
Rest註解:
- @RestController
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PathVariable
SpringMVC工作流程
準備工作:
/* 在SpringMVC首次刷新或重新載入時,會調用SpringMVC初始化策略 */ protected void onRefresh(ApplicationContext context) { this.initStrategies(context); } /** 初始化策略方法 */ protected void initStrategies(ApplicationContext context) { this.initMultipartResolver(context); // 初始化文件上傳解析器 this.initLocaleResolver(context); // 初始化本地解析器 this.initThemeResolver(context); // 初始化中心解析器 this.initHandlerMappings(context); // 初始化處理器映射器 this.initHandlerAdapters(context); // 初始化處理器適配器 this.initHandlerExceptionResolvers(context); // 初始化處理器異常解析器 this.initRequestToViewNameTranslator(context); // 初始化 請求視圖 翻譯器 this.initViewResolvers(context); // 初始化視圖解析器 this.initFlashMapManager(context); // 初始化 映射刷新管理器 }
2:請求處理流程
- DispathcerServlet(前端控制器)接收請求 :DispatcherServlet 通過 doService()方法 接收用戶請求。調用 doDispatch() 方法進行請求調度。
- DispatcherServlet(前端控制器)調用HandlerMapping(處理器映射器):DispatcherServlet 通過getHandler() 調用HandlerMapping獲取HandlerExecutionChain
- HandlerMapping將(HandlerExecutionChain)處理器執行鏈返回給DispatcherServlet
- DispathcerServlet(前端控制器)通過HandlerAdapter(處理器適配器),獲取對應的Handler處理器方法。
- HandlerAdapter(處理器適配器)調用Handler處理器方法,處理用戶請求。
- Handler處理器方法將返回值返回給HandlerAdapter(處理器適配器)
- HandlerAdapter將Handler處理器方法的返回值以ModelAndView的形式返回給DispatherServlet:
- DispatcherServlet將ModelAndView交給ViewResolver(視圖解析器)解析。
- ViewResolver(視圖解析器)將ModelAndView中的數據渲染到View(視圖)中。
- ViewResolver返回的視圖信息到DispatcherServlet。
- DispatcherServlet將視圖信息及返回值返回給客戶端。
MyBAtis
MyBatis是一款輕量級的ORM(對象關係映射)(持久化)框架,能夠將JDBC相關的持久化代碼通過配置文件的形式實現,提高開發效率。
實現原理:工廠模式
手機工廠:MI,IPHONE,HUAWEI
簡單工廠
工廠對象:
抽象實體:
具體實體:
工廠方法
抽象工廠:
抽象產品:
實體工廠:
實體產品:
抽象工廠
MyBatis基本應用
MyBatis使用分為
- 配置文件(mybatis-config.xml)
- mapper介面
- 與mapper介面對應的mapper.xml映射文件
MyBatis的基本配置(MyBatis-config.xml)
MyBatis數據源配置
<!-- 環境配置標簽 default中引用的為預設使用的環境 -->
<environments default="development">
<!-- 環境標簽 id 唯一,不可重覆 -->
<environment id="development">
<!-- 事務管理器配置 -->
<transactionManager type="JDBC"/>
<!-- 數據源配置 type預設為POOLED(預設啟用資料庫連接池模式) ,UNPOOLED 不啟用連接池 -->
<dataSource type="POOLED">
<!-- 屬性標簽,該處屬性為數據源配置屬性 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/smbms_3"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
資料庫連接池 (druid c3p0)
映射文件配置
<mappers>
<!-- 映射文件的地址,引用相對路徑 -->
<mapper resource="mappers/SUserMapper.xml"></mapper>
</mappers>
其他配置
<!-- MyBatis別名配置 只能配置實體類 -->
<typeAliases>
<!-- <typeAlias type="com.dzqc.smbms.entity.SUser" alias="u"></typeAlias>-->
<package name="com.dzqc.smbms.entity"/>
</typeAliases>
<settings>
<!-- 配置控制台日誌組件 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
MyBatis整合Log4J
MyBatis主要構件
獲取資源:Resources.getResourceAsStream("MyBatis-config.xml");
SqlSessionFactoryBuilder:構建工廠的建築隊,需要給資源後,調用build方法才能構建工廠。
SqlSessionFactory:工廠建好後,調用openSession方法,產出SqlSession
SqlSession:產出的SqlSession可以通過Mapper介面的位元組碼(.class)來獲取SqlSession中生產的代理對象,從而操作資料庫。
MyBatis映射文件
基本標簽
<select id="唯一,不可重覆" parameterType="參數類型" resultType="返回值類型" resultMap=""
>查詢SQL</select>
<insert id="唯一,不可重覆" parameterType="" keyColumn="" keyProperty="" useGeneritedKeys="true">插入語句</insert>
<update id="唯一,不可重覆" parameterType="" >修改語句</update>
<delete id="唯一,不可重覆" parameterType="" >刪除語句</delete>
動態SQL
判斷條件成立時,會向SQL後拼接的條件語句 自動判斷,如果where中包含的 有任意一個或多個成立,就會自動向SQL後拼接 where , 並將 中的拼接語句之前的關鍵字進行適當的截取。 :拼接SQL的動態欄位,常用屬性有 perfix , suffix , perfixOverrides,suffixOverrides。 :自動拼接Set關鍵字,並截取 中的尾碼內容。 :column:要遍歷的迴圈體內容,item:每次迴圈到的元素,open:開始時自動拼接的內容,close:結束時自動拼接的內容,seprector:分隔符,自動在每個元素後面拼接的內容。
ResultMap
resultMap是MyBatis實現ORM特性的一個最重要的標簽。
基礎查詢 - 基本映射
外聯查詢 - 一對一查詢
<association property="role" javaType="srole" column="u.userRole">
<id column="userRole" property="id"></id>
<result column="roleCode" property="roleCode"></result>
<result column="roleName" property="roleName"></result>
</association>
<association property="role" javaType="srole" column="userRole"
select="com.dzqc.smbms.mapper.SRoleMapper.selectById" >
</association>
property:實體類的屬性名
column:外關聯欄位的欄位名
javaType:property對應的實體類的類型。
select:外關聯查詢的方法
外聯查詢 - 一對多查詢
<collection property="roleUsers" column="id" javaType="list" ofType="suser"
select="com.dzqc.smbms.mapper.ISUserMapper.selectByRoleId">
</collection>
property:實體類的屬性名
column:外關聯欄位的欄位名
javaType:一對多關聯集合類型。
ofType:一對多關聯集合中存放的數據的類型。
select:外關聯查詢的方法
MyBatis 一級緩存與二級緩存
事務管理
Spring事務管理
什麼是事務:事務的本身其實就是一組業務邏輯,在業務邏輯當中包含有針對於資料庫持久化的操作。在事務當中的所有業務邏輯被視為一個整體。
事務的特性(ACID)
- 原子性(Atomicity):事務中所有的操作,被視為一個整體,一組業務邏輯中一旦有一點出現異常或失敗,那麼所有的業務邏輯都被視為失敗。原子性保證了事務當中的業務邏輯要麼全部成功,要麼全部失敗。
- 一致性(Consistency):事務的操作,不能破壞資料庫中的數據完整性,在事務執行之前或執行之後,資料庫都處於一致的狀態。在事務中一旦對數據進行持久化,那麼持久化的數據一定符合該資料庫的所有規範,不會破壞資料庫中數據的完整性。
- 持久性(Durability):事務中的數據一旦被提交,在之後的操作中都可以查詢到,無論出現什麼故障都不會導致數據的損壞或丟失(物理損壞除外,存儲介質損壞)。
- 隔離性(Isolation):資料庫允許多個事務同時執行,事務之間的數據相互不會有影響。
事務的隔離級別
事務不隔離會產生的問題:
臟讀:當前事務讀到了其他事務未提交的數據
幻讀(虛讀):當前事務重覆讀取同樣的數據,讀取到了其他事務新增的數據,導致短時間內兩次讀取的數據不一致。
不可重覆讀:當前事務讀到了其他事務已提交的數據,在短時間,兩次查詢的結果不一致。
- 讀未提交(read-uncommitted):預設隔離級別,允許當前事務讀取其他事務未提交的數據,在該隔離級別中,臟讀、幻讀、不可重覆讀都有可能出現。
- 讀已提交(read-committed):允許當前事務讀取其他事務已提交的數據,在該隔離級別下,臟讀不會出現,幻讀和不可重覆讀有可能出現。
- 可重覆讀(Repeatable-read):允許當前數據讀取其他事務新增的數據,不讀取其他事務提交的修改數據(不允許修改操作)。在該事務隔離級別下,臟讀與不可重覆讀不會出現,幻讀有可能會出現。
- 串列化(Serializable):最高的事務隔離級別,臟讀、幻讀、不可重覆讀都不會出現,但是運行效率低,一般不使用。
Spring事務
編程式事務
優點:對事務的操作精細,能夠更加精確的對事務進行管理
缺點:對代碼的侵入度高,在後期進行維護、功能變更時會提高維護難度。
聲明式事務
事務傳播機制
事務傳播機制一共有7種。
- requierd:如果當前沒有事務,就新建一個事務,如果已存在一個事務中,加入到這個事務中,(事務預設傳播機制)。
- supports:支持當前事務,如果沒有當前事務,就以非事務方法執行。
- mandatory:使用當前事務,如果沒有當前事務,就拋出異常。
- required_new:新建事務,如果當前存在事務,把當前事務掛起。
- not_supported:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
- never:以非事務方式執行操作,如果當前事務存在則拋出異常。
- nested:如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與propagation_required類似的操作