為什么要用Mybatis
- 代碼重復
- 結果集處理太復雜
- 連接管理
常見的工具
- DbUtils
- 數據源的支持
- QueryRunner
- 直接使用QueryRunner來查詢
- 提供了Resulthandler
- 通過反射來做屬性的映射
- 直接使用QueryRunner來查詢
- JDBCTemplate
- 提供了數據源的支持
- RowMapper來使用結果的處理
- RowMapper提供泛型,避免每次創(chuàng)建對應的實現類
- mapRow
- RowMapper提供泛型,避免每次創(chuàng)建對應的實現類
上述沒有解決的問題
- 直接把SQL寫在了代碼里面
- 條件只能按照順序傳入
- 沒有實現實體類到數據庫記錄的映射
- 沒有提供緩存等功能
ORM框架
- O
- 對象
- M
- 映射
- R
- 關系型數據庫
- Hibernate
- 直接利用
session#save
- 直接利用
問題
- 不能指定部分字段
- 無法自定義SQL,優(yōu)化困難
- 不支持動態(tài)SQL
關于選型
- 業(yè)務簡單的項目用Hibernate
- 需要靈活的SQL,使用Mybatis
- 對于性能要求高,使用JDBC
- Spring JDBC 可以和ORM框架混用
Mybatis
特性
- 連接池對連接進行管理
- SQL和代碼分離,集中管理
- 參數映射和動態(tài)SQl
- 結果集映射
- 緩存管理
- 重復SQL的提取
- 插件機制
Mybatis編程式開發(fā)
- 導入依賴
- 指定配置
mybatis-config.xml- 讀取配置
- 通過會話工廠構建器
SqlsessionFactoryBuild創(chuàng)建SqlsessionFactory - 創(chuàng)建會話session
- 通過session獲取Mapper
- BlogMapper.class
mappedstatement
核心對象作用域
SqlsessionFactoryBuilder
? 這個類可以被實例化、使用和丟棄,一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變量)。
? 你可以重用 SqlSessionFactoryBuilder 來創(chuàng)建多個 SqlSessionFactory實例,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
SqlsessionFactory
? SqlSessionFactory 一旦被創(chuàng)建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創(chuàng)建另一個實例。
? 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重復創(chuàng)建多次,多次重建 SqlSessionFactory 被視為一種代碼“壞習慣”。
? 因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態(tài)單例模式。
SqlSession
? 每個線程都應該有它自己的 SqlSession 實例。
? SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
? 絕對不能將 SqlSession 實例的引用放在一個類的靜態(tài)域,甚至一個類的實例變量也不行。 也絕不能將 SqlSession 實例的引用放在任何類型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現在正在使用一種 Web 框架,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說,每次收到 HTTP 請求,就可以打開一個 SqlSession,返回一個響應后,就關閉它。 這個關閉操作很重要,為了確保每次都能執(zhí)行關閉操作,你應該把這個關閉操作放到 finally 塊中。 下面的示例就是一個確保 SqlSession 關閉的標準模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的應用邏輯代碼
}
Mapper
? 映射器是一些綁定映射語句的接口。
? 映射器接口的實例是從 SqlSession 中獲得的。雖然從技術層面上來講,任何映射器實例的最大作用域與請求它們的 SqlSession 相同。但方法作用域才是映射器實例的最合適的作用域。
? 也就是說,映射器實例應該在調用它們的方法中被獲取,使用完畢之后即可丟棄。 映射器實例并不需要被顯式地關閉。
? 盡管在整個請求作用域保留映射器實例不會有什么問題,但是你很快會發(fā)現,在這個作用域上管理太多像 SqlSession 的資源會讓你忙不過來。 因此,最好將映射器放在方法作用域內。就像下面的例子一樣:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的應用邏輯代碼
}
核心配置
https://mybatis.org/mybatis-3/zh/configuration.html
- configuration(配置)
- properties(屬性)
- settings(設置)
- typeAliases(類型別名)
- typeHandlers(類型處理器)
- objectFactory(對象工廠)
- plugins(插件)
- environments(環(huán)境配置)
- environment(環(huán)境變量)
- transactionManager(事務管理器)
- dataSource(數據源)
- environment(環(huán)境變量)
- databaseIdProvider(數據庫廠商標識)
- mappers(映射器)
Properties
? 這些屬性可以在外部進行配置,并可以進行動態(tài)替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置。
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
設置(settings)
這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為。具體細節(jié)參考文檔
https://mybatis.org/mybatis-3/zh/configuration.html#settings
完整的settings
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
類型別名(typeAliases)
可以讓我們在使用的時候簡寫
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
使用的時候直接簡寫就行了
也可以直接指定包名
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
內置類型別名
-
基本數據類型(在前面+
_)- _byte->byte
- 。。。
-
包裝類(直接小寫)
- byte->Byte
-
其他默認類型(全小寫)
date Date decimal BigDecimal bigdecimal BigDecimal object Object map Map hashmap HashMap list List arraylist ArrayList collection Collection iterator Iterator
類型處理器
Java數據類型->數據庫類型
TypeHandlerRegistry
從代碼可以看到注冊了很多類型轉化器
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
// mybatis-typehandlers-jsr310
if (Jdk.dateAndTimeApiExists) {
this.register(Instant.class, InstantTypeHandler.class);
this.register(LocalDateTime.class, LocalDateTimeTypeHandler.class);
this.register(LocalDate.class, LocalDateTypeHandler.class);
this.register(LocalTime.class, LocalTimeTypeHandler.class);
this.register(OffsetDateTime.class, OffsetDateTimeTypeHandler.class);
this.register(OffsetTime.class, OffsetTimeTypeHandler.class);
this.register(ZonedDateTime.class, ZonedDateTimeTypeHandler.class);
this.register(Month.class, MonthTypeHandler.class);
this.register(Year.class, YearTypeHandler.class);
this.register(YearMonth.class, YearMonthTypeHandler.class);
this.register(JapaneseDate.class, JapaneseDateTypeHandler.class);
}
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
BooleanTypeHandler
public class BooleanTypeHandler extends BaseTypeHandler<Boolean> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType)
throws SQLException {
ps.setBoolean(i, parameter);
}
@Override
public Boolean getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getBoolean(columnName);
}
@Override
public Boolean getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getBoolean(columnIndex);
}
@Override
public Boolean getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getBoolean(columnIndex);
}
}
- 我們可以創(chuàng)建自己的類型轉化器
- JDBC->JAVA
- 列名來轉化結果
- 下標來轉化結果
- JAVA->JDBC類型
- 參數設置
- JDBC->JAVA
數據庫提供了Json類型,我們需要做轉化,就需要自己來寫自己的JSON類型
插入的時候,我們可以在語句指定TypeHandler
#{name,jdbcType=VARCHAR,typeHandler=com.gupaoedu.type.MyTypeHandler}
在ResultMap指定TypeHandler
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.gupaoedu.type.MyTypeHandler"/>
對象工廠(objectFactory)
每次 MyBatis 創(chuàng)建結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成實例化工作。
默認的對象工廠需要做的僅僅是實例化目標類,要么通過默認無參構造方法,要么通過存在的參數映射來調用帶有參數的構造方法。 如果想覆蓋對象工廠的默認行為,可以通過創(chuàng)建自己的對象工廠來實現。比如:
public class ExampleObjectFactory extends DefaultObjectFactory {
public Object create(Class type) {
return super.create(type);
}
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
public void setProperties(Properties properties) {
super.setProperties(properties);
}
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
插件(plugins)
環(huán)境配置(environments)
- 我們通過環(huán)境可以用來區(qū)分不同環(huán)境的配置
- 不過要記住:盡管可以配置多個環(huán)境,但每個 SqlSessionFactory 實例只能選擇一種環(huán)境。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
Mappers
- 導入的方式
類路徑的資源導入
<!-- 使用相對于類路徑的資源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
完全限定資源定位符URL
<!-- 使用完全限定資源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
映射器接口實現類的完全限定類名
<!-- 使用映射器接口實現類的完全限定類名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
保內的映射器接口實現全部注冊為映射器
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
我的筆記倉庫地址gitee 快來給我點個Star吧