《partner4java 講述MyBatis入門》之第二步:MyBatis 常用標籤

(導讀:MyBatis系列內容僅供技術參考和技術儲備。因本人目前未在項目中進行實戰,可能會出現一些知識誤導,還請諒解。本文大量參考了官方文檔,如果和官方文檔有偏差,請以官方文檔爲準)

主配置文件詳解

(我們本章節主要講解的mybatis-config.xml各配置)
Configuration
The MyBatis configuration contains settings and properties that have a dramatic effect on how MyBatis behaves. The high level structure of the document is as follows:
·configuration
· properties
·property
· settings
· typeAliases
· typeHandlers
· objectFactory
· plugins
· environments
 ·environment
·transactionManager
·dataSource
· databaseIdProvider
· mappers

(我們下面講解的各標籤在mybatis-config.xml文件添加順序是固定的,不能打亂:
- The content of element type "configuration" must match 
"(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,proxyFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
- Start tag of element <configuration>)

4.1 properties -- 參數值

三種方式:
1、引入指定文件
<properties resource="mysql.properties" />
<properties>元素允許在主配置文件之外提供一個properties格式對應文件,從而使得主配置文件更加通用。這樣對部署非常有用。
兩個屬性
resource--類路徑上的一個資源
url -- 統一資源定位符
最後通過名稱來引用這些屬性。

2、內部配置
property:
這是通過外部文件引入的方式,我們可以在properties內創建內部property元素,如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>

3、在Java代碼中傳入
Properties can also be passed into the SqlSessionBuilder.build() methods. For example:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);

使用方式:
然後可以使用配置文件裏的屬性值或者使用內部方式加入的屬性值,來給爲配置文件進行動態配置:

<dataSource type="POOLED">
	<property value="${driver}" name="driver" />
	<property value="${url}" name="url" />
	<property value="${username}" name="username" />
	<property value="${password}" name="password" />
</dataSource>
在這個例子中的用戶名和密碼等將被替換成屬性元素中設置的值。

問題:
當上面三種方式出現重複的值如何處理?
首先他們三者的加載順序是:內部配置 --> 引入指定文件 --> 在Java代碼中傳入。若在加載過程中出現重複值,後者會覆蓋前者。

4.2 settings -- 全局配置

<settings>元素添加若干屬性來設置這些可選項。MyBatis有許多配置項可用,並且每個配置項對於這個SQLMap實例來說都是全局的。
這些都是非常重要的配置,用於指定MyBatis在運行時的行爲。如果不指定,會使用默認值。

<settings>

		<!-- 在全局範圍內"啓用或禁用任何高速緩存" - 應該是除了session;默認值true -->
		<setting name="cacheEnabled" value="true" />

		<!-- 延遲加載是一種只加載必要信息而推遲加載其他未明確請求的數據的技術。也就是說,除非絕對需要,否則應用程序加載的數據越少越好。 lazyLoadingEnabled配置項用於指定當存在相關聯的已映射語句時,"是否使用延遲加載"。其有效值爲true或false,默認值爲true。 -->
		<!-- <setting name="lazyLoadingEnabled" value="true" /> -->

		<!-- 開啓懶加載。默認值爲true -->
		<!-- <setting name="aggressiveLazyLoading" value="true" /> -->

		<!-- 指定對象中什麼方法會觸發延遲加載。值爲以逗號分隔的方法名稱列表。 -->
		<!-- <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" 
			/> -->

		<!-- 是否允許一個statement返回多個ResultSets,但是需要驅動程序支持。默認值true -->
		<setting name="multipleResultSetsEnabled" value="true" />

		<!-- 是否可以使用別名獲取,具體取決於驅動程序是否支持。默認爲true -->
		<setting name="useColumnLabel" value="true" />

		<!-- 允許JDBC 生成主鍵。需要驅動器支持。如果設爲了true,這個設置將強制使用被生成的主鍵,有一些驅動器不兼容不過仍然可以執行(比如Derby)。默認值false -->
		<!-- <setting name="useGeneratedKeys" value="false" /> -->

		<!-- 指定是否以及如何自動映射數據字段到POJO字段。三個選項:NONE不自動映射;PARTIAL將只自動映射簡單的,沒有嵌套的結果;FULL將自動映射任何(包含嵌套或以其他方式)複雜的結果。默認值PARTIAL -->
		<setting name="autoMappingBehavior" value="PARTIAL" />

		<!-- 默認執行方式。三種方式:SIMPLE默認沒有特殊處理;REUSE重用PreparedStatements;BATCH重用statements和進行批量更新。默認值:SIMPLE -->
		<setting name="defaultExecutorType" value="SIMPLE" />

		<!-- 設置Statement默認數據庫響應超時時間,單位爲妙。 -->
		<setting name="defaultStatementTimeout" value="1" />

		<!-- 嵌套結果映射,是否使用rowbounds進行分頁,建議不要使用rowbounds進行分頁,特別是SQL執行返回結果很大的情況下(因爲它用的是返回後截取-邏輯分頁)。默認值false -->
		<setting name="safeRowBoundsEnabled" value="false" />

		<!-- 進行自動映射時,數據以下劃線命名,如數據庫返回的"order_address"命名字段是否映射爲class的"orderAddress"字段。默認爲false -->
		<setting name="mapUnderscoreToCamelCase" value="false" />

		<!-- MyBatis使用本地緩存來提高重複查詢的速度。兩個選項:SESSION,和我們hibernate裏的session類似,在同一個session中共享;STATEMENT,只在同一個statement中共享。默認值SESSION -->
		<setting name="localCacheScope" value="SESSION" />

		<!-- 有些驅動程序在返回值爲NULL時需要指定JDBC類型,大部分情況下是:NULL、VARCHAR或OTHER。默認值OTHER -->
		<setting name="jdbcTypeForNull" value="OTHER" />

	</settings>



