mybatis

mybatis

1. what is mybatis

MyBatis 是一款優(yōu)秀的持久層框架,它支持定制化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設(shè)置參數(shù)以及獲取結(jié)果集。MyBatis 可以使用簡單的 XML 或注解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數(shù)據(jù)庫中的記錄。 

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>
一個使用 MyBatis-Spring 的主要原因是它允許 MyBatis 參與到 Spring 的事務(wù)管理中。而 不是給 MyBatis 創(chuàng)建一個新的特定的事務(wù)管理器,MyBatis-Spring 利用了存在于 Spring 中的 DataSourceTransactionManager。
一旦 Spring 的 PlatformTransactionManager 配置好了,你可以在 Spring 中以你通常的做 法來配置事務(wù)。@Transactional 注解和 AOP(Aspect-Oriented Program,面向切面編程,譯 者注)樣式的配置都是支持的。在事務(wù)處理期間,一個單獨的 SqlSession 對象將會被創(chuàng)建 和使用。當(dāng)事務(wù)完成時,這個 session 會以合適的方式提交或回滾。
一旦事務(wù)創(chuàng)建之后,MyBatis-Spring 將會透明的管理事務(wù)。
<bean id="transactionManager"           class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>
SqlSessionTemplate
SqlSessionTemplate 是 MyBatis-Spring 的核心。 這個類負(fù)責(zé)管理 MyBatis 的 SqlSession, 調(diào)用 MyBatis 的 SQL 方法, 翻譯異常。 SqlSessionTemplate 是線程安全的, 可以被多個 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; //一級緩存  session 級別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ù)庫1+n問題 

N+1 查詢問題”。概括地講,N+1 查詢問題可以是這樣引起的:
你執(zhí)行了一個單獨的 SQL 語句來獲取結(jié)果列表(就是“+1”)。
對返回的每條記錄,你執(zhí)行了一個查詢語句來為每個加載細(xì)節(jié)(就是“N”)。
這個問題會導(dǎo)致成百上千的 SQL 語句被執(zhí)行。這通常不是期望的。
MyBatis 能延遲加載這樣的查詢就是一個好處,因此你可以分散這些語句同時運行的消 耗。然而,如果你加載一個列表,之后迅速迭代來訪問嵌套的數(shù)據(jù),你會調(diào)用所有的延遲加 載,這樣的行為可能是很糟糕的。
2. 結(jié)果嵌套 查詢嵌套
3. 緩存
一級緩存  二級緩存
Mybatis 使用到了兩種緩存:本地緩存(local cache)和二級緩存(second level cache)。
每當(dāng)一個新 session 被創(chuàng)建,MyBatis 就會創(chuàng)建一個與之相關(guān)聯(lián)的本地緩存。任何在 session 執(zhí)行過的查詢語句本身都會被保存在本地緩存中,那么,相同的查詢語句和相同的參數(shù)所產(chǎn)生的更改就不會二度影響數(shù)據(jù)庫了。本地緩存會被增刪改、提交事務(wù)、關(guān)閉事務(wù)以及關(guān)閉 session 所清空。
默認(rèn)情況下,本地緩存數(shù)據(jù)可在整個 session 的周期內(nèi)使用,這一緩存需要被用來解決循環(huán)引用錯誤和加快重復(fù)嵌套查詢的速度,所以它可以不被禁用掉,但是你可以設(shè)置 localCacheScope=STATEMENT 表示緩存僅在語句執(zhí)行時有效。
注意,如果 localCacheScope 被設(shè)置為 SESSION,那么 MyBatis 所返回的引用將傳遞給保存在本地緩存里的相同對象。對返回的對象(例如 list)做出任何更新將會影響本地緩存的內(nèi)容,進(jìn)而影響存活在 session 生命周期中的緩存所返回的值。因此,不要對 MyBatis 所返回的對象作出更改,以防后患。
MyBatis 包含一個非常強大的查詢緩存特性,它可以非常方便地配置和定制。MyBatis 3 中的緩存實現(xiàn)的很多改進(jìn)都已經(jīng)實現(xiàn)了,使得它更加強大而且易于配置。

默認(rèn)情況下是沒有開啟緩存的,除了局部的 session 緩存,可以增強變現(xiàn)而且處理循環(huán) 依賴也是必須的。要開啟二級緩存,你需要在你的 SQL 映射文件中添加一行:
<cache/>
映射語句文件中的所有 select 語句將會被緩存。
映射語句文件中的所有 insert,update 和 delete 語句會刷新緩存。
緩存會使用 Least Recently Used(LRU,最近最少使用的)算法來收回。
根據(jù)時間表(比如 no Flush Interval,沒有刷新間隔), 緩存不會以任何時間順序 來刷新。
緩存會存儲列表集合或?qū)ο?無論查詢方法返回什么)的 1024 個引用。
緩存會被視為是 read/write(可讀/可寫)的緩存,意味著對象檢索不是共享的,而 且可以安全地被調(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)建一個預(yù)處理語句參數(shù)
5. XML 映射文件 使用 Java 注解

命名空間(Namespaces)

6. 作用域(Scope)和生命周期
SqlSessionFactoryBuilder 方法作用域(也就是局部方法變量)
SqlSessionFactory  最佳作用域是應(yīng)用作用域。最簡單的就是使用單例模式或者靜態(tài)單例模式
SqlSession    每個線程都應(yīng)該有它自己的 SqlSession 實例。SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
  如果你現(xiàn)在正在使用一種 Web 框架,要考慮 SqlSession 放在一個和 HTTP 請求對象相似的作用域中。換句話說,每次收到的 HTTP 請求,就可以打開一個 SqlSession,返回一個響應(yīng),就關(guān)閉它。這個關(guān)閉操作是很重要的,你應(yīng)該把這個關(guān)閉操作放到 finally 塊中以確保每次都能執(zhí)行關(guān)閉。
映射器實例(Mapper Instances)
  映射器是一個你創(chuàng)建來綁定你映射的語句的接口。映射器接口的實例是從 SqlSession 中獲得的。因此從技術(shù)層面講,任何映射器實例的最大作用域是和請求它們的 SqlSession 相同的。盡管如此,映射器實例的最佳作用域是方法作用域。也就是說,映射器實例應(yīng)該在調(diào)用它們的方法中被請求,用過之后即可廢棄。并不需要顯式地關(guān)閉映射器實例,盡管在整個請求作用域(request scope)保持映射器實例也不會有什么問題,但是很快你會發(fā)現(xiàn),像 SqlSession 一樣,在這個作用域上管理太多的資源的話會難于控制。所以要保持簡單,最好把映射器放在方法作用域(method scope)內(nèi)。
7. 動態(tài) SQL
if
choose (when, otherwise)
trim (where, set)
foreach
bind
8. Java API
每個映射器方法簽名應(yīng)該匹配相關(guān)聯(lián)的 SqlSession 方法,而字符串參數(shù) ID 無需匹配。相反,方法名必須匹配映射語句的 ID。 
此外,返回類型必須匹配期望的結(jié)果類型,單返回值時為所指定類的值,多返回值時為數(shù)組或集合。所有常用的類型都是支持的,包括:原生類 型、Map、POJO 和 JavaBean

你可以傳遞多個參數(shù)給一個映射器方法。如果你這樣做了,默認(rèn)情況下它們將會以 "param" 字符串緊跟著它們在參數(shù)列表中的位置來命名,比如:#{param1}、#{param2}等。如果你想改變參數(shù)的名稱(只在多參數(shù)情況下),那么你可以在參數(shù)上使用 @Param("paramName") 注解。

你也可以給方法傳遞一個 RowBounds 實例來限制查詢結(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>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容