全局配置檔案的屬性
properties屬性(基本不用)
<!-- mybatis可以使用properties來引入外部properties配置檔案的内容
resource:引入類路徑下的資源
url:引入網絡路徑下的資源,或者磁盤上絕對路徑
-->
<properties resource="jdbcconfig.properties"></properties>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1234
settings設定
這是 MyBatis 中極為重要的調整設定,它們會改變 MyBatis 的運作時行為。
比如裡面的mapUnderscoreToCamelCase就是是否開啟自動駝峰命名規則(camel case)映射,就是從資料庫列名THE_NAME到java屬性名theName的類映射,使用查詢的時候能自動進行比對,注意下劃線後面的字母還是大寫的,這是駝峰命名規則
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
typeAliases别名處理器
<!--
typeAliases:别名處理器,可以為我們的Java類型起别名(别名不區分大小寫)
typeAlias是為某一個Java類型起别名
type:指定要起别名的類型全限類名,預設是類名的小寫
alias:指定新的别名
package:為某個包下的所有類批量起别名
name:指定包名(為目前包以及下面的所有子包的每一個類都起一個預設别名)
但是上面的還有一個問題,就是這個包和它的子包有一個類的名字是一樣的
這樣的話,mybatis就會報錯,是以我們還有一個方法起别名
使用@Alias注解起别名
批量起别名的情況下,使用注解為某個類型指定新的别名
-->
<typeAliases>
<typeAlias type="bean.Emp" alias="emp"/>
<package name=""/>
</typeAliases>
environments屬性
<!--
environments:多種環境,mybatis可以配置多種環境,default是指定使用某種環境,達到快速切換環境的效果
environment:配置一個具體的環境資訊,id是這個環境的唯一辨別
transactionManager:事務管理器
type:事務管理器的類型,有:
JDBC:JdbcTransactionFactory
MANAGED:ManagedTransactionFactory
還可以自定義事務管理器:隻要實作TransactionFactory接口,type是指定它的全限類名就行,
或者參考上面兩個這麼寫的就怎麼寫
dataSource:資料源
type:資料源的類型,有;
JNDI:JndiDataSourceFactory
POOLED:PooledDataSourceFactory
UNPOOLED:UnpooledDataSourceFactory
還可以自定義資料源:實作DataSourceFactory接口就行,type就是它的全限類名
-->
<environments default="development_mysql">
<environment id="development_mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
databaseIdProvider屬性
全局配置
<!--
databaseIdProvider:讓mybatis支援多資料庫廠商,讓mybatis的移植性更好
type="DB_VENDOR":作用是得到資料庫廠商的辨別(就是驅動,根據getDatabaseProductName()的方法得到)
mybatis就能根據資料庫廠商辨別來執行不同的sql
比如MySQL Oracle SQL Server
可以利用下面這個标簽給資料庫廠商起一個好用的别名
<property name="" value=""/>
然後就是映射檔案的配置,
在select标簽裡面的databaseId="mysql"屬性表明這個查詢是使用哪一個資料庫的
-->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracl"/>
</databaseIdProvider>
映射檔案配置
如果這裡是在mysql環境下,會加載有mysql辨別的和沒有任何辨別的語句,如果執行的時候優先使用帶mysql辨別的sql
<mapper namespace="bean.EmpMapper">
<select id="getEmpById" resultType="bean.Emp" >
select eid,ename name,email,gender from emp where eid= #{id}
</select>
<!-- 這是兩個版本資料庫的查詢,上面是mysql,下面的是oracle的查詢 -->
<select id="getEmpById" resultType="bean.Emp" databaseId="mysql">
select eid,ename name,email,gender from emp where eid= #{id}
</select>
<select id="getEmpById" resultType="bean.Emp" databaseId="oracle">
select eid,ename name,email,gender from emp where eid= #{id}
</select>
</mapper>
這裡也配置了兩個環境
<environments default="development_mysql">
<environment id="development_mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
<environment id="development_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1234
orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=1234
mappers映射注冊
<!-- 将寫好的sql映射檔案注冊到全局配置檔案中 -->
<!--
mappers:将sql映射注冊到全局檔案中
mapper:注冊一個sql映射
注冊配置檔案
resource:引用類路徑下的sql映射檔案
url:引用網絡路徑或者磁盤路徑的sql映射檔案
注冊接口:
class:引用接口,就是注冊接口,而不是映射檔案了
1.有sql映射檔案的,映射檔案名必須和接口同名,而且是要放在接口同一目錄下
2.mybatis也支援沒有sql映射檔案的,就是所有的sql都是寫在接口的注解上面
上面兩種方法的推薦:
比較重要的,複雜的Dao接口寫在映射檔案上
不重要,簡單的Dao接口為了開發友善可以寫在注解上面
3.第三種就是批量注冊了,使用package标簽
注意的是批量注冊會找到包下的所有類
如果是接口注釋的我們還能了解,
但是如果是檔案注冊的mybatis是怎麼找到映射檔案的呢?
是以這個批量注冊還是有要求的,如果有映射注冊,
就必須接口類和映射檔案在同一個包下
注意你可能覺得java源檔案和xml檔案放在一起,這樣不好看
是以你可以在資源檔案夾conf的目錄下建立一個和接口類一模一樣的包名
那麼在編譯的時候,因為src和conf都是資源檔案夾,是以會被解析到一個檔案夾裡面
那麼這兩個檔案夾就是同一個檔案夾了,隻是在開發的時候視覺上不是同一個檔案夾
-->
<mappers>
<mapper resource="EmpMapper.xml" />
<mapper class="bean.EmpMapperAnnotation"/>
<package name="bean"/>
</mappers>
注意那些檔案路徑的問題, 比如什麼java源檔案,批量注冊的包路徑名,包之間使用.(點号), 而什麼xml檔案的路徑名,包之間使用的是/(斜杠)
映射檔案
增删改查
下面的代碼中,增删改的操作做,需要傳回值就直接是傳回Integer/Long/Boolean這些類型,這些傳回值mybatis已經定義好了,會自動幫你封裝,隻要傳回不是0行資料,Boolean都是True
package bean;
public interface EmpMapper
{
public Emp getEmpById(Integer id);
public long addEmp(Emp emp);
public boolean updateEmp(Emp emp);
public long deleteEmpById(Integer eid);
}
映射檔案寫法
<!--
public long addEmp(Emp emp);
public boolean updateEmp(Emp emp);
public long deleteEmpById(Integer eid);
-->
<insert id="addEmp" parameterType="bean.Emp">
insert into emp (ename,email,gender) values(#{ename},#{email},#{gender})
</insert>
<update id="updateEmp">
update emp set ename=#{ename},email=#{email},gender=#{gender}
where eid=#{eid}
</update>
<delete id="deleteEmpById">
delete from emp where eid=#{eid}
</delete>
@org.junit.Test
public void Test4() throws IOException
{
String resource="mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession openSession = sqlSessionFactory.openSession();
try
{
EmpMapper empMapper = openSession.getMapper(EmpMapper.class);
//Emp emp=new Emp(2, "jane", "jane.com", "男");
//empMapper.addEmp(emp);
//boolean updateEmp = empMapper.updateEmp(emp);
//System.out.println(updateEmp);
long deleteEmpById = empMapper.deleteEmpById(2);
System.out.println(deleteEmpById);
//必須手動送出
openSession.commit();
}finally
{
openSession.close();
}
}
<insert id="addEmp" parameterType="bean.Emp" useGeneratedKeys="true" keyProperty="eid">
insert into emp (ename,email,gender) values(#{ename},#{email},#{gender})
</insert>
= openSession.getMapper(EmpMapper.class);
Emp emp=new Emp(null, "jane", "jane.com", "男");
empMapper.addEmp(emp);
System.out.println(emp.getEid());
<!--
擷取非自增的主鍵的值:
像Oracle是不支援自增的,Oracle使用的是序列來模拟自增的
每一次插入的資料的主鍵都是從序列中拿到的值,那麼我們如何擷取這個值呢?
mybatis支援使用屬性selectKey來擷取主鍵:
keyProperty:查出的主鍵值封裝給JavaBean的哪一個屬性
order:設定目前的selectKey的sql語句在插入sql語句的前後運作,有兩個參數
BEFORE:目前的selectKey的sql語句在插入sql語句的前運作
運作順序:
先運作selectKey查詢id的sql;查出id值封裝給javaBean的id屬性
在運作插入的sql;就可以取出id屬性對應的值
AFTER:目前的selectKey的sql語句在插入sql語句的後運作
運作順序:
先運作插入的sql(從序列中取出新值作為id);
再運作selectKey查詢id的sql;
resultType:查出的資料的傳回值類型
-->
<!-- BEFORE的編寫 -->
<insert id="addEmp" parameterType="bean.Emp" databaseId="oracle">
<selectKey keyProperty="eid" order="BEFORE" resultType="Integer">
select EMPLOYEES_SEQ.nextval from dual
</selectKey>
<!-- 插入時的主鍵時從序列中拿到的 -->
insert into emp (eid,ename,email,gender) values(#{eid},#{ename},#{email},#{gender})
</insert>
<!-- AFTER的編寫 -->
<insert id="addEmp" parameterType="bean.Emp" databaseId="oracle">
<selectKey keyProperty="eid" order="AFTER" resultType="Integer">
select EMPLOYEES_SEQ.currval from dual
</selectKey>
<!-- 插入時的主鍵時從序列中拿到的 -->
insert into emp (eid,ename,email,gender) values(employees_seq.nextval,#{ename},#{email},#{gender})
</insert>
映射檔案的參數問題
<!--
mybatis的參數問題:
單個參數:mybatis不會做特殊的處理
#{參數名/任意的名稱} :都可以取出參數的值
多個參數:mybatis會做特殊的處理
多個參數會被封裝成一個map對象,
key:名稱就是param1,param2...paramN,或者參數的索引也可以取出值
value:就是你傳入的參數的值
如果直接寫參數名,
select eid,ename,email,gender from emp where eid= #{eid} and ename=#{ename}
會報錯:
Cause: org.apache.ibatis.binding.BindingException:
Parameter 'eid' not found.
Available parameters are [0, 1, param1, param2]
我們可以将參數寫成0,1或者param1,param2
但是如果參數很多,就顯得不合規矩
是以我們可以命名參數:
明确指定封裝參數時map的key的值是什麼,
使用的是@Param注釋進行指明
key:就是使用@Param的值
value:參數值
例如:public Emp getEmpByIdAndName(@Param("eid")Integer eid,@Param("ename")String ename);
上面的命名參數,如果參數實在是不少,那麼寫起來還是挺麻煩的,下面還有三種方法
POJO:
如果多個參數正好是我們業務邏輯的資料模型,那麼我們就可以直接傳入pojo,就是JavaBean類
使用:#{屬性名}進行取出傳入的pojo的屬性值
Map:
如果多個參數不是業務邏輯的資料模型,沒有對應的pojo,不經常使用這個sql,為了友善
我們可以自己建構一個map傳進去
使用:#{key}就可以取出map對應的值
TO:
如果多個參數不是業務模型的資料,但是經常使用,推薦自己編寫一個TO
(Transfer Object)資料傳輸對象
例子:
public Emp getEmp(@Param("eid")Integer eid,String ename)
取值:eid==>#{eid}或#{param1}
ename==>#{param2}
public Emp getEmp(Integer eid,@Param("e")Emp emp)
取值:eid==>#{param1}
ename==>#{param2.ename}或者#{e.ename}
##注意的是:如果是Collection(List,Set)類型或者是數組
mybatis也會特殊處理,把傳入的list或者數組封裝在map中
key:Collection封裝成collection
如果是List可以使用list
數組封裝成array
public Emp getEmpById(List<Integer> eids)
取值:取出第一個id的值:#{list[0]}
結合源碼進行了解:
參數多時會封裝map,為了不混亂,我們可以使用@Param來指定封裝時使用的key
使用#{key}就可以取到map中的值
例子:
(@Param("eid")Integer eid,@Param("ename")String ename);
ParamNameResolver解析參數封裝map的;
1. 首先names:{0=eid,1=ename};這是在構造器的時候就建立好了
怎麼建立的呢:
1.擷取每一個标注了@Param注解的參數的@Param的值eid,ename,指派給names
2.每一次解析一個參數給map中儲存資訊
(key:參數索引,就是第幾個參數,value:name的值
name的值:标注了@Param注解的:就是注解的值
沒有标注的:
1.全局配置:isUseActualParamName (jdk1.8):name=參數名
2.name=map.size();就是目前元素的索引值
比如{0=eid,1=ename,2=2}//如果有第三個元素的話
2.後面就是對參數args的處理
假設現在args=[7,"jane"]
1.如果參數時null就直接傳回
2.如果隻有一個元素,并且沒有@Param注解,直接傳回args[0]
3.有多個元素或者有@Param注解
周遊names集合{0=eid,1=ename,2=2}
names集合的value作為key,names集合的key又作為取值的參數args[entry.getKey()
由上面的例子得到:
{eid=args[0],ename=args[1],2=args[2]}
結果是:{eid=7,ename="jane",2=2}
4.最後還額外将每一個參數儲存在map中,使用新的key:param1,param2...paramN
這樣就可以:有param注解的可以#{key}取值,或者#{param1}取值
public Object getNamedParams(Object[] args)
{
final int paramCount = names.size();
if (args == null || paramCount == 0)
{
return null;
}
else if (!hasParamAnnotation && paramCount == 1)
{
return args[names.firstKey()];
}
else
{
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet())
{
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName))
{
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
//names的源碼
private final SortedMap<Integer, String> names;
private boolean hasParamAnnotation;
public ParamNameResolver(Configuration config, Method method)
{
final Class<?>[] paramTypes = method.getParameterTypes();
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++)
{
if (isSpecialParameter(paramTypes[paramIndex]))
{
// skip special parameters
continue;
}
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex])
{
if (annotation instanceof Param)
{
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null)
{
// @Param was not specified.
if (config.isUseActualParamName())
{
name = getActualParamName(method, paramIndex);
}
if (name == null)
{
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
-->
package bean;
import org.apache.ibatis.annotations.Param;
public interface EmpMapper
{
public Emp getEmpByIdAndName(@Param("eid")Integer eid,@Param("ename")String ename);
public Emp getEmpByEmp(Emp emp);
public Emp getEmpById(Integer id);
public long addEmp(Emp emp);
public boolean updateEmp(Emp emp);
public long deleteEmpById(Integer eid);
}
參數值的擷取
<!--
參數值的擷取:
#{}:可以擷取map中的值或者pojo對象的屬性的值
${};效果和上面的一樣
差別是:
#{}是以預編譯的形式将參數設定到sql語句中的,使用的方法是PreparedStatement,可以防止sql注入
${}是将取出的值直接拼接在sql語句中的,會有安全問題
大多數情況,我們使用的是#{}
但是原生JDBC不支援占位符的地方,我們就可以使用${}進行取值
比如:分表,排序,模糊查詢....
按照年份查詢薪資
select * from ${year}_salary where xxxx;
select * from emp orde by ${f_name} ${order}
select * from emp where ename like '%${thechar}%'
-->
#{}的其他用法
<!--
#{}的其他用法:
規定參數的一些規則:
javaType、 jdbcType、 mode(存儲過程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未來準備支援的功能);
jdbcType通常在某種特定的條件下需要設定:
就是我們需要插入的資料的某個字段是null值的時候
有些資料庫可能不能識别mybatis對null值得預設處理,
比如Oracle就無法識别,插入的時候會報錯,JdbcType OTHER:無效的類型;
因為mybatis得全局配置中,對于所有的null值都是預設映射原生得jdbc得OTHER類型
Oracle無法識别這種類型
解決的方法有:
原因是全局配置的jdbcTypeForNull=OTHER,
1.#{email,jdbcType=OTHER}
2.将全局配置改變:
<setting name="jdbcTypeForNull" value="NULL"/>
-->