4.3 typeAliases -- 別名

一個type alias是爲一個Java Type定義的短名稱。
除非別無選擇,否則沒人願意輸入像這樣一個長串的名字:com.partner4java.demo.entity.Account。
<typeAlias>元素允許定義別名,這樣就可以直接用account來代替剛纔的那個冗長的全限定類名了。
<typeAlias alias="user" type="com.partner4java.demo2.User" />
當在配置過程中定義account這個別名之後,就可以在任何時候使用他了,每當你必須提供一個數據類型時,都可以使用全限定類名或者別名來告訴iBATIS你想使用哪個數據。
iBATIS爲若干類型定義了別名,以免除開發人員必須手工添加這些別名的麻煩。
Demo:
<!-- 注意一點,這個放在properties後面,不然會加載出錯 -->
<typeAliases>
<typeAlias alias="user" type="com.partner4java.demo2.User" />
</typeAliases>

我們加了上面別名之後,就可以修改User.xml文件的返回類型<select id="selectUser" resultType="user">

我們可以使用更簡潔的方式,指定一個包,MyBatis會自動掃描包下的所有類,以非限定類名首字母小寫來作爲別名,當然你也可以使用@Alias註解手工指定:
<!-- mybatis-config.xml -->
<typeAliases>
	<package name="com.partner4java"/>
</typeAliases>

@Alias("user")
public class User {
	...
}

There are many built-in type aliases for common Java types. They are all case insensitive, note the
special handling of primitives due to the overloaded names.
Alias Mapped Type
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal Decimal
bigdecimal  BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

(學習到這裏,你應該轉到“自動映射”,學完自動映射然後再返回這裏)

4.4 typeHandlers -- 類型處理器

MyBatis使用類型處理器(type handler)將數據從數據庫特定的數據類型轉換爲應用程序中的數據類型,這樣你就可以創建一個以一種儘可能透明的方式來使用數據庫的應用程序。
類型處理器本質上就是一個翻譯器(translator)--他將數據庫返回的結果集合中的列“翻譯”爲相應的JavaBean中的字段。

MyBatis自帶的類型處理器:
Type Handler Java Types JDBC Types
BooleanTypeHandler java.lang.Boolean,booleanAny compatible BOOLEAN
ByteTypeHandler java.lang.Byte, byte Any compatible NUMERIC or BYTE
ShortTypeHandler java.lang.Short, short Any compatible NUMERIC or SHORT INTEGER
IntegerTypeHandler java.lang.Integer, int Any compatible NUMERIC or INTEGER
LongTypeHandler java.lang.Long, long Any compatible NUMERIC or LONG INTEGER
FloatTypeHandler java.lang.Float, float Any compatible NUMERIC or FLOAT
DoubleTypeHandler java.lang.Double, double Any compatible NUMERIC or DOUBLE
BigDecimalTypeHandler java.math.BigDecimal Any compatible NUMERIC or DECIMAL
StringTypeHandler java.lang.String CHAR, VARCHAR
ClobTypeHandler java.lang.String CLOB, LONGVARCHAR
NStringTypeHandler java.lang.String NVARCHAR, NCHAR
NClobTypeHandler java.lang.String NCLOB
ByteArrayTypeHandler byte[] Any compatible byte stream type
BlobTypeHandler byte[] BLOB, LONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER, or unspecified
EnumTypeHandler Enumeration Type VARCHAR any string compatible type, as the code is stored (not the index).
EnumOrdinalTypeHandler Enumeration Type Any compatible NUMERIC or DOUBLE, as the position is stored(not the code itself).

你可以覆蓋上面的類型處理器,也可以自定義一個類型處理器。
如果這麼做,你只需要三步:
1、寫一個類繼承自org.apache.ibatis.type.BaseTypeHandler;
2、使用@MappedJdbcTypes註解給該類指定要處理的類型;
3、在XML中配置一個本類的<typeHandler>標籤,然後指定掃描指定包內的。
Demo:
// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType ps.setString(i, parameter);
	}
	@Override
	public String getNullableResult(ResultSet rs, String columnName) throws SQLException return rs.getString(columnName);
	}
	@Override
	public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException return rs.getString(columnIndex);
	}
	@Override
	public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException return cs.getString(columnIndex);
	}
}


<typeHandlers>
	<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>


第2步你也可以省略,在<typeHandler>標籤中使用jdbcType屬性指定。
第3步如果你想掃描指定包下的所有帶有@MappedJdbcTypes註解的類爲類型處理器,不單獨聲明,可替換爲:
<!-- mybatis-config.xml -->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>

4.5 objectFactory

Each time MyBatis creates a new instance of a result object, it uses an ObjectFactory instance to do so. 
The default ObjectFactory does little more than instantiate the target class with a default constructor, or a parameterized constructor if parameter mappings exist. 
If you want to override the default behaviour of the ObjectFactory, you can create your own. For example:
// ExampleObjectFactory.java
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);
	}
}

<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
	<property name="someProperty" value="100"/>
</objectFactory>

The ObjectFactory interface is very simple. It contains two create methods, one to deal with the
default constructor, and the other to deal with parameterized constructors. Finally, the setProperties
method can be used to configure the ObjectFactory. Properties defined within the body of the
objectFactory element will be passed to the setProperties method after initialization of your
ObjectFactory instance.

4.6 plugins

不做討論,當你對MyBatis足夠熟悉的時候可以自行研究。


4.7 environments -- 多數據源配置

environment:
MyBatis的可配置多個環境。
這可以幫助你解決應用場景中很多問題。
例如,你可能有不同的配置,爲您的開發,測試和生產環境。
(但是,在你使用時只能選擇其中一種)
Demo:
<!-- 配置數據源相關的信息 -->
<environments default="demo1">
	<environment id="demo1">
		<transactionManager type="JDBC" />
		<dataSource type="POOLED">
			<property value="${driver}" name="driver" />
			<property value="${url}" name="url" />
			<property value="${username}" name="username" />
			<property value="${password}" name="password" />
		</dataSource>
	</environment>
	<environment id="demo2">
		<transactionManager type="JDBC" />
		<dataSource type="POOLED">
			<property value="${driver}" name="driver" />
			<property value="${url}" name="url" />
			<property value="${username}" name="username" />
			<property value="${password}" name="password" />
		</dataSource>
	</environment>
</environments>

比如上面這個,他會默認使用demo1,但是你如果想代碼指定demo2,可以調用SqlSessionFactoryBuilder().build(InputStream inputStream, String environment)接收String類型environment參數的方法。

<transactionManager>元素:
由於MyBatis就是爲了使數據庫訪問變得更加簡單而設計的,因此他將爲你處理所有的數據庫事務。
使用MyBatis時,有哪些事務管理器實現類是可用的。可以考慮使用MyBatis那些預定義的事務管理器。
<transactionManager>元素的type屬性就是用於指定應該使用哪個事務管理器的。
iBATIS提供了若干內建事務管理器實現:
JDBC 用於提供簡單的基於jdbc的事務管理。大多數情況下,使用這個事務管理器就足夠了。
EXTERNAL 用於提供非事務管理,並假定管理事務的是應用程序,而不是MyBatis

對於事務管理器,另一個可用的設置就是commitRequired屬性,該屬性可被設置爲true或者false,默認是爲false。
他主要用於那些要求在釋放某個連接之前必須提交(commit)或者回退(rollback)的情況。
對於某些操作(例如選擇和存儲過程調用),通常並不需要事務,因此一般會將其忽略。
某些數據庫驅動程序(例如Sybase驅動程序)不會輕易釋放連接,直至該連接的每個已啓動事務都已提交或回退。在這些情況下,就可以使用commitRequired屬性來強制釋放連接,即使在那些通常需要在一個事務中釋放卻沒用釋放的情況。

<dataSource>元素
在java中,使用連接池的標準方法就是通過javax.sql.DataSource對象。
事務管理的<dataSource>元素有一個屬性,用來告訴MyBatis他應該爲其數據源工廠實例化並使用哪個類。
<dataSource>元素名字很容易仍人產生誤解,因爲實際上他並不是用來定義DataSource,而是用來定義DataSourceFactory實現類,MyBatis將用這個實現類來創建實際的DataSource。
MyBatis具有三種數據源工廠實現類:
UNPOOLED -- 最簡單的方式,沒有加入連接池
有五個基本配置:
·driver – This is the fully qualified Java class of the JDBC driver (NOT of the DataSource class
if your driver includes one).
· url – This is the JDBC URL for your database instance.
·username – The database username to log in with.
·password - The database password to log in with.
·defaultTransactionIsolationLevel – The default transaction isolation level for
connections.
Optionally, you can pass properties to the database driver as well. To do this, prefix the properties
with driver., for example:
·driver.encoding=UTF8
This will pass the property encoding, with the value UTF8, to your database driver via the
DriverManager.getConnection(url, driverProperties) method.

POOLED -- 這是一個加入了連接池策略的的方式
在上面五個基本配置外又增加了:
·poolMaximumActiveConnections – This is the number of active (i.e. in use) connections that
can exist at any given time. Default: 10
·poolMaximumIdleConnections – The number of idle connections that can exist at any given
time.
·poolMaximumCheckoutTime – This is the amount of time that a Connection can be "checked
out" of the pool before it will be forcefully returned. Default: 20000ms (i.e. 20 seconds)
·poolTimeToWait – This is a low level setting that gives the pool a chance to print a log status
and re-attempt the acquisition of a connection in the case that it’s taking unusually long (to avoid
failing silently forever if the pool is misconfigured). Default: 20000ms (i.e. 20 seconds)
·poolPingQuery – The Ping Query is sent to the database to validate that a connection is in
good working order and is ready to accept requests. The default is "NO PING QUERY SET",
which will cause most database drivers to fail with a decent error message.
·poolPingEnabled – This enables or disables the ping query. If enabled, you must also set the
poolPingQuery property with a valid SQL statement (preferably a very fast one). Default: false.
·poolPingConnectionsNotUsedFor – This configures how often the poolPingQuery
will be used. This can be set to match the typical timeout for a database connection, to
avoid unnecessary pings. Default: 0 (i.e. all connections are pinged every time – but only if
poolPingEnabled is true of course).

JNDI -- This implementation of DataSource is intended for use with containers such as EJB or
Application Servers that may configure the DataSource centrally or externally and place a reference to
it in a JNDI context. This DataSource configuration only requires two properties:
·initial_context – This property is used for the Context lookup from the InitialContext
(i.e. initialContext.lookup(initial_context)). This property is optional, and if omitted, then the
data_source property will be looked up against the InitialContext directly.
·data_source – This is the context path where the reference to the instance of the DataSource
can be found. It will be looked up against the context returned by the initial_context lookup, or
against the InitialContext directly if no initial_context is supplied.
Similar to the other DataSource configurations, it’s possible to send properties directly to the
InitialContext by prefixing those properties with env., for example:
·env.encoding=UTF8
This would send the property encoding with the value of UTF8 to the constructor of the
InitialContext upon instantiation.


4.8 databaseIdProvider -- 不同庫SQL匹配

我們可以配置不同數據庫的不同SQL,會根據數據庫引擎進行決定。
<databaseIdProvider type="VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>


4.9 mappers -- 引入SQL

前面我們已經提到了,有兩種使用方式:
1、在XML中配置SQL,需要挨個在mybatis-config.xml中指定
如:
<mappers>
	<mapper resource="com/partner4java/demo2/User.xml" />
</mappers>

2、使用註解管理SQL,直接指定要掃描的jar包
<mappers>
	<!-- 指定掃描指定目錄的註解 -->
	<package name="com.partner4java" />
</mappers>

public interface UserMapper {
	//我們用到了註解@Select,參數問SQL
	@Select("select * from user where id = #{id}")
	public User selectUserByID(int id);
}


自動映射

從數據庫中獲取數據是一個應用程序中最基本的操作之一。MyBatis框架使得編寫大部分的SELECT語句都毫不費力,並且提供了很多特性,使得你幾乎可以從數據庫中得到任何想要的數據。

1.使用XML
正如我們helloworld裏面的一樣,在XML中配置select語句。
查詢參數替代(#{})符號。

<!-- User.xml -->
<!-- parameterType爲我們參數類型 -->
<select id="selectUserByID" resultType="user" parameterType="int">
	select * from user where id = #{value}
</select>

public static void main(String[] args) {
	InputStream is = null;
	SqlSessionFactory sqlSessionFactory = null;
	SqlSession session = null;
	try {
		is = Resources.getResourceAsStream("mybatis-config4.xml");
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
		session = sqlSessionFactory.openSession();
		//執行一個唯一返回值的查詢;user.selectUserByID由兩部分組成:“user.”爲我們的namespace;“selectUserByID”爲我們具體id。
		User user = session.selectOne("user.selectUserByID", 1);
		System.out.println(user);
	} catch (IOException e) {
		e.printStackTrace();
	}finally{
		try {
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			session.close();
		}
	}
}


問題:MyBatis框架是如何處理該語句的?
首先,MyBatis將查找名爲user.selectUserByID的已映射語句,並把#{value}佔位符變換爲一個預備語句參數,就是轉換成我們JDBC中所用的"?"格式。

2.使用註解的方式
我們的User.xml就不需要了,但是需要額外兩步操作:
配置mybatis-config.xml
<mappers>
	<!-- 指定掃描指定目錄的註解 -->
	<package name="com.partner4java" />
</mappers>

新建Mapper接口類:
(Mapper接口只是定義了各種查詢的格式,含義很容易理解,方法參數就是爲我們的SQL輸入參數,返回值爲SQL返回封裝類型)
public interface UserMapper {
	//我們用到了註解@Select,參數問SQL
	@Select("select * from user where id = #{id}")
	public User selectUserByID(int id);
}

執行:
is = Resources.getResourceAsStream("mybatis-config5.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
session = sqlSessionFactory.openSession();
//指定全類名,加方法
User user = session.selectOne("com.partner4java.demo2.UserMapper.selectUserByID", 1);
System.out.println(user);

上面的執行,我們還可以進行修改,給session指定一個默認的Mapper接口類(這樣每次執行都不需要再給全類名了,更簡潔):
is = Resources.getResourceAsStream("mybatis-config5.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
session = sqlSessionFactory.openSession();
// 指定我們本session加載那個Mapper接口類
session.getMapper(UserMapper.class);
// 現在就只需要指定方法名
User user = session.selectOne("selectUserByID", 1);
System.out.println(user);

3.自動結果映射
你可能已經注意到了,在所有我們給出的示例中都沒有定義任何結果映射(result map),但是確實定義了結果類(result class)。
這種方式可行是因爲MyBatis的自動結果映射機制,該機制會在已映射語句第一次被執行時,迅速的自動創建一個結果映射,然後將它應用於這條已映射語句。
可以有3種方式來使用這個特性:單列選擇(single-column selection),固定多列選擇(fixed-column list selection)和動態多列選擇(dynamic-column list selection)。

單列選擇 --
如果只想從某個查詢中獲取單例,就可以使用別名value作爲一種快捷方式來完成此目的,這樣就可以不需要定義複雜的結果映射了,只需要指定resultType:
<select id="selectUsernameByID" resultType="string" parameterType="int">
select username from user where id = #{value}
</select>

固定多列選擇 -- 
如果需要查詢多列,就可以使用自動結果映射來告訴MyBatis將列名作爲bean的特性名,或者作爲Map鍵。
當以這種方式映射到bean時,需要牢記一點:如果所選擇的列在數據庫中存在,但是不存在於你要映射的bean中,你將不會得到任何錯誤或警告,但是也得不到任何數據--這些數據只會靜靜的被忽略。
映射到Map對象時,也有相似的問題:儘管你仍然可以得到數據,但是這些數據不會在你所期望的地方。

動態多列選擇 -- 
如果語句中被選擇的那些字段列表可以在運行時改變,那麼也可以使用動態結果映射。
(後面章節我們再具體說動態映射)

4.聯結相關數據
有時候出於製作報表或者其他的什麼目的,你可能想要把多個數據表聯結起來,形成一個平板型結構。
MyBatis框架可以讓你毫不費力地完成這項工作,因爲它是將SQL語句映射爲對象,而不是將數據庫中的表映射爲對象,所以映射表查詢和映射多表查詢幾乎沒有什麼不同。


作用範圍和生命週期

和Hibernate一樣,我們類似Session各種資源的作用範圍和生命週期是很重要的。

SqlSessionFactoryBuilder:
這個類在創建SqlSessionFactory之後沒必要保持其“生命”,因爲在創建了所需的SqlSessionFactory之後SqlSessionFactoryBuilder就沒有任何作用。
SqlSessionFactoryBuilder建議在方法內使用即可,這樣它的聲明週期即爲該方法執行過程。
保證其及時釋放的意義在於,SqlSessionFactoryBuilder內持有了很多解析後的XML數據。

SqlSessionFactory:
創建SqlSession的工廠類。
猶如我們常用的工廠類,工廠是不建議銷燬的,因爲它要一直不斷給我們創建所需對象。
最好的處理方式就是把SqlSessionFactory設置爲單例的。

SqlSession:
每個線程都應該有自己的SqlSession實例。
SqlSession的實例不能共享,不是線程安全的。
因此,最好的範圍是請求或方法的範圍。
不要讓一個SqlSession實例的引用一個靜態字段,甚至一個類的實例字段。
在會話結束時,你要確保它的及時正確關閉。
如:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}

Mapper Instances:
映射器是您創建的綁定映射statements的接口。
從SqlSession實例的映射接口收購。
它的生命週期是依附於SqlSession,因此不需要自己主動銷燬。
映射器實例的最佳範圍是方法範圍(同SqlSession)。
也就是說,它在方法被請求時生效,被用於在該方法內,然後伴隨方法結束時丟棄。
如:
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// do work
} finally {
session.close();
}


Mapper XML Files 基礎篇

1.Mapper XML Files
首先還是那句話MyBatis與O/RM不同:他不是直接把類映射爲數據庫表或者說把類的字段映射爲數據庫列,而是“把SQL語句的參數和結果映射爲類”。
所以,我們主要的工作就是書寫維護SQL。我們使用單獨的XML文件維護一組相關SQL。也就是我們接下來學習的知識。
首先根元素爲<mapper>,這是“奇蹟”發生的地方,如果你和JDBC代碼對比,你會立即看到節省了95%的代碼,且它以SQL爲核心,讓我們可以尋求極致完美的完成自己的項目。

(我們這裏不再一一闡述SqlSession的各方法作用,如果你學過JDBC,根據方法名很容易猜想作用,如果沒學過JDBC請參考 http://blog.csdn.net/partner4java/article/details/8213119)

Mapper XML文件中只有很少數的一級標籤:
·cache – 給定命名空間的緩存配置.
·cache-ref – 從另一個命名空間引用緩存配置.
·resultMap – 這些標籤中最強大最複雜的標籤,描述如何把返回的數據結果封裝成Java對象.
·parameterMap – 已過時(用於描述Java對象如何對應數據庫佔位符).
·sql – 元素用於創建一個文本片段,而這些文本片段又可以組合起來以創建完整的SQL語句。.
·insert – A mapped INSERT statement.
·update – A mapped UPDATE statement.
·delete – A mapped DELETE statement.
·select – A mapped SELECT statement.

接下來的章節將詳細描述這些元素:

·select
select將是我們最常用的元素,不過不用擔心,因爲它使用起來很簡單。
For example:
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
This statement is called selectPerson, takes a parameter of type int (or Integer), and returns a
HashMap keyed by column names mapped to row values.

Notice the parameter notation:
#{id}
This tells MyBatis to create a PreparedStatement parameter. With JDBC, such a parameter would be
identified by a "?" in SQL passed to a new PreparedStatement, something like this:
// Similar JDBC code, NOT MyBatis…
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);

在你使用MyBatis時上面的代碼就不需要你寫了,這也是我們使用MyBatis的原因。不過MyBatis幫你做的不僅如此,還包含結果的獲取封裝,這個只是我們後面會講解。

The select element has more attributes that allow you to configure the details of how each statement should behave.
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10000"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">

Attribute Description
--------------------------------------------------------------------------
id A unique identifier in this namespace that can be used
to reference this statement.

parameterType The fully qualified class name or alias for the
parameter that will be passed into this statement.

parameterMap This is a deprecated approach to referencing an
external parameterMap. Use inline parameter
mappings and the parameterType attribute.

resultType The fully qualified class name or alias for the expected
type that will be returned from this statement. Note
that in the case of collections, this should be the
type that the collection contains, not the type of the
collection itself. Use resultType OR resultMap,
not both.

resultMap A named reference to an external resultMap.
Result maps are the most powerful feature of MyBatis,
and with a good understanding of them, many difficult
mapping cases can be solved. Use resultMap OR
resultType, not both.


flushCache Setting this to true will cause the local and 2nd level
caches to be flushed whenever this statement is
called. Default: false for select statements.


useCache Setting this to true will cause the results of this
statement to be cached in 2nd level cache. Default:
true for select statements.


timeout This sets the number of seconds the driver will wait for
the database to return from a request, before throwing
an exception. Default is unset (driver dependent).

fetchSize This is a driver hint that will attempt to cause the
driver to return results in batches of rows numbering
in size equal to this setting. Default is unset (driver
dependent).


statementType Any one of STATEMENT, PREPARED or CALLABLE.
This causes MyBatis to use Statement,
PreparedStatement or CallableStatement
respectively. Default: PREPARED.


resultSetType Any one of FORWARD_ONLY|
SCROLL_SENSITIVE| SCROLL_INSENSITIVE.
Default is unset (driver dependent).

databaseId In case there is a configured databaseIdProvider,
MyBatis will load all statements with no
databaseId attribute or with a databaseId that
matches the current one. If case the same statement
if found with and without the databaseId the latter
will be discarded.

我們挨個講解一下select的這些屬性標籤:
id -- 一個命名空間裏的唯一標識,你如果動手完成了前面的helloworld,這個沒什麼難理解的。

parameterType -- 自動映射參數類型,值爲一個全類名或者alias別名。
(具體可參照iBatis中講述的映射參數 http://blog.csdn.net/partner4java/article/details/8175358)
Demo1:簡單類型
我們根據用戶id查詢對應用戶信息:
這裏的parameterType就爲一個int類型
<select id="selectUsernameByID" resultType="string" parameterType="int">
select username from user where id = #{value}
</select>
String username = session.selectOne("user.selectUsernameByID", 1);

Demo2:自定義對象類型
我們查詢一條Person數據,它的名字爲hello,年齡爲23。
<select id="queryPerson" resultType="person" parameterType="person">
select * from person where person_name = #{personName} and age = #{age}
</select>
Person person = new Person();
person.setPersonName("hello");
person.setAge(23);
System.out.println(sqlSession.selectList("person.queryPerson",person));
parameterType="person"我們指定了一個alias別名。

parameterMap -- 自定義參數映射(已過時)
前面parameterType藉助了默認的反射機制,我們還可以自己指定各種參數對應關係和類型:
<parameterMap class="account" id="accountMap">
<parameter property="username" nullValue="hello" jdbcType="VARCHAR"/>
<parameter property="password" nullValue="123" jdbcType="VARCHAR"/>
<parameter property="groupname" nullValue="123" jdbcType="EMPLOYEE"/>
</parameterMap>
<select ... parameterMap="accountMap">
</select>

resultType -- 自動映射返回類型,值爲一個全類名或者alias別名。
如果數據庫返回列名稱和POJO名字相對應,就應該直接使用本配置,MyBatis會自動把相匹配的數據塞進我們這裏指定的類。
有時我們的類單詞分隔符使用的下劃線,而POJO是大寫字母,我們可以在setting中配置mapUnderscoreToCamelCase(參考“配置詳解”)。

resultMap -- 自定義返回映射
用法同parameterMap

cache將在後面章節具體學習。

timeout -- 最大相應時間。

fetchSize -- 
參照JDBC:setFetchSize 最主要是爲了減少網絡交互次數設計的。訪問ResultSet時,如果它每次只從服務器上取一行數據,則會產生大量的開銷。setFetchSize的意 思是當調用rs.next時,ResultSet會一次性從服務器上取得多少行數據回來,這樣在下次rs.next時,它可以直接從內存中獲取出數據而不 需要網絡交互,提高了效率。 這個設置可能會被某些JDBC驅動忽略的,而且設置過大也會造成內存的上升。

databaseId -- 結合“配置詳解”裏的databaseIdProvider

·insert, update and delete
The data modification statements insert, update and delete are very similar in their implementation:
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20000">
<update
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20000">
<delete
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20000">


Attribute Description
id A unique identifier in this namespace that can be used
to reference this statement.

parameterType The fully qualified class name or alias for the
parameter that will be passed into this statement.


parameterMap This is a deprecated approach to referencing an
external parameterMap. Use inline parameter
mappings and the parameterType attribute.

flushCache Setting this to true will cause the 2nd level and local
caches to be flushed whenever this statement is
called. Default: true for insert, update and delete
statements.

timeout This sets the maximum time the driver will wait for the
database to return from a request, before throwing an
exception. Default is unset (driver dependent).


statementType Any one of STATEMENT, PREPARED or CALLABLE.
This causes MyBatis to use Statement,
PreparedStatement or CallableStatement
respectively. Default: PREPARED.


useGeneratedKeys (insert only) This tells MyBatis to use the JDBC
getGeneratedKeys method to retrieve keys
generated internally by the database (e.g. auto
increment fields in RDBMS like MySQL or SQL
Server). Default: false


keyProperty (insert only) Identifies a property into which
MyBatis will set the key value returned by
getGeneratedKeys, or by a selectKey child
element of the insert statement. Default: unset.


keyColumn (insert only) Sets the name of the column in the table
with a generated key. This is only required in certain
databases (like PostgreSQL) when the key column is
not the first column in the table.

The following are some examples of insert, update and delete statements.
<insert id="insertAuthor" parameterType="domain.blog.Author">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor" parameterType="domain.blog.Author">
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
</update>
<delete id="deleteAuthor" parameterType="int">
delete from Author where id = #{id}
</delete>

這些元素的使用和我們select大致相同。
不同的是多了useGeneratedKeys屬性標籤:
用於在你插入的時候自動生成主鍵。
作何解釋呢?比如你MySQL(支持自動生成主鍵),做了一條插入:
<insert id="addPerson" parameterType="person">
insert into person (id,person_name,password,age) values (#{id},#{personName},#{pass},#{age})
</insert>
Person person = new Person();
person.setPersonName("hello");
person.setAge(23);
sqlSession.insert("person.addPerson", person);
sqlSession.commit();
System.out.println(person.getId());
執行完,打印id爲null。可是,有時候我們想獲取id怎麼呢?
<insert id="addPerson" parameterType="person" useGeneratedKeys="true" keyProperty="id">
insert into person (id,person_name,password,age) values (#{id},#{personName},#{pass},#{age})
</insert>
首先打開使用getGeneratedKeys -- useGeneratedKeys="true",然後告訴MyBatis哪個是你的主鍵keyProperty="id"(id爲我們POJO主鍵屬性名稱)。
當然使用本特性的關鍵在於驅動程序支持。

MyBatis還對不支持自增長的數據庫提供了支持,在insert標籤中使用selectKey標籤:
MyBatis has another way to deal with key generation for databases that don't support auto-generated
column types, or perhaps don't yet support the JDBC driver support for auto-generated keys.
Here's a simple (silly) example that would generate a random ID (something you'd likely never do,
but this demonstrates the flexibility and how MyBatis really doesn't mind):
<insert id="insertAuthor" parameterType="domain.blog.Author">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection}
</insert>
In the example above, the selectKey statement would be run first, the Author id property would
be set, and then the insert statement would be called. This gives you a similar behavior to an autogenerated
key in your database without complicating your Java code.
The selectKey element is described as follows:
<selectKey
keyProperty="id"
resultType="int"
order="BEFORE"
statementType="PREPARED">
Attribute Description
--------------------------------------------------------
keyProperty The target property where the result of the
selectKey statement should be set.

resultType The type of the result. MyBatis can usually figure
this out, but it doesn't hurt to add it to be sure.
MyBatis allows any simple type to be used as the key,
including Strings.

order This can be set to BEFORE or AFTER. If set to
BEFORE, then it will select the key first, set the
keyProperty and then execute the insert
statement. If set to AFTER, it runs the insert statement
and then the selectKey statement – which is
common with databases like Oracle that may have
embedded sequence calls inside of insert statements.

statementType Same as above, MyBatis supports STATEMENT,
PREPARED and CALLABLE statement types that
map to Statement, PreparedStatement and
CallableStatement respectively.


·sql
This element can be used to define a reusable fragment of SQL code that can be included in other
statements. For example:
<sql id="userColumns"> id,username,password </sql>


The SQL fragment can then be included in another statement, for example:
<select id="selectUsers" parameterType="int" resultType="hashmap">
select <include refid="userColumns"/>
from some_table
where id = #{id}
</select>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章