mybatis
1. what is mybatis
MyBatis 是一款優(yōu)秀的持久層框架,它支持定制化 SQL并思、存儲(chǔ)過程以及高級(jí)映射入偷。MyBatis 避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集。MyBatis 可以使用簡(jiǎn)單的 XML 或注解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫(kù)中的記錄坐求。
2. how to use mybatis
1. 編程式
要使用 MyBatis讼载, 只需將 mybatis-x.x.x.jar 文件置于 classpath 中即可。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
2. 集成式
1. MyBatis-Spring
要使用 MyBatis-Spring 模塊,你只需要包含 mybatis-spring-x.x.x.jar 文 件就可以了,并在類路徑中加入相關(guān)的依賴 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>x.x.x</version> </dependency> SqlSessionFactoryBean MapperFactoryBean <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" /> </bean> 一個(gè)使用 MyBatis-Spring 的主要原因是它允許 MyBatis 參與到 Spring 的事務(wù)管理中儒溉。而 不是給 MyBatis 創(chuàng)建一個(gè)新的特定的事務(wù)管理器,MyBatis-Spring 利用了存在于 Spring 中的 DataSourceTransactionManager宦焦。 一旦 Spring 的 PlatformTransactionManager 配置好了,你可以在 Spring 中以你通常的做 法來配置事務(wù)。@Transactional 注解和 AOP(Aspect-Oriented Program,面向切面編程,譯 者注)樣式的配置都是支持的顿涣。在事務(wù)處理期間,一個(gè)單獨(dú)的 SqlSession 對(duì)象將會(huì)被創(chuàng)建 和使用波闹。當(dāng)事務(wù)完成時(shí),這個(gè) session 會(huì)以合適的方式提交或回滾。 一旦事務(wù)創(chuàng)建之后,MyBatis-Spring 將會(huì)透明的管理事務(wù)涛碑。 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> SqlSessionTemplate SqlSessionTemplate 是 MyBatis-Spring 的核心精堕。 這個(gè)類負(fù)責(zé)管理 MyBatis 的 SqlSession, 調(diào)用 MyBatis 的 SQL 方法, 翻譯異常。 SqlSessionTemplate 是線程安全的, 可以被多個(gè) DAO 所共享使用蒲障。 <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> MapperScannerConfigurer <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="org.mybatis.spring.sample.mapper" /> </bean>
2. mybatis/spring-boot-starter
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> @MapperScan("com.neo.mapper") 或者直接在Mapper類上面添加注解@Mapper @ImportResource({"classpath:config/spring/spring_*.xml"}) mybatis.mapperLocations=classpath*:**/mappers/*.xml
3. mybatis framework
1. Resources
String resource = "file:///C:\\Users\\banma\\Desktop\\academy\\demo\\src\\main\\resources\\static\\pom.xml"; String resource = "https://maven.apache.org/what-is-maven.html"; InputStream inputStream = Resources.getUrlAsStream(resource); String resource = "static/pom.xml"; InputStream inputStream = Resources.getResourceAsStream(resource);
2. Configuration
public class SqlSessionFactoryBuilder public SqlSessionFactory build(Configuration config) {new DefaultSqlSessionFactory(config);} public class DefaultSqlSessionFactory implements SqlSessionFactory public SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);} Configuration Environment environment {id transactionFactory dataSource} LocalCacheScope localCacheScope = LocalCacheScope.SESSION; //一級(jí)緩存 session 級(jí)別SESSION,STATEMENT ExecutorType defaultExecutorType = ExecutorType.SIMPLE;//SIMPLE, REUSE, BATCH reflectorFactory // model mapperRegistry {config Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();} //class factory interface mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection"); //xml node namespace collection resultMaps = new StrictMap<ResultMap>("Result Maps collection"); parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection"); keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection"); Set<String> loadedResources = new HashSet<String>();// xml namespace interface sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers"); interceptorChain = new InterceptorChain(); typeHandlerRegistry = new TypeHandlerRegistry(); typeAliasRegistry = new TypeAliasRegistry();
3. Connection
public interface javax.sql.DataSource Connection getConnection() public interface java.sql.Connection java.sql.Statement connection.prepareStatement(sql, keyColumnNames) public interface java.sql.PreparedStatement extends Statement
4. SqlSession
Resources InputStream SqlSessionFactoryBuilder SqlSessionFactory openSession() DefaultSqlSessionFactory / SqlSessionManager SqlSession -> DefaultSqlSession ResultHandler -> DefaultMapResultHandler ResultContext -> DefaultResultContext MapperRegistry MapperProxyFactory MappedStatement MapperProxyFactory TypeHandler -> IntegerTypeHandler / BaseTypeHandler<Integer> Plugin Executor ExecutorType Interceptor InterceptorChain SqlSession public interface org.apache.ibatis.session.SqlSession extends Closeable // insert delete update select // commit rollback close // getConfiguration() getMapper() getConnection() public class DefaultSqlSession implements SqlSession configuration executor public interface org.apache.ibatis.executor.Executor public abstract class BaseExecutor implements Executor protected Transaction transaction; protected Executor wrapper; protected Configuration configuration; public class CachingExecutor implements Executor Executor delegate tcm = new TransactionalCacheManager(); public interface Transaction protected java.sql.Connection connection; protected javax.sql.DataSource dataSource; protected TransactionIsolationLevel level;
5. Proxy
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); MapperProxyFactory final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); public class MapperProxyFactory<T> private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public class MapperProxy<T> implements InvocationHandler, Serializable private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public Object invoke(Object proxy, Method method, Object[] args) result = sqlSession.selectOne(command.getName(), param); SimpleExecutor DataSource PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps);
4. source code
1. public class MapperMethod
private final SqlCommand command;
private final MethodSignature method;
2. public enum SqlCommandType
{ UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;}
5. issues
1. n+1 問題
n+1次查詢 懶加載 (部分緩解 ) 1+n 問題 join 查詢主數(shù)據(jù)歹篓,是1次查詢,查詢出n條記錄揉阎;根據(jù)這n條主記錄庄撮,查詢從記錄,共需要n次毙籽,所以叫數(shù)據(jù)庫(kù)1+n問題 N+1 查詢問題”洞斯。概括地講,N+1 查詢問題可以是這樣引起的: 你執(zhí)行了一個(gè)單獨(dú)的 SQL 語句來獲取結(jié)果列表(就是“+1”)。 對(duì)返回的每條記錄,你執(zhí)行了一個(gè)查詢語句來為每個(gè)加載細(xì)節(jié)(就是“N”)坑赡。 這個(gè)問題會(huì)導(dǎo)致成百上千的 SQL 語句被執(zhí)行烙如。這通常不是期望的。 MyBatis 能延遲加載這樣的查詢就是一個(gè)好處,因此你可以分散這些語句同時(shí)運(yùn)行的消 耗毅否。然而,如果你加載一個(gè)列表,之后迅速迭代來訪問嵌套的數(shù)據(jù),你會(huì)調(diào)用所有的延遲加 載,這樣的行為可能是很糟糕的亚铁。
2. 結(jié)果嵌套 查詢嵌套
3. 緩存
一級(jí)緩存 二級(jí)緩存 Mybatis 使用到了兩種緩存:本地緩存(local cache)和二級(jí)緩存(second level cache)。 每當(dāng)一個(gè)新 session 被創(chuàng)建搀突,MyBatis 就會(huì)創(chuàng)建一個(gè)與之相關(guān)聯(lián)的本地緩存刀闷。任何在 session 執(zhí)行過的查詢語句本身都會(huì)被保存在本地緩存中熊泵,那么,相同的查詢語句和相同的參數(shù)所產(chǎn)生的更改就不會(huì)二度影響數(shù)據(jù)庫(kù)了甸昏。本地緩存會(huì)被增刪改顽分、提交事務(wù)、關(guān)閉事務(wù)以及關(guān)閉 session 所清空施蜜。 默認(rèn)情況下卒蘸,本地緩存數(shù)據(jù)可在整個(gè) session 的周期內(nèi)使用,這一緩存需要被用來解決循環(huán)引用錯(cuò)誤和加快重復(fù)嵌套查詢的速度翻默,所以它可以不被禁用掉缸沃,但是你可以設(shè)置 localCacheScope=STATEMENT 表示緩存僅在語句執(zhí)行時(shí)有效。 注意修械,如果 localCacheScope 被設(shè)置為 SESSION趾牧,那么 MyBatis 所返回的引用將傳遞給保存在本地緩存里的相同對(duì)象。對(duì)返回的對(duì)象(例如 list)做出任何更新將會(huì)影響本地緩存的內(nèi)容肯污,進(jìn)而影響存活在 session 生命周期中的緩存所返回的值翘单。因此,不要對(duì) MyBatis 所返回的對(duì)象作出更改蹦渣,以防后患哄芜。 MyBatis 包含一個(gè)非常強(qiáng)大的查詢緩存特性,它可以非常方便地配置和定制。MyBatis 3 中的緩存實(shí)現(xiàn)的很多改進(jìn)都已經(jīng)實(shí)現(xiàn)了,使得它更加強(qiáng)大而且易于配置柬唯。 默認(rèn)情況下是沒有開啟緩存的,除了局部的 session 緩存,可以增強(qiáng)變現(xiàn)而且處理循環(huán) 依賴也是必須的认臊。要開啟二級(jí)緩存,你需要在你的 SQL 映射文件中添加一行: <cache/> 映射語句文件中的所有 select 語句將會(huì)被緩存。 映射語句文件中的所有 insert,update 和 delete 語句會(huì)刷新緩存锄奢。 緩存會(huì)使用 Least Recently Used(LRU,最近最少使用的)算法來收回失晴。 根據(jù)時(shí)間表(比如 no Flush Interval,沒有刷新間隔), 緩存不會(huì)以任何時(shí)間順序 來刷新。 緩存會(huì)存儲(chǔ)列表集合或?qū)ο?無論查詢方法返回什么)的 1024 個(gè)引用拘央。 緩存會(huì)被視為是 read/write(可讀/可寫)的緩存,意味著對(duì)象檢索不是共享的,而 且可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改师坎。 The cache will only apply to statements declared in the mapping file where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then statements declared in the companion interface will not be cached by default. You will need to refer to the cache region using the @CacheNamespaceRef annotation.
4. # $
# 創(chuàng)建一個(gè)預(yù)處理語句參數(shù)
5. XML 映射文件 使用 Java 注解
命名空間(Namespaces)
6. 作用域(Scope)和生命周期
SqlSessionFactoryBuilder 方法作用域(也就是局部方法變量) SqlSessionFactory 最佳作用域是應(yīng)用作用域。最簡(jiǎn)單的就是使用單例模式或者靜態(tài)單例模式 SqlSession 每個(gè)線程都應(yīng)該有它自己的 SqlSession 實(shí)例堪滨。SqlSession 的實(shí)例不是線程安全的,因此是不能被共享的蕊温,所以它的最佳的作用域是請(qǐng)求或方法作用域袱箱。 如果你現(xiàn)在正在使用一種 Web 框架,要考慮 SqlSession 放在一個(gè)和 HTTP 請(qǐng)求對(duì)象相似的作用域中义矛。換句話說发笔,每次收到的 HTTP 請(qǐng)求,就可以打開一個(gè) SqlSession凉翻,返回一個(gè)響應(yīng)了讨,就關(guān)閉它。這個(gè)關(guān)閉操作是很重要的,你應(yīng)該把這個(gè)關(guān)閉操作放到 finally 塊中以確保每次都能執(zhí)行關(guān)閉前计。 映射器實(shí)例(Mapper Instances) 映射器是一個(gè)你創(chuàng)建來綁定你映射的語句的接口胞谭。映射器接口的實(shí)例是從 SqlSession 中獲得的。因此從技術(shù)層面講男杈,任何映射器實(shí)例的最大作用域是和請(qǐng)求它們的 SqlSession 相同的丈屹。盡管如此,映射器實(shí)例的最佳作用域是方法作用域伶棒。也就是說旺垒,映射器實(shí)例應(yīng)該在調(diào)用它們的方法中被請(qǐng)求,用過之后即可廢棄肤无。并不需要顯式地關(guān)閉映射器實(shí)例先蒋,盡管在整個(gè)請(qǐng)求作用域(request scope)保持映射器實(shí)例也不會(huì)有什么問題,但是很快你會(huì)發(fā)現(xiàn)宛渐,像 SqlSession 一樣竞漾,在這個(gè)作用域上管理太多的資源的話會(huì)難于控制。所以要保持簡(jiǎn)單皇忿,最好把映射器放在方法作用域(method scope)內(nèi)畴蹭。
7. 動(dòng)態(tài) SQL
if choose (when, otherwise) trim (where, set) foreach bind
8. Java API
每個(gè)映射器方法簽名應(yīng)該匹配相關(guān)聯(lián)的 SqlSession 方法,而字符串參數(shù) ID 無需匹配鳍烁。相反叨襟,方法名必須匹配映射語句的 ID。 此外幔荒,返回類型必須匹配期望的結(jié)果類型糊闽,單返回值時(shí)為所指定類的值,多返回值時(shí)為數(shù)組或集合爹梁。所有常用的類型都是支持的右犹,包括:原生類 型、Map姚垃、POJO 和 JavaBean 你可以傳遞多個(gè)參數(shù)給一個(gè)映射器方法念链。如果你這樣做了,默認(rèn)情況下它們將會(huì)以 "param" 字符串緊跟著它們?cè)趨?shù)列表中的位置來命名积糯,比如:#{param1}掂墓、#{param2}等。如果你想改變參數(shù)的名稱(只在多參數(shù)情況下)看成,那么你可以在參數(shù)上使用 @Param("paramName") 注解君编。 你也可以給方法傳遞一個(gè) RowBounds 實(shí)例來限制查詢結(jié)果。
9. 日志
<setting name="logImpl" value="LOG4J"/>
10. generator
<build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.39</version> </dependency> </dependencies> <configuration> <!--配置文件的路徑--> <configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build>