1. Mybatis中如何實現批量數據的插入,請寫出配置文件的配置信息以及java代碼的源
MyBatis提供用於插入數據的註解有兩個:@insert,@InsertProvider,類似還有:@DeleteProvider@UpdateProvider,和@SelectProvider,
作用:
用來在實體類的Mapper類里註解保存方法的SQL語句
區別:
@Insert是直接配置SQL語句,而@InsertProvider則是通過SQL工廠類及對應的方法生產SQL語句,這種方法的好處在於,我們可以根據不同的需求生產出不同的SQL,適用性更好。
使用:
@Insert
@Insert(「insert into blog(blogId,title,author) values(#blogId,#title,#author)」)
public boolean saveBlog(Blog blog);
@InsertProvider
在mapper介面中的方法上使用@InsertProvider註解:
參數解釋:
type為工廠類的類對象,
method為對應的工廠類中的方法,方法中的@Param(「list」)是因為批量插入傳入的是一個list,但是Mybatis會將其包裝成一個map。其中map的key為「list」,value為傳入的list。
2. mybatis各階段的詳解
比如我們在引入了jdbc的配置文件使用了properties標簽,引入jdbc有什麼好處?,可以在配置文件中統一管理
內容而不是在很多個文件改來改去,而且在核心配置文件中把資料庫連接相關的寫死,顯然是硬編碼的所以我們用配置文件代替 nice!!!
你可能注意到了上面的寫法 用前綴jdbc. 可以很好地將他們與其他的變數區分開,(可以從名字很容易看出是jdbc相關的數據,不至於和同名變數搞混因為username這種可能
不止會出現在資料庫的連接)
上面的代碼中引入配置文件的部分為
可以從上面看到寫法:
下面這段就是用來設置類的別名:
那麼問題來了,為什麼要有類的別名這種操作??
因為在映射文件中每次都要寫全類名顯然有點麻煩比如下面這樣:
一個項目是會有很多個映射文件的為了方便,所以類別名就出現了。可以在核心配置文件寫介面類和對應的別名
這樣就可以在映射文件的命名空間里可以直接寫User(對大小沒有要求也可以是user; 其實可以比這更加簡單,也是我們在實際開發中常用的寫法
就是將整個包寫成別名的形式,如果不寫alias屬性默認為類名(不區分大小寫),這樣就容易多了,我們只需一行代碼,便可以在所有的映射文件命名空間
中直接寫對應的類名
引入核心的配置文件
首先需要思考的這里是映射文件的引入,我們正常的一個項目的資料庫是有很多個表組成的那麼每一張表對應一個mapper介面,每個介面對應一個映射文件,那麼就需要導入大量的映射文件,還容易漏掉-->
上面這種以包的形式的導入非常方便,不用每次新建一個介面就要導入它的映射文件,但是上面這種寫法需要 注意 一些問題:
如果你在映射文件中編寫查詢語句的sql,但是粗心的你忘記了設置返回類型會在控制台拋異常且會看到這樣的說明:
It's likely that neither a Result Type nor a Result Map was specified.
下面只是指定返回類型的一種方式:resultType,還有 resultMap
它們的區別:
查詢的標簽必須指定resultType或resultMap
com.kobe.mybatis 獲取參數的兩種方式:${} 和 #{}
上面是使用了 #{}寫法相當於原生jdbc的佔位符,這個前面已經提到過了所以不多贅述, 需要注意的是#{}裡面的變數名可以是任意的username規范顯然很好,但是aaaa也沒錯因為只是用來佔位的;
還有就是在使用${}時注意''單引號問題,因為${}是字元拼接的方式,所以需要注意!!
傳輸參數時有多個參數時
在測試代碼里通過傳入兩個參數分別為 username和password 但是在上面代碼的(映射文件里的部分代碼)執行失敗,(sql語句未能解析)
報錯:
Cause: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
可以從錯誤提示的信息不難發現我們的參數在映射文件里未能真正地接受到,可以用[arg1, arg0, param1, param2] 的方式獲取,mybatis將參數放到map容器可以通過建arg0,agr1..的方式
獲取參數(也可以是param1,param2..)
將上面的代碼改動:
需要注意的是:使用${}時需要手動添加''才能正常訪問,因為他的處理方式是字元串的拼接
做了改動之後結果很感人!!
User{id=6, userName='旺財', age=20, password='cwlz'}
可以直接通過鍵訪問相對應的值(通過自己的方式訪問到數據,上面的形式是mybatis默認提供的map和mybatis默認的提取指的方式 arg0,arg2...)
當需要傳多個參數時將他們放到一個map容器,然後將map傳給對應的方法(模擬mybatis的做法,就可以在sql語句中直接通過鍵訪問到值)代碼如下:
映射文件中的部分代碼 :
通過鍵直接獲取值,注意:使用${}時不要忘了單引號!!!!
當參數以實體對象的形式傳參時如何解決?
只需要通過#{}以屬性名的方式訪問!
所以代碼的編寫一定要規范,才能減少這種錯誤!!!
一定要和註解中的參數名一一對應!!!
如果查詢的結果只有一個,也可以通過Map集合接收,欄位名為鍵欄位的值為值:{password=0000, id=3, userName=圖區, age=20}
java.lang.Ingeger --> int ,Integer
int --> _int,_Integer
Map --> map
String --> string
注意:
所以在批量刪除的案例:需要注意的是不能使用#{} 因為它是會自動添加'' 所以在批量刪除的語句中我們要使用${}
若欄位和屬性名不一致 ,則可以通過resultMap設置自定義映射
在mybatis的核心配置文件用下面的代碼將 資料庫中命名的規范 (user_name) 轉換為 java中的命名規范 (userName)
就是手動設置屬性與欄位的映射關系:
如果設置了手動的設置屬性和欄位的映射關系,注意主鍵使用 id 標簽,普通欄位使用 result標簽,就算屬性和欄位名一一對應,只要
用了這種方式就 必須要寫全 !!!
一對多的查詢:
通過分步查詢實現:
多條件的查詢
if 根據標簽中test的屬性所對應的表達式決定標簽中的內容是否拼接到sql語句中
上面的where後面的 1=1 是細節,因為當where後面的條件都為空時就成了 select * from t_user where
顯然這種sql語句是有問題的,還有一種情況就是當userName為null時語句就成了 select * from t_user where and age=#{age}
這也是錯的,所以在where後加一個恆成立的條件不僅不會影響查詢結果,而且沒有會在特定情況時sql語句是會報錯的所以很有必要
where 當where標簽中有內容時,會自動生成where關鍵字,並且將內容前多餘的and 或者or去掉
當where中沒有內容時,此時where標簽沒有任何效果 就是不會生成關鍵字 注意:在寫條件時不能在後面加and or 這個在下一條語句無效時mybatis不會幫你去掉!
相當於 if else
一個案例 -->就是當我們需要批量刪除一些東西時(參數以數組的形式傳入)
sql 片段: 在我們的查詢語句不能在實際開發中也一直寫 *;因為我們要按需查找,不必將不需要的也查詢出來,我們可以將我們平常查詢次數較多的欄位
放在sql片段內,可以在需要查詢時直接進行引用!
緩存,這個術語我們聽過很多次,在web階段時訪問網頁時有緩存機制!
現在sql的查詢時也有緩存機制,有一級緩存,一級緩存是默認開啟的,一級緩存的范圍時sqlSession,將我們查詢到的數據先進行緩存,若下次有相同的查詢時不用重新
訪問資料庫,可以直接從緩存中取出!!!!
手動清空緩存 sqlSession.clearCache();
在mapper配置文件中添加cache標簽可以設置一些屬性:
逆向工程就是不難理解,我們之前都是由實體類到資料庫,而逆向類就是通過資料庫表生成實體類,
3. MyBatis解析
從命名上可以看出,這個是一個 Builder 模式的,用於創建 SqlSessionFactory 的類。SqlSessionFactoryBuilder 根據配置來構造 SqlSessionFactory。其中配置方式有兩種:
mybatis-config.xml 就是我們的配置文件:
Java Config 相比較 XML 文件的方式而言,會有一些限制。比如修改了配置文件需要重新編譯,註解方式沒有 XML 配置項多等。所以,業界大多數情況下是選擇 XML 文件的方式。但到底選擇哪種方式,這個要取決與自己團隊的需要。比如,項目的 SQL 語句不復雜,也不需要一些高級的 SQL 特性,那麼 Java Config 則會更加簡潔一點;反之,則可以選擇 XML 文件的方式。
創建配置文件解析器XMLConfigBuilder
解析mybatis-config.xml里的配置為Configuration對象,Mybatis的全局配置對象。
XMLConfigBuilder#parseConfiguration解析mapper下的xml
XMLMapperBuilder#bindMapperForNamespace,根據xml里的 namespace 反射出 mapper介面 的 class,如果有mapper介面,則把該mapper介面的class添加到Configuration的mapperRegistry里。
如果該介面已經注冊,則拋出已經綁定的異常。
為該介面注冊MapperProxyFactory,但這里只是注冊其創建MapperProxy的工廠,並不是創建MapperProxy。
如果Mapper對應的xml資源未載入,觸發xml的綁定操作,將xml中的sql語句與Mapper建立關系。
addMapper方法,只是為**Mapper創建對應對應的MapperProxyFactory。
根據Mapper介面與SqlSession創建MapperProxy對象。
根據介面類獲取MapperProxyFactory。
調用MapperProxyFactory的newInstance創建MapperProxy對象。
SqlSessionFactory 顧名思義,是用於生產 SqlSession 的工廠。 通過如下的方式來獲取 SqlSession 實例:
SqlSession 包含了執行 SQL 的所有的方法。以下是示例:
當然,下面的方式可以做到類型安全:
MapperProxy是MapperProxyFactory使用SqlSession創建出來的。所以MapperProxy中包含SqlSession。
可以看到MapperProxy調用invoke方法,進而調用MapperMethod的execute(),這些MapperMethod就是和你要執行的命令相關,比如執行select語句,則會通過SqlSession的select()方法,最終調用到Executor的query方法。Executor會再協調另外三個核心組件。
MapperProxy:
MapperMethod:
插件的構建:
談原理首先要知道StatementHandler,ParameterHandler,Result Handler都是代理,他們是Configuration創建,在創建過程中會調用interceptorChain.pluginAll()方法,為四大組件組裝插件(再底層是通過Plugin.wrap(target,XX, new Plugin( interceptor))來來創建的)。
插件鏈是何時構建的:
在執行SqlSession的query或者update方法時,SqlSession會通過Configuration創建Executor代理,在創建過程中就調用interceptor的pluginAll方法組裝插件。然後executor在調用doQuery()方法的時候,也會調用Configuration的newStatementHandler方法創建StatemenHandler(和上面描述的一樣,這個handler就是個代理,也是通過interceptorChain的pluginAll方法構建插件)
插件如何執行:
以statementhandler的prepare方法的插件為例,正如前面所說,statementhandler是一個proxy,執行他的prepare方法,將調用invokeHandler的invoke方法,而invokeHandler就是Plugin.wrap(target, xxx, new Plugin(interceptor))中的第三個參數,所以很自然invokeHanlder的invoke的方法最終就會調用interceptor對象的intercept方法。
Mybatis的插件配置在configuration內部,初始化時,會讀取這些插件,保存於Configuration對象的InterceptorChain中。
org.apache.ibatis.plugin.InterceptorChain.java源碼。
上面的for循環代表了只要是插件,都會以責任鏈的方式逐一執行,所謂插件,其實就類似於攔截器。
插件的編寫
插件必須實現org.apache.ibatis.plugin.Interceptor介面。
-intercept()方法:執行攔截內容的地方,攔截目標對象的目標方法的執行
-plugin()方法:決定是否觸發intercept()方法。 作用:包裝目標對象,包裝就是為目標對象創建一個代理對象
-setProperties()方法:給自定義的攔截器傳遞xml配置的屬性參數。將插件注冊時的property屬性設置進來
下面自定義一個攔截器:
為什麼要寫Annotation註解?註解都是什麼含義?
Mybatis規定插件必須編寫Annotation註解,是必須,而不是可選。@Intercepts註解:裝載一個@Signature列表,一個@Signature其實就是一個需要攔截的方法封裝。那麼,一個攔截器要攔截多個方法,自然就是一個@Signature列表。
type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }
解釋:要攔截Executor介面內的query()方法,參數類型為args列表。
Plugin.wrap(target, this)是干什麼的?
使用JDK的動態代理,給target對象創建一個delegate代理對象,以此來實現方法攔截和增強功能,它會回調intercept()方法。
Mybatis可以攔截哪些介面對象?
Mybatis只能攔截ParameterHandler、ResultSetHandler、StatementHandler、Executor共4個介面對象內的方法。
重新審視interceptorChain.pluginAll()方法:該方法在創建上述4個介面對象時調用,其含義為給這些介面對象注冊攔截器功能,注意是注冊,而不是執行攔截。
攔截器執行時機:plugin()方法注冊攔截器後,那麼,在執行上述4個介面對象內的具體方法時,就會自動觸發攔截器的執行,也就是插件的執行。
Invocation
可以通過invocation來獲取攔截的目標方法,以及執行目標方法。
分頁插件原理
由於Mybatis採用的是邏輯分頁,而非物理分頁,那麼,市場上就出現了可以實現物理分頁的Mybatis的分頁插件。 要實現物理分頁,就需要對String sql進行攔截並增強,Mybatis通過BoundSql對象存儲String sql,而BoundSql則由StatementHandler對象獲取。
因此,就需要編寫一個針對StatementHandler的query方法攔截器,然後獲取到sql,對sql進行重寫增強。
4. spring整合mybatis怎樣配置註解
mybatis和spring的整合步驟:
1)使用mybatis,必須有個全局配置文件configuration.xml,來配置mybatis的緩存,延遲載入等等一系列屬性,該配置文件示例如下:
Java代碼
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<configuration>
<settings>
<!-- 全局映射器啟用緩存 -->
<setting name="cacheEnabled" value="true" />
<!-- 查詢時,關閉關聯對象即時載入以提高性能 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 設置關聯對象載入的形態,此處為按需載入欄位(載入欄位由SQL指 定),不會載入關聯表的所有欄位,以提高性能 -->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 對於未知的SQL查詢,允許返回不同的結果集以達到通用的效果 -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 允許使用列標簽代替列名 -->
<setting name="useColumnLabel" value="true" />
<!-- 允許使用自定義的主鍵值(比如由程序生成的UUID 32位編碼作為鍵值),數據表的PK生成策略將被覆蓋 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 給予被嵌套的resultMap以欄位-屬性的映射支持 -->
<setting name="autoMappingBehavior" value="FULL" />
<!-- 對於批量更新操作緩存SQL以提高性能 -->
<setting name="defaultExecutorType" value="BATCH" />
<!-- 資料庫超過25000秒仍未響應則超時 -->
<setting name="defaultStatementTimeout" value="25000" />
</settings>
<!-- 全局別名設置,在映射文件中只需寫別名,而不必寫出整個類路徑 -->
<typeAliases>
<typeAlias alias="TestBean"
type="com.wotao.taotao.persist.test.dataobject.TestBean" />
</typeAliases>
<!-- 非註解的sql映射文件配置,如果使用mybatis註解,該mapper無需配置,但是如果mybatis註解中包含@resultMap註解,則mapper必須配置,給resultMap註解使用 -->
<mappers>
<mapper resource="persist/test/orm/test.xml" />
</mappers>
</configuration>
2)該文件放在資源文件的任意classpath目錄下,假設這里就直接放在資源根目錄,等會spring需要引用該文件。
查看ibatis-3-config.dtd發現除了settings和typeAliases還有其他眾多元素,比如properties,objectFactory,environments等等,這些元素基本上都包含著一些環境配置,數據源定義,資料庫事務等等,在單獨使用mybatis的時候非常重要,比如通過以構造參數的形式去實例化一個sqlsessionFactory,就像這樣:
Java代碼
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, properties);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, properties);
而typeHandlers則用來自定義映射規則,如可以自定義將Character映射為varchar,plugins元素則放了一些攔截器介面。
2)在spring配置文件中指定c3p0數據源定義如下:
Java代碼
<!-- c3p0 connection pool configuration -->
<bean id="testDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 資料庫驅動 -->
<property name="driverClass" value="${db.driver.class}" />
<!-- 連接URL串 -->
<property name="jdbcUrl" value="${db.url}" />
<!-- 連接用戶名 -->
<property name="user" value="${db.username}" />
<!-- 連接密碼 -->
<property name="password" value="${db.password}" />
<!-- 初始化連接池時連接數量為5個 -->
<property name="initialPoolSize" value="5" />
<!-- 允許最小連接數量為5個 -->
<property name="minPoolSize" value="5" />
<!-- 允許最大連接數量為20個 -->
<property name="maxPoolSize" value="20" />
<!-- 允許連接池最大生成100個PreparedStatement對象 -->
<property name="maxStatements" value="100" />
<!-- 連接有效時間,連接超過3600秒未使用,則該連接丟棄 -->
<property name="maxIdleTime" value="3600" />
<!-- 連接用完時,一次產生的新連接步進值為2 -->
<property name="acquireIncrement" value="2" />
<!-- 獲取連接失敗後再嘗試10次,再失敗則返回DAOException異常 -->
<property name="acquireRetryAttempts" value="10" />
<!-- 獲取下一次連接時最短間隔600毫秒,有助於提高性能 -->
<property name="acquireRetryDelay" value="600" />
<!-- 檢查連接的有效性,此處小弟不是很懂什麼意思 -->
<property name="testConnectionOnCheckin" value="true" />
<!-- 每個1200秒檢查連接對象狀態 -->
<property name="idleConnectionTestPeriod" value="1200" />
<!-- 獲取新連接的超時時間為10000毫秒 -->
<property name="checkoutTimeout" value="10000" />
</bean>
配置中的${}都是佔位符,在指定資料庫驅動打war時會自動替換,替換的值在父pom中配置。
3)需要一個sessionFactory來生成session,sessionFactory配置如下:
Java代碼
<bean id="testSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:configuration.xml" />
<property name="dataSource" ref="testDataSource" />
</bean>
4)配置一個映射器介面來對應sqlSessionTemplate,該映射器介面定義了介面方法:
Java代碼
<!-- data OR mapping interface -->
<bean id="testMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="testSqlSessionFactory" />
<property name="mapperInterface" value="com.wotao.taotao.persist.test.mapper.TestMapper" />
</bean>
5)至此,一個完整的myabtis整合spring的配置文件看起來應該如下所示:
Java代碼
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- c3p0 connection pool configuration -->
<bean id="testDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${db.driver.class}" />
<property name="jdbcUrl" value="${db.url}" />
<property name="user" value="${db.username}" />
<property name="password" value="${db.password}" />
<property name="initialPoolSize" value="5" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="maxStatements" value="100" />
<property name="maxIdleTime" value="3600" />
<property name="acquireIncrement" value="2" />
<property name="acquireRetryAttempts" value="10" />
<property name="acquireRetryDelay" value="600" />
<property name="testConnectionOnCheckin" value="true" />
<property name="idleConnectionTestPeriod" value="1200" />
<property name="checkoutTimeout" value="10000" />
</bean>
<bean id="testSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:configuration.xml" />
<property name="dataSource" ref="testDataSource" />
</bean>
<!-- data OR mapping interface -->
<bean id="testMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="testSqlSessionFactory" />
<property name="mapperInterface" value="com.wotao.taotao.persist.test.mapper.TestMapper" />
</bean>
<!-- add your own Mapper here -->
<!-- comment here, using annotation -->
<!-- <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> -->
<!-- <constructor-arg index="0" ref="sqlSessionFactory" /> -->
<!-- </bean> -->
<!-- base DAO class, for mole business, extend this class in DAO -->
<!-- <bean id="testBaseDAO" class="com.test..TestBaseDAO"> -->
<!-- <property name="sqlSessionTemplate" ref="sqlSessionTemplate" /> -->
<!-- </bean> -->
<!-- <bean id="testDAO" class="com.test..impl.TestDAOImpl" /> -->
<!-- you can DI Bean if you don't like use annotation -->
</beans>