一,開發所依賴的jar文件
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
####二.接口式編程
Mapper配置文件中,的名稱空間指定接口的全類名;
package com.yangzlong.mybatis.DAO;
import com.yangzlong.mybatis.bean.Employee;
//接口
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
這樣就實現了接口和mapper文件的動態綁定;
測試:
測試結果:
org.apache.ibatis.binding.MapperProxy
結論:
==通過sqlSession對象和接口綁定通過調用getMapper方法獲取接口的實現類,是通過Mybatis實現的,mybatis通過反射得到一個代理對象org.apache.ibatis.binding.MapperProxy@5034c75a用代理對象進行增刪改查的操作;實現了接口式編程將DAO層分開,為后期開發和擴展做了很好的鋪墊,推介使用這種獲取mapper實現類的方式。好處可以解耦,類型檢查等==
-
sqlSession對象代表了和數據庫的一次會話交互,用完必須關閉;
-
sqlSession底層其實就是維護了一個connection對象,所以他兩一樣都是非線程安全的;每次使用都需要獲取新的對象,所以不能把它放在共享的成員變量中;(private SqlSession sqlSession這樣在多線程環境中會出錯)
-
mapper接口沒有實現類,但mybatis會為這個接口生成一個代理對象(通過xml和接口的綁定)
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Mybatis規定通過Sqlsession的getMapper方法得到接口類型的代理對象進行相應的增刪改查操作;
三,MyBatis全局配置文件properties引入外部文件
配置數據源通過外部文件引入,使用properties標簽,后期mybatis整合spring時數據源等信息陪 都交給spring來完成不做重點,了解記憶;
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
<configuration>
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<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>
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
####四,MyBatis全局配置文件settstings運行時行為設置(行為處理器)
這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為。
具體可參考官方文檔進行設置;
實例:
<!--開啟駝峰命名規范-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
五,MyBatis全局配置文件typeAliases別名處理器
類型別名是為 Java 類型設置一個短的名字。 它只和 XML 配置有關,存在的意義僅在於用來減少類完全限定名的冗余。
使用typeAliases標簽可以給Java類型起一個短別名;
<typeAliases>
<!-- <typeAlias type="com.yangzlong.mybatis.bean.Employee" alias="emp"/> -->
<package name="com.yangzlong.mybatis.bean"/>
</typeAliases>
這里介紹三種:
1,直接使用typeAlias標簽指定類型的全類名,默認別名為類名首字母小寫;
2,使用package標簽進行批量修改別名,只要是在該包下面的或者是子包全部掃描,默認類名首字母小寫;
3,使用注解@Alias("要起得別名")
PS:如果使用package標簽批量操作起別名,包下h或者子包出現相同的類型或者相同類名時使用注解區分;
但建議使用全類名;
####六,MyBatis全局配置文件typeHandlers類型處理器
無論Mybatis在預處理sql中設置參數,或者在結果集總取出一個值得時候都會用類型處理器來獲取值轉化整java類型
七,MyBatis全局配置文件plugins插件
MyBatis允許在已經運行的語句中一點進行攔截,plugins插件標簽就是進行攔截的;
主要對已下對象進行攔截:
Executor執行器對象
parameterHandler參數執行器對象
ResultSetHandler返回結果集對象
StatementHandler sql執行器對象
只需實現Intercaptor接口;
八,MyBatis全局配置文件environments環境配合器
MyBatis可以配置多個環境,例如開發環境,測試環境等;可以通過environments標簽來指定。
注意:雖然可以配置多個環境,但是每個環境只能有一個SqlSessionFactroy對象
environment標簽必須要有DataSource和transactionManage;
<environments default="development">
<environment id="development">
<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>
default:指當前環境為哪種環境;
id:當前的唯一標識,標識當前環境的唯一標識(名稱);
transactionManager:事務管理器;可以自定義事務管理器,只需要實現transactionManagerFactory接口;
type:當前事務的類型;JDBC(支持回滾,提交等操作依賴於數據源中得到的事務)|MANAGED(幾乎沒有什么配置,不會提交或者回滾數據,取決於J2ee的容器決定);
datasource:數據源配置;根據property屬性選擇器來指定屬性值;
type:數據源的類型;公有三種數據源類型:POOLED(使用連接池技術)
|UNPOOLED(不使用連接池技術)
|JNDI(使用jndi用於EJB服務)
同樣的也可以自定義數據源,實現DataSourceFactory接口,type就是全類名;
九,MyBatis全局配置文件,DataSourceIdProvider支持多數據庫
可以指定多個數據庫廠商,進行數據操作;通過DataSourceIdProvider來指定數據庫的連接,
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
可以指定多個數據庫標識;type=”DB_VENDOR“底層是根據驅動獲取數據庫廠商標識;
可以通過environment的id屬性指定對應的數據庫連接;
指定Mapper映射文件中sql映射databaseId指定相應的數據庫支持;
十,MyBatis全局配置文件,Mapper映射器
mapper映射器是將sql映射文件注冊到MyBatis的全局配置當中,然后MyBatis執行sql映射文件中的sql語句;
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
mapper:表示注冊一個sql映射文件;
配置文件注冊:
resource:類路徑下指定sql映射文件的位置(通常單獨定義個包,里面放sql映射文件)
url:引用網絡上或者是磁盤上的映射文件;
注冊接口:
class:使用class引用接口注冊,(注意:接口和sql映射文件必須在同一個目錄下,而且名字要相同)
基於注解:
MyBatis支持注解的方式,通過在接口是指明@select等增刪改查方法;
還可以使用package標簽批量注冊;name屬性為包名;
十一,MyBatis sql映射文件中insert_update獲取自增主鍵的值
想要獲取自增主鍵的值,如果數據庫中主鍵是自增的,要獲取主鍵值,可以使用useGeneratedKeys
默認值是false
注意:僅僅可以使用在insert和update有用;
<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee (last_name,gender,email)values(#{lastName},#{gender},#{email})
</insert>
useGeneratadKeys
默認值是false,開啟獲取自增主鍵的話應該設置為true;
keyProperty
指定主鍵映射封裝到實體的哪個屬性;
MyBatis底層是調用JDBC的getGenreatedkeys()方法來獲取自增主鍵值;
####十二,MyBatis 獲取非自增主鍵
oracle是不支持自增主鍵的,oracle是通過序列來模擬自增;
獲取非自增主鍵值:
<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
select tbl_employee nextval from dual
</selectKey>
insert into tbl_employee (id,last_name,gender,email)values(#{id},#{lastName},#{gender},#{email})
</insert>
使用selectKey來查詢主鍵;
keyPeoperty:封裝指定的對象屬性;
order:在進行插入語句之前進行主鍵查詢;
可以為after,值執行語句之后查詢主鍵;
resultType:查詢到結構的類型;
運行順序:在進行插入語句之前查詢非自增主鍵;
將查詢到的id值封裝給指定的屬性;
再去運行插入語句,將封裝好的id值取出來,進行插入語句的操作;
在進行插入語句之后獲取主鍵:
<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id" >
<selectKey keyProperty="id" order="AFTER">
select tbl_employee currval from dual
</selectKey>
insert into tbl_employee (id,last_name,gender,email)
values(employe_seq.nextval,#{lastName},#{gender},#{email})
</insert>
運行流程:
先進行插入,在序列中取到主鍵;
在進行selectKey主鍵的查詢,將主鍵取出來;
總結:在實際開發中,用的最多的是before在插入之前取主鍵,after的方式是記錄最后一次執行sql時的主鍵,所以建議使用before的方式;
十三,MyBatis映射文件參數處理,單個參數和多個參數的處理
單個參數:
public Employee getEmpById(Integer id);
單個參數MyBatis不會進行特殊的封裝,#{參數名},直接可以取出數據;
多個參數:
public Employee getEmpIdAndLastName(
當多個參數的時候,MyBatis會進行特殊的封裝,多個參數會被封裝成一個map,#{}就是從map中獲取指定key值,所對應的value;
key:Parma1····ParmaN;
value:就是傳入的參數的值;
解決方法:
因為參數會被MyBatis進行特殊封裝成一個map,會報綁定異常,底層封裝是通過map(key,value)
key:param1...paramN,value就是傳入參數的值,可以通過指定key來獲取value的值,也可以根據下標索引的方式來指定key所對應的值;
方式一:
<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{param1} and last_name=#{param2}
</select>
方式二:
<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{0} and last_name=#{1}
</select>
方式三:
命名參數:@param(" ")
public Employee getEmpIdAndLastName(
通過在接口方法中添加@param("指定的參數名"),來的指定參數的一一對應;也是最推介的一種方式;
十四,MyBatis映射文件 參數處理
同時也可以自定義,可以傳入map集合
map(key,value)
源碼分析:
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;
}
}
總結:參數封裝,參數多的時候MyBatis會將參數封裝成一個Map,為不混亂可以使用@Param注解來指定封裝時的key,#{key}就能取出map中的值;
十五,MyBatis映射文件 ${} #{}參數值得獲取
區別:#{}是以預編譯的形式將參數設置到sql語句中,防止了sql注入;
${}是將取出的值,直接拼接到sql語句中,會有安全問題;
在原生sql中如不需要動態獲取參數的情況下:例如,進行年份分庫分表之后需要傳入固定的表名的時候就應該使用${}進行取值拼接操作;
十六,MyBatis映射文件 select查詢語句返回List集合
public List<Employee> getListByLastNameLike(String lastName);
<select id="getListByLastNameLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
</select>
resultType:返回結果類型為返回元素的類型;並不是返回為List類型;
測試:
十七,MyBatis映射文件 select查詢封裝Map
對單條記錄的封裝,將查詢出的數據封裝成一個map
public Map<String,Object> getMapById(Integer id);
<select id="getMapById" resultType="map">
select * from tbl_employee where id=#{id}
</select>
封裝單條記錄為map集合,resultType:返回結果類型為map類型(MyBatis已經自動封裝了好類型)
測試:
{gender=1, id=2, last_Name=jreey, email=jreey@163.com}
多條記錄進行封裝:
@MapKey(“id”)根據id為列名進行查詢. Map集合中key表示為列名(記錄主鍵),value就表示為值(返回javaBean類型);
<select id="getMapByIdLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
</select>
多條記錄進行封裝成Map,映射文件中resultType:返回值類型為javabean類型;(我們要做的是封裝一個map集合,集合的value是一個JavaBean對象所以返回結果類型應該是javaBean的類型)
測試:
{2=Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com],
3=Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com],
4=Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com]}
十八,MyBatis映射文件 select查詢 resultMap自定義結果集映射規則
resultType:MyBatis會進行自動封裝的相應的指定的javaBean類型;根據我們對其的指定自動進行封裝;
resultMap:自定義封裝,在沒對應的JavaBean時自定義封裝類型,換而言之:如果查出的數據和javaBean的屬性沒有進行一一對應時就可以使用resultMap自定義映射規則;
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myemp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
</resultMap>
<select id="getEmp" resultMap="myemp">
select * from tbl_employee where id=#{id}
</select>
type:自定義結果集的類型;
id:自定義結果集映射規則的唯一ID;
column:數據庫表中的字段;
property:java實體中對應的屬性;
測試:
十九,MyBatis映射文件,select查詢,關聯查詢級聯屬性封裝結果
如果業務需求中存在對象關聯,可以使用ResultMap自定義封裝結果集,比如數據庫中存在多表的關聯,在resultMap中就可以使用級聯屬性的方式進行自定義結果集的封裝;
private Integer id;
private String lastName;
private String gender;
private String email;
private Department department;
在javaBean中存在對象關聯
SELECT e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id did,d.id id,d.dept_name deptName
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id
AND e.id=2
數據庫中進行多表關聯查詢
在Mapper映射文件中使用resultMap自定義封裝映射結果集來對屬性進行封裝
<resultMap type="com.yangzlong.mybatis.bean.Employee"
id="MyEmpAndDept">
<id column="id" property="id" />
<result column="last_name" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />
<result column="d_id" property="department.id" />
<result column="deptName" property="department.deptName" />
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>
測試方法:
二十,MyBatis映射文件,select查詢,resultMap自定義映射結果集,關聯查詢association定義關聯對象封裝規則
使用級聯屬性的方式可以自定義封裝規則還可以使用association
標簽來指定聯合查詢;
可以使用association來關聯單個對象
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpAndDept2">
<id column="id" property="id" />
<result column="lastName" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />
<association property="department" javaType="com.yangzlong.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="deptName" property="deptName"/>
</association>
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept2">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>
使用association來聯合查詢;property:指定關聯的對象屬性名,JavaType:指定該對象的全類名;
二十一,MyBatis映射文件,select查詢,resultMap自定義封裝 結果集,使用association進行分步查詢
上述介紹了自定義映射結果集中association的關聯查詢,association還可以進行分步查詢,好處是:不用寫復雜的多表關聯sql語句,可以組合已有的方法,使用多個簡單sql語句就可以實現需求,可讀性高思路清晰;
DepartmentMapper.xml:
<mapper namespace="com.yangzlong.mybatis.DAO.DepartmentMapper">
<select id="getIdByDept" resultType="com.yangzlong.mybatis.bean.Department">
select id,dept_name deptName from tbl_dept where id=#{id}
</select>
</mapper>
EmployeeMapper.xml:
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>
<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>
使用association進行分步查詢,property:employee中關聯對象屬性名稱,
select:指定通過那個查詢方法;
column:java實體中對象數據庫字段;
思路:通過select指定的方法(插入column指定的參數值)查出對象,並封裝給property指定的java屬性;
測試及結果:
Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com, dept=Department [id=1, deptName=開發部]]
二十二,MyBatis映射文件,select查詢 基於resultMap中 association分步查詢使用的延時加載(懶加載,或按需加載)
在mybatis進行分步查詢時,如果只需要得到某一個單獨的字段或者說一個對象時(因需求而定)使用了association的分步查詢,這樣的話就會進行兩次查詢,會訪問兩次數據庫,既耗費了資源又浪費了時間,這時候我們就可以使用mybatist提供的延時加載,又叫懶加載;
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>
<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>
之前的配置文件不需要修該,在我們mybatis的全局配置文件中添加兩個設置項就可以實現 懶加載:
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
lazyLoadingEnable:懶加載延遲加載開關,默認值是false,開啟時所有的關聯對象就會延遲加載;
aggressiveLazyLoading:默認值為false,禁止的時候我們需要的屬性會按需加載,開啟的話屬性會全部加載;
####二十三,MyBatis映射文件,select查詢_resultMap關聯查詢,collection定義關聯集合封裝規則
需求:查詢部門的時候,將部門對應的所有員工信息查詢出來; 之前的association是定義對象,現在我們要定義集合使用collection;
查部門的時候查詢部門所有的員工;
private Integer id;
private String deptName;
private List<Employee> emp;
每個不懵對應多個員工;一對多的關系;
接口方法:
public Department getDeptAndEmp(Integer id);
映射文件xml:
<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDeptMap">
<id column="did" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" ofType="com.yangzlong.mybatis.bean.Employee">
<id column="e_id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
<select id="getDeptAndEmp" resultMap="myDeptMap">
SELECT
d.id did,d.dept_name dept_name,e.id e_id,e.last_name last_name,e.email
email,e.gender gender
FROM
tbl_dept d
LEFT JOIN
tbl_employee e
ON
d.id=e.d_id
WHERE
d.id=#{id}
</select>
使用resultMap自定義結果集封裝,集合使用collection來封裝集合類型,
property:集合屬性值;
ofType:定義集合屬性的類型;
測試結果:
Department [id=2, deptName=銷售部, emp=[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]
二十四,MyBatis映射文件 select查詢_resultMap關聯查詢collection集合封裝分步查詢,延遲加載
之前使用association關聯查詢對象可以是使用分步查詢,在使用collection封裝集合的時候同樣也可使用分步查詢和使用延遲加載;
需求:根據部門Id查詢該部門下所有的員工信息,要求使用分步查詢;
查詢方法:根據部門ID查詢部門
public Department getDeptById(Integer deptId);
員工方法:根據部門ID查詢所有的員工
public List<Employee> getEmpsByDeptId(Integer deptId);
步驟:通過部門ID查詢部門信息,員工表中d_id為兩表的關聯外鍵,在員工表中查詢需傳入關聯到部門的外鍵也就是部門id,即員工表中的d_id字段;
DepartmentMapper:
<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDepts">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id">
</collection>
</resultMap>
<select id="getDeptById" resultMap="myDepts">
select * from tbl_dept where id=#{id}
</select>
同樣適用selcet屬性指定方法進行參數查詢(column參數指定數據庫字段進行參數查詢)然后封裝給property指定的屬性;
EmployeeMapper:
<select id="getEmpByIdStep" resultType="com.yangzlong.mybatis.bean.Employee" >
select * from tbl_employee where d_id=#{deptId}
</select>
測試結果:
Department [id=1, deptName=開發部, emp=[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]
二十五,MyBatis映射文件,select查詢_resultMap關聯查詢傳遞多列參數值&fetchType
不管使用的是association還是collection在這兩個標簽中都有column屬性,對應的是傳遞參數給select查詢方法並封裝給property對應的對象屬性或者集合屬性,同時,也可傳遞多個參數;
可以通過將多列參數封裝成map的形式來傳遞,如:column="{key1=column1,key2=column2}"封裝成map集合的形式;
同時可以使用fetchType=“lazy”的配置實施延遲加載;
該屬性可以取兩個值:lazy:開啟延遲加載配置;
eager:開啟立即加載;
<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id"
fetchType="lazy">
</collection>
<association property="department" javaType="com.yangzlong.mybatis.bean.Department" fetchType="lazy"> <id column="did" property="id"/> <result column="deptName" property="deptName"/> </association>
####26,MyBatis映射文件,select_resultMap_discriminator鑒別器
這是最后一個resultMap中的標簽,鑒別器driscriminator,主要是用來進行做鑒別處理的;指定一個屬性並對他進行操作,例:age屬性,當年齡大於18歲可以進行貸款申請,小於18歲不可以進行貸款申請;
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpDis"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <discriminator javaType="string" column="gender"> <case value="1" resultType="com.yangzlong.mybatis.bean.Employee"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="last_name" property="gender"/> <result column="email" property="email"/> </case> <case value="0" resultType="com.yangzlong.mybatis.bean.Department"> <association property="dept" select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id"> </association> </case> </discriminator> </resultMap>
使用discriminatory進行鑒別處理:javaType:為指定該字段的數據類型;column:java實體所對應的字段類型;
case:所對應的該屬性的鑒別規則,如上當性別為女時查詢部門,性別為男時使用姓名做email;
用的不是很多,作為了解知識;
27,Mybatis動態SQL,if判斷_OGNL表達式
動態SQL和JSTL語法很相似,都是基於xml中,但是Mybatis大大簡化了這一操作,只需了解之前一辦多的元素就可以,而且MyBatis使用了OGNL表達式來淘汰其他元素,大大簡化了操作;
if元素:通過if元素可以對sql語句進行篩選和可選,判斷其是否為空是否為null;
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee
where
<if test="id!=null">
id=#{id}
</if>
<if test="lastName!=null and lastName!=''">
and last_name like #{lastName}
</if>
<if test="email!=null and email.trim()!=''">
and email=#{email}
</if>
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</select>
在日常拼接sql語句時可以使用if元素對sql進行判定;如果有該字段就進行查找,沒有就不查找;
結果:
2019-06-26 04:15:59,196 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==> Preparing: select * from tbl_employee where id=? and last_name like ? 2019-06-26 04:15:59,274 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==> Parameters: 2(Integer), %e%(String) 2019-06-26 04:15:59,314 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - <== Total: 1 Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com]
28,MyBatis動態SQL,where查詢條件
在我們進行查詢的時候有些條件沒帶,可能在SQL拼裝的時候有問題;
如:我們要根據ID查數據,但是如果ID為null的時候SQL語句如下:
Preparing: select * from tbl_employee where and last_name like ?
XML如下:
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee where <if test="id!=null"> id=#{id} </if> <if test="lastName!=null and lastName!=''"> and last_name like #{lastName} </if> <if test="email!=null and email.trim()!=''"> and email=#{email} </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </select>
如果ID為null的時候,失去了語句拼接就會有錯誤,select * from tbl_employee ==where and== last_name like ? where 關鍵字 和and,所有解決辦法:
解決方案一:
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee where 1=1 <if test="id!=null"> id=#{id} </if> <if test="lastName!=null and lastName!=''"> and last_name like #{lastName} </if> <if test="email!=null and email.trim()!=''"> and email=#{email} </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </select>
通過給where后面添加一條1=1(條件為true)的方式,可以解決SQL拼接問題的發生;
解決方案二:
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee <where> <if test="id!=null"> id=#{id} </if> <if test="lastName!=null and lastName!=''"> and last_name like #{lastName} </if> <if test="email!=null and email.trim()!=''"> and email=#{email} </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </where> </select>
使用where標簽把所有條件查詢條件全部包裹住,都寫在where標簽中;但必須拼接條件的and必須寫在語句左邊;
29,MyBatis動態SQL,trim字符串截取
我們知道,在使用where查詢條件時,如果拼接SQL的語句關鍵字and或者or寫在了條件的右邊(后面),就會導致SQL語句錯誤,執行報錯,所以我們使用trim來進行字符串的截取;
只要將所有條件執行SQL寫在trim標簽內,就相當於就這些條件語句進行拼串操作;
<select id="getEmpTrim" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee <trim prefix="where" suffixOverrides="and"> <if test="id!=null"> id=#{id} and </if> <if test="lastName!=null and lastName!=''"> last_name like #{lastName} and </if> <if test="email!=null and email!=''"> email=#{email} and </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </trim> </select>
prefix:前綴,給整個trim標簽中的串加一個前綴;
suffix :后綴 給整個trim標簽包裹的串加一個后綴;
prefixOverrides:前綴覆蓋,覆蓋掉trim標簽中串前面的某個字符;
suffixOverrides:后綴覆蓋,覆蓋掉trim標簽中串后面的某個字符;
30,MyBatis動態SQL,choose分支選擇
choose分支就類似switch-case分支結構,when就相當於每個case;
<select id="getEmpsChoose" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee <where> <choose> <when test="id!=null"> id=#{id} </when> <when test="lastName!=null and lastName!=''"> last_name like #{lastName} </when> <when test="email!=null"> email=#{email} </when> <otherwise> gender=0 </otherwise> </choose> </where> </select>
==在choose里面包裹的就相當於帶了break的switch分支語句,每一個when就相當於case,當沒有對應的條件時才進otherwise;==
31,MyBatis動態SQL,set與if結合動態跟新
在進行update更新操作的時候可以配合if標簽一起使用,傳入哪個字段就跟新哪個字段,如果沒有傳入的就不進行更新操作;使用set標簽可以防止SQL拼接錯誤,在更新的時候會有多余逗號的干擾,可以使用set標簽來解決,同樣的也可以使用trim標簽截取多余的逗號;
//更新操作,傳入一個employee對象 public void updateEmp(Employee employee);
xml映射:
<update id="updateEmp"> update tbl_employee set <if test="lastName!=null"> last_name=#{lastName}, </if> <if test="email!=null"> email=#{email} </if> <if test="gender!=null"> gender=#{gender} </if> where id=#{id} </update>
出現SQL錯誤: Preparing: update tbl_employee set last_name=?, where id=?
拼接的SQL語句出現了錯誤,多了一個逗號,導致程序報錯;
解決方法一:
使用set標簽:將所有更新條件全部包裹在內看成一個整體;
<update id="updateEmp"> update tbl_employee <set> <if test="lastName!=null"> last_name=#{lastName}, </if> <if test="email!=null"> email=#{email} </if> <if test="gender!=null"> gender=#{gender} </if> </set> where id=#{id} </update>
==> Preparing: update tbl_employee SET last_name=? where id=?
這樣就不會出現SQL拼接的問題;
解決方法二:
使用trim標簽截取字符串:使用preffx設置前綴,使用suffixOverrides覆蓋語句的逗號;
<update id="updateEmp">
<!-- update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id} -->
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id}
</update>
==> Preparing: update tbl_employee set last_name=? where id=?
同樣的可以可以避免sql拼接問題;
ps:在執行更新操作的時候記得要進行commit提交操作;
32,MyBatis動態SQL,foreach遍歷集合
在開發中我們會遇到,一條SQL語句會查多條數據,比如where id in(?,?,?);類似於這樣的操作,這時候就要用到foreach標簽了;
通過多個ID查詢多條數據;
public List<Employee> getEmpsFoeach(@Param("ids")List<Integer> ids);
xml映射文件:
<select id="getEmpsFoeach" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee where id in <foreach collection="ids" item="item_ids" separator="," open="(" close=")"> #{item_ids} </foreach> </select>
通過foreach標簽來遍歷list集合:collection:遍歷的是那種集合,上述為list,如果是map就寫map;
item:將當前遍歷的值賦值給指定的變量;(接口方法傳進來的參數)
separator:每個元素之間的分隔符;
open遍歷出所有元素拼接一個開始的字符;
close:遍歷出所有元素拼接一個結束字符;
index:索引;
如果是list的時候就是指的索引;item就是每個元素的值;
如果是map的時候值得就是map中的key,item就是map中的value;
查詢結果:
==> Preparing: select * from tbl_employee where id in ( ? , ? )
33,MyBatis動態SQL,在mysql下foreach批量插入的兩種方法
在我們添加數據的時候也可以使用foreach進行批量插入數據到數據庫中;
第一種:
<insert id="addEmps">
insert into tbl_employee(last_name,email,gender,d_id)
values
<foreach collection="emps" separator="," item="emp" open="(" close=")">
#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>
第二種:
<insert id="addEmps">
<foreach collection="emps" separator=";" item="emp" >
insert into tbl_employee(last_name,email,gender,d_id)
values #{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>
直接使用封號隔開,發送多條SQl語句,但前提是必須打開mysql數據庫連接屬性,允許發送多條SQL;t同樣的方法可以進行批量刪除和批量修改,僅限於mysql;
jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true
34,MyBatis動態SQL,oracle下foreach批量操作的兩種方法
剛剛使用mysql數據庫,進行批量操作的方法,可以使用多條語句一起發送也可以使用類似VALUES(),(),()...的方法來進行批量操作,然而oracle不支持者中連續操作方式;
oracle不支持values(),(),(),()這種方式的操作;
我們可以是使用方式一:
使用begin end 來包裹插入語句;
begin
insert into employees (employee_id,last_name,email)values
(employee_seq.nextval,'test01','test01@162.com');
insert into employees (employee_id,last_name,email)values
(employee_seq.nextval,'test01','test01@162.com');
end
<insert id="addEmps">
`<foreach collection="emps" item="emp" open="begin" close="end;">
insert into employees (employee_id,last_name,email)values
(employee_seq.nextval,#{emp.lastName},#{emp.email});
</foreach>
</insert>
方式二:可以使用中間表來進行批量插入操作;
insert into employees(employee_id,last_name,email)
select employees_seq.nextval,lastName,email from(
select 'test01' lastName,'test01@e11.com' email from dual
union
select 'test02' lastName,'test02@e11.com' email from dual
)
使用中間表的形式;
<insert id="addEmps">
insert into employees(employee_id,last_name,email)
<foreach collection="emps" item="emp" open="select employees_seq.nextval,lastName,email from(" close=")" separator="union">
select #{emp.lastName} lastName,#{emp.email} email from dual
</foreach>
</insert>
35,MyBatis動態SQL,內置參數_parameter & _databaseId
MyBatis中內置了兩個參數,
_paramter:當傳進來是一個參數的時候parameter就代表這個參數,如果說傳進來是多個參數時,mybatis會進行參數封裝,將參數封裝成一個map集合,此時這個集合就是parameter;
_databaseId:如果配置databaseprvoder,這個參數就產生了,databaseId就代表者配置數據庫的別名;
例:
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>
根據不同數據庫_databaseId來分別執行不同sql語句到不同數據庫;
例:
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter !=null">
where last_name=#{lastName}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>
根據_parameter內置參數來判斷傳進來的參數,此時的parameter就相當於傳進來的employee,也可以寫寫成
#{_parameter.lastName}
;
36,MyBatis動態SQL,綁定_bind
bind綁定參數操作;我們要進行模糊查詢的時候,要求查詢%e%名字中帶有e的員工;
emp.setLastName("e");
不在代碼中直接寫“%e%”可以在mapper中直接通過bind來綁定到參數中;
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
<bind name="_lastname" value="'%'+lastName+'%'"/>
select * from employees
<if test="_parameter !=null">
where last_name like #{_lastname}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>
bind可以通過OGNL表達式的值來綁定到一個變量中,然后引用,<bind name="_lastname" value="'%'+lastName+'%'"/>
引用到#{_lastname};
37,MyBatis動態SQL,抽取可重用的SQL片段
可以將一些重復使用的字段全部提取出來
<sql id="selectClom"> <!--同樣的也可以進行判斷--> <if test="_databaseId=='mysql'"> last_name,gender,email,d_id </if> <if test="_databaseId=='oracle'"> employee_id,last_name,email </if> </sql>
引用:
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee"> <if test="_databaseId=='oracle'"> <bind name="_lastname" value="'%'+lastName+'%'"/> select <include refid="selectClom"/> from employees <if test="_parameter !=null"> where last_name like #{_lastname} </if> </if> <if test="_databaseId=='mysql'"> select <include refid="selectClom"/> from tbl_employee </if> </select>
38,MyBatis緩存,介紹
MyBatis有兩級緩存;
一級緩存:sqlsession,只要是同一個sqlsession對象就會自動啟動一級緩存機制;sqlsession本身死一個map如果執行操作,會存在這個map中,再次執行的時候會先從map中查詢,有就直接拿,沒有就查詢數據庫;
緩存失效:1,不同的sqlsession;
2,sqlsession相同,查詢條件不同;
3,sqlSession相同,在此之前執行了增刪改操作可能對該數據有影響;
4,sqlSession相同,手動清除了一級緩存(clearCahe);
二級緩存:全局緩存,基於namespace級別的緩存,一個namespace對應一個二級緩存;
工作機制:1,一個會話查詢一條數據,這個數據就會放到一級緩存中去,
2,如果會話關閉,數據會放到二級緩存中,新的會話查詢就會參照一級緩存;
3,不同namespac查出來的數據會放在不同的自己對應的緩存中;(map)
####39,MyBatis緩存,二級緩存的使用
開啟全局緩存;
MyBatis緩存<setting name="cacheEnable" value="true"/>
xml:
<!-- eviction:緩存回收策略; flushInterval:緩存刷新間隔的時間默認毫秒 readOnly:是否只讀; true:只讀,速度快,不安全; false:非只讀;都有可能被修改;速度慢,安全 size:存放緩存的個數; --> <cache eviction="FIFO" flushInterval="6000" readOnly="false" size="1024"> </cache>
####40,MyBatis緩存,緩存的有關設置和屬性
<setting name="cacheEnable" value="true"/>
*該設置是開啟二級緩存;一級緩存是一直可用的;
*每個select標簽都會有一個屬性usercahe=“ true”;默認的是開啟緩存,如果設置為false,一級緩存可以使用,二級緩存就會關閉;
*在使用一級緩存的時候,在同樣的sqlSession的時候只要執行一次增刪改操作,一級緩存就失效了,這是因為,在每個增刪改標簽上都有一個默認的屬性:flashcahe=“true”,他會自動執行完后刷新緩存;清除掉緩存;同樣的二級緩存也會被清除掉;在查詢標簽中flashcahe是false,如果改為true,每次查詢都會清除,緩存就沒有被使用到的;==增刪改后一級二級都會清除==;
*opensession.clearCache()方法只會清除當前session的一級緩存;和二級緩存是沒干系的;
*localcachescope:本地緩存作用域,SESSION|STATEMENT兩個值,session就是一級緩存默認值;statement的時候禁用一級緩存;
41,MyBatis的工作流程
MyBatis框架主要分為以下幾大層面;
==引導層==:基於xml配置;基於javaAPI;
==框架支持層==:主要有全局配置文件;以及配置方式:基於xml配置,基於注解配置;
一,開發所依賴的jar文件
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
####二.接口式編程
Mapper配置文件中,的名稱空間指定接口的全類名;
package com.yangzlong.mybatis.DAO;
import com.yangzlong.mybatis.bean.Employee;
//接口
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
這樣就實現了接口和mapper文件的動態綁定;
測試:
測試結果:
org.apache.ibatis.binding.MapperProxy
結論:
==通過sqlSession對象和接口綁定通過調用getMapper方法獲取接口的實現類,是通過Mybatis實現的,mybatis通過反射得到一個代理對象org.apache.ibatis.binding.MapperProxy@5034c75a用代理對象進行增刪改查的操作;實現了接口式編程將DAO層分開,為后期開發和擴展做了很好的鋪墊,推介使用這種獲取mapper實現類的方式。好處可以解耦,類型檢查等==
-
sqlSession對象代表了和數據庫的一次會話交互,用完必須關閉;
-
sqlSession底層其實就是維護了一個connection對象,所以他兩一樣都是非線程安全的;每次使用都需要獲取新的對象,所以不能把它放在共享的成員變量中;(private SqlSession sqlSession這樣在多線程環境中會出錯)
-
mapper接口沒有實現類,但mybatis會為這個接口生成一個代理對象(通過xml和接口的綁定)
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Mybatis規定通過Sqlsession的getMapper方法得到接口類型的代理對象進行相應的增刪改查操作;
三,MyBatis全局配置文件properties引入外部文件
配置數據源通過外部文件引入,使用properties標簽,后期mybatis整合spring時數據源等信息陪 都交給spring來完成不做重點,了解記憶;
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
<configuration>
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<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>
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
####四,MyBatis全局配置文件settstings運行時行為設置(行為處理器)
這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為。
具體可參考官方文檔進行設置;
實例:
<!--開啟駝峰命名規范-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
五,MyBatis全局配置文件typeAliases別名處理器
類型別名是為 Java 類型設置一個短的名字。 它只和 XML 配置有關,存在的意義僅在於用來減少類完全限定名的冗余。
使用typeAliases標簽可以給Java類型起一個短別名;
<typeAliases>
<!-- <typeAlias type="com.yangzlong.mybatis.bean.Employee" alias="emp"/> -->
<package name="com.yangzlong.mybatis.bean"/>
</typeAliases>
這里介紹三種:
1,直接使用typeAlias標簽指定類型的全類名,默認別名為類名首字母小寫;
2,使用package標簽進行批量修改別名,只要是在該包下面的或者是子包全部掃描,默認類名首字母小寫;
3,使用注解@Alias("要起得別名")
PS:如果使用package標簽批量操作起別名,包下h或者子包出現相同的類型或者相同類名時使用注解區分;
但建議使用全類名;
####六,MyBatis全局配置文件typeHandlers類型處理器
無論Mybatis在預處理sql中設置參數,或者在結果集總取出一個值得時候都會用類型處理器來獲取值轉化整java類型
七,MyBatis全局配置文件plugins插件
MyBatis允許在已經運行的語句中一點進行攔截,plugins插件標簽就是進行攔截的;
主要對已下對象進行攔截:
Executor執行器對象
parameterHandler參數執行器對象
ResultSetHandler返回結果集對象
StatementHandler sql執行器對象
只需實現Intercaptor接口;
八,MyBatis全局配置文件environments環境配合器
MyBatis可以配置多個環境,例如開發環境,測試環境等;可以通過environments標簽來指定。
注意:雖然可以配置多個環境,但是每個環境只能有一個SqlSessionFactroy對象
environment標簽必須要有DataSource和transactionManage;
<environments default="development">
<environment id="development">
<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>
default:指當前環境為哪種環境;
id:當前的唯一標識,標識當前環境的唯一標識(名稱);
transactionManager:事務管理器;可以自定義事務管理器,只需要實現transactionManagerFactory接口;
type:當前事務的類型;JDBC(支持回滾,提交等操作依賴於數據源中得到的事務)|MANAGED(幾乎沒有什么配置,不會提交或者回滾數據,取決於J2ee的容器決定);
datasource:數據源配置;根據property屬性選擇器來指定屬性值;
type:數據源的類型;公有三種數據源類型:POOLED(使用連接池技術)
|UNPOOLED(不使用連接池技術)
|JNDI(使用jndi用於EJB服務)
同樣的也可以自定義數據源,實現DataSourceFactory接口,type就是全類名;
九,MyBatis全局配置文件,DataSourceIdProvider支持多數據庫
可以指定多個數據庫廠商,進行數據操作;通過DataSourceIdProvider來指定數據庫的連接,
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
可以指定多個數據庫標識;type=”DB_VENDOR“底層是根據驅動獲取數據庫廠商標識;
可以通過environment的id屬性指定對應的數據庫連接;
指定Mapper映射文件中sql映射databaseId指定相應的數據庫支持;
十,MyBatis全局配置文件,Mapper映射器
mapper映射器是將sql映射文件注冊到MyBatis的全局配置當中,然后MyBatis執行sql映射文件中的sql語句;
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
mapper:表示注冊一個sql映射文件;
配置文件注冊:
resource:類路徑下指定sql映射文件的位置(通常單獨定義個包,里面放sql映射文件)
url:引用網絡上或者是磁盤上的映射文件;
注冊接口:
class:使用class引用接口注冊,(注意:接口和sql映射文件必須在同一個目錄下,而且名字要相同)
基於注解:
MyBatis支持注解的方式,通過在接口是指明@select等增刪改查方法;
還可以使用package標簽批量注冊;name屬性為包名;
十一,MyBatis sql映射文件中insert_update獲取自增主鍵的值
想要獲取自增主鍵的值,如果數據庫中主鍵是自增的,要獲取主鍵值,可以使用useGeneratedKeys
默認值是false
注意:僅僅可以使用在insert和update有用;
<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee (last_name,gender,email)values(#{lastName},#{gender},#{email})
</insert>
useGeneratadKeys
默認值是false,開啟獲取自增主鍵的話應該設置為true;
keyProperty
指定主鍵映射封裝到實體的哪個屬性;
MyBatis底層是調用JDBC的getGenreatedkeys()方法來獲取自增主鍵值;
####十二,MyBatis 獲取非自增主鍵
oracle是不支持自增主鍵的,oracle是通過序列來模擬自增;
獲取非自增主鍵值:
<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
select tbl_employee nextval from dual
</selectKey>
insert into tbl_employee (id,last_name,gender,email)values(#{id},#{lastName},#{gender},#{email})
</insert>
使用selectKey來查詢主鍵;
keyPeoperty:封裝指定的對象屬性;
order:在進行插入語句之前進行主鍵查詢;
可以為after,值執行語句之后查詢主鍵;
resultType:查詢到結構的類型;
運行順序:在進行插入語句之前查詢非自增主鍵;
將查詢到的id值封裝給指定的屬性;
再去運行插入語句,將封裝好的id值取出來,進行插入語句的操作;
在進行插入語句之后獲取主鍵:
<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id" >
<selectKey keyProperty="id" order="AFTER">
select tbl_employee currval from dual
</selectKey>
insert into tbl_employee (id,last_name,gender,email)
values(employe_seq.nextval,#{lastName},#{gender},#{email})
</insert>
運行流程:
先進行插入,在序列中取到主鍵;
在進行selectKey主鍵的查詢,將主鍵取出來;
總結:在實際開發中,用的最多的是before在插入之前取主鍵,after的方式是記錄最后一次執行sql時的主鍵,所以建議使用before的方式;
十三,MyBatis映射文件參數處理,單個參數和多個參數的處理
單個參數:
public Employee getEmpById(Integer id);
單個參數MyBatis不會進行特殊的封裝,#{參數名},直接可以取出數據;
多個參數:
public Employee getEmpIdAndLastName(
當多個參數的時候,MyBatis會進行特殊的封裝,多個參數會被封裝成一個map,#{}就是從map中獲取指定key值,所對應的value;
key:Parma1····ParmaN;
value:就是傳入的參數的值;
解決方法:
因為參數會被MyBatis進行特殊封裝成一個map,會報綁定異常,底層封裝是通過map(key,value)
key:param1...paramN,value就是傳入參數的值,可以通過指定key來獲取value的值,也可以根據下標索引的方式來指定key所對應的值;
方式一:
<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{param1} and last_name=#{param2}
</select>
方式二:
<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{0} and last_name=#{1}
</select>
方式三:
命名參數:@param(" ")
public Employee getEmpIdAndLastName(
通過在接口方法中添加@param("指定的參數名"),來的指定參數的一一對應;也是最推介的一種方式;
十四,MyBatis映射文件 參數處理
同時也可以自定義,可以傳入map集合
map(key,value)
源碼分析:
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;
}
}
總結:參數封裝,參數多的時候MyBatis會將參數封裝成一個Map,為不混亂可以使用@Param注解來指定封裝時的key,#{key}就能取出map中的值;
十五,MyBatis映射文件 ${} #{}參數值得獲取
區別:#{}是以預編譯的形式將參數設置到sql語句中,防止了sql注入;
${}是將取出的值,直接拼接到sql語句中,會有安全問題;
在原生sql中如不需要動態獲取參數的情況下:例如,進行年份分庫分表之后需要傳入固定的表名的時候就應該使用${}進行取值拼接操作;
十六,MyBatis映射文件 select查詢語句返回List集合
public List<Employee> getListByLastNameLike(String lastName);
<select id="getListByLastNameLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
</select>
resultType:返回結果類型為返回元素的類型;並不是返回為List類型;
測試:
十七,MyBatis映射文件 select查詢封裝Map
對單條記錄的封裝,將查詢出的數據封裝成一個map
public Map<String,Object> getMapById(Integer id);
<select id="getMapById" resultType="map">
select * from tbl_employee where id=#{id}
</select>
封裝單條記錄為map集合,resultType:返回結果類型為map類型(MyBatis已經自動封裝了好類型)
測試:
{gender=1, id=2, last_Name=jreey, email=jreey@163.com}
多條記錄進行封裝:
@MapKey(“id”)根據id為列名進行查詢. Map集合中key表示為列名(記錄主鍵),value就表示為值(返回javaBean類型);
<select id="getMapByIdLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
</select>
多條記錄進行封裝成Map,映射文件中resultType:返回值類型為javabean類型;(我們要做的是封裝一個map集合,集合的value是一個JavaBean對象所以返回結果類型應該是javaBean的類型)
測試:
{2=Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com],
3=Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com],
4=Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com]}
十八,MyBatis映射文件 select查詢 resultMap自定義結果集映射規則
resultType:MyBatis會進行自動封裝的相應的指定的javaBean類型;根據我們對其的指定自動進行封裝;
resultMap:自定義封裝,在沒對應的JavaBean時自定義封裝類型,換而言之:如果查出的數據和javaBean的屬性沒有進行一一對應時就可以使用resultMap自定義映射規則;
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myemp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
</resultMap>
<select id="getEmp" resultMap="myemp">
select * from tbl_employee where id=#{id}
</select>
type:自定義結果集的類型;
id:自定義結果集映射規則的唯一ID;
column:數據庫表中的字段;
property:java實體中對應的屬性;
測試:
十九,MyBatis映射文件,select查詢,關聯查詢級聯屬性封裝結果
如果業務需求中存在對象關聯,可以使用ResultMap自定義封裝結果集,比如數據庫中存在多表的關聯,在resultMap中就可以使用級聯屬性的方式進行自定義結果集的封裝;
private Integer id;
private String lastName;
private String gender;
private String email;
private Department department;
在javaBean中存在對象關聯
SELECT e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id did,d.id id,d.dept_name deptName
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id
AND e.id=2
數據庫中進行多表關聯查詢
在Mapper映射文件中使用resultMap自定義封裝映射結果集來對屬性進行封裝
<resultMap type="com.yangzlong.mybatis.bean.Employee"
id="MyEmpAndDept">
<id column="id" property="id" />
<result column="last_name" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />
<result column="d_id" property="department.id" />
<result column="deptName" property="department.deptName" />
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>
測試方法:
二十,MyBatis映射文件,select查詢,resultMap自定義映射結果集,關聯查詢association定義關聯對象封裝規則
使用級聯屬性的方式可以自定義封裝規則還可以使用association
標簽來指定聯合查詢;
可以使用association來關聯單個對象
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpAndDept2">
<id column="id" property="id" />
<result column="lastName" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />
<association property="department" javaType="com.yangzlong.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="deptName" property="deptName"/>
</association>
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept2">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>
使用association來聯合查詢;property:指定關聯的對象屬性名,JavaType:指定該對象的全類名;
二十一,MyBatis映射文件,select查詢,resultMap自定義封裝 結果集,使用association進行分步查詢
上述介紹了自定義映射結果集中association的關聯查詢,association還可以進行分步查詢,好處是:不用寫復雜的多表關聯sql語句,可以組合已有的方法,使用多個簡單sql語句就可以實現需求,可讀性高思路清晰;
DepartmentMapper.xml:
<mapper namespace="com.yangzlong.mybatis.DAO.DepartmentMapper">
<select id="getIdByDept" resultType="com.yangzlong.mybatis.bean.Department">
select id,dept_name deptName from tbl_dept where id=#{id}
</select>
</mapper>
EmployeeMapper.xml:
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>
<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>
使用association進行分步查詢,property:employee中關聯對象屬性名稱,
select:指定通過那個查詢方法;
column:java實體中對象數據庫字段;
思路:通過select指定的方法(插入column指定的參數值)查出對象,並封裝給property指定的java屬性;
測試及結果:
Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com, dept=Department [id=1, deptName=開發部]]
二十二,MyBatis映射文件,select查詢 基於resultMap中 association分步查詢使用的延時加載(懶加載,或按需加載)
在mybatis進行分步查詢時,如果只需要得到某一個單獨的字段或者說一個對象時(因需求而定)使用了association的分步查詢,這樣的話就會進行兩次查詢,會訪問兩次數據庫,既耗費了資源又浪費了時間,這時候我們就可以使用mybatist提供的延時加載,又叫懶加載;
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>
<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>
之前的配置文件不需要修該,在我們mybatis的全局配置文件中添加兩個設置項就可以實現 懶加載:
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
lazyLoadingEnable:懶加載延遲加載開關,默認值是false,開啟時所有的關聯對象就會延遲加載;
aggressiveLazyLoading:默認值為false,禁止的時候我們需要的屬性會按需加載,開啟的話屬性會全部加載;
####二十三,MyBatis映射文件,select查詢_resultMap關聯查詢,collection定義關聯集合封裝規則
需求:查詢部門的時候,將部門對應的所有員工信息查詢出來; 之前的association是定義對象,現在我們要定義集合使用collection;
查部門的時候查詢部門所有的員工;
private Integer id;
private String deptName;
private List<Employee> emp;
每個不懵對應多個員工;一對多的關系;
接口方法:
public Department getDeptAndEmp(Integer id);
映射文件xml:
<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDeptMap">
<id column="did" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" ofType="com.yangzlong.mybatis.bean.Employee">
<id column="e_id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
<select id="getDeptAndEmp" resultMap="myDeptMap">
SELECT
d.id did,d.dept_name dept_name,e.id e_id,e.last_name last_name,e.email
email,e.gender gender
FROM
tbl_dept d
LEFT JOIN
tbl_employee e
ON
d.id=e.d_id
WHERE
d.id=#{id}
</select>
使用resultMap自定義結果集封裝,集合使用collection來封裝集合類型,
property:集合屬性值;
ofType:定義集合屬性的類型;
測試結果:
Department [id=2, deptName=銷售部, emp=[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]
二十四,MyBatis映射文件 select查詢_resultMap關聯查詢collection集合封裝分步查詢,延遲加載
之前使用association關聯查詢對象可以是使用分步查詢,在使用collection封裝集合的時候同樣也可使用分步查詢和使用延遲加載;
需求:根據部門Id查詢該部門下所有的員工信息,要求使用分步查詢;
查詢方法:根據部門ID查詢部門
public Department getDeptById(Integer deptId);
員工方法:根據部門ID查詢所有的員工
public List<Employee> getEmpsByDeptId(Integer deptId);
步驟:通過部門ID查詢部門信息,員工表中d_id為兩表的關聯外鍵,在員工表中查詢需傳入關聯到部門的外鍵也就是部門id,即員工表中的d_id字段;
DepartmentMapper:
<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDepts">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id">
</collection>
</resultMap>
<select id="getDeptById" resultMap="myDepts">
select * from tbl_dept where id=#{id}
</select>
同樣適用selcet屬性指定方法進行參數查詢(column參數指定數據庫字段進行參數查詢)然后封裝給property指定的屬性;
EmployeeMapper:
<select id="getEmpByIdStep" resultType="com.yangzlong.mybatis.bean.Employee" >
select * from tbl_employee where d_id=#{deptId}
</select>
測試結果:
Department [id=1, deptName=開發部, emp=[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]
二十五,MyBatis映射文件,select查詢_resultMap關聯查詢傳遞多列參數值&fetchType
不管使用的是association還是collection在這兩個標簽中都有column屬性,對應的是傳遞參數給select查詢方法並封裝給property對應的對象屬性或者集合屬性,同時,也可傳遞多個參數;
可以通過將多列參數封裝成map的形式來傳遞,如:column="{key1=column1,key2=column2}"封裝成map集合的形式;
同時可以使用fetchType=“lazy”的配置實施延遲加載;
該屬性可以取兩個值:lazy:開啟延遲加載配置;
eager:開啟立即加載;
<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id"
fetchType="lazy">
</collection>
<association property="department" javaType="com.yangzlong.mybatis.bean.Department" fetchType="lazy"> <id column="did" property="id"/> <result column="deptName" property="deptName"/> </association>
####26,MyBatis映射文件,select_resultMap_discriminator鑒別器
這是最后一個resultMap中的標簽,鑒別器driscriminator,主要是用來進行做鑒別處理的;指定一個屬性並對他進行操作,例:age屬性,當年齡大於18歲可以進行貸款申請,小於18歲不可以進行貸款申請;
<resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpDis"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <discriminator javaType="string" column="gender"> <case value="1" resultType="com.yangzlong.mybatis.bean.Employee"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="last_name" property="gender"/> <result column="email" property="email"/> </case> <case value="0" resultType="com.yangzlong.mybatis.bean.Department"> <association property="dept" select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id"> </association> </case> </discriminator> </resultMap>
使用discriminatory進行鑒別處理:javaType:為指定該字段的數據類型;column:java實體所對應的字段類型;
case:所對應的該屬性的鑒別規則,如上當性別為女時查詢部門,性別為男時使用姓名做email;
用的不是很多,作為了解知識;
27,Mybatis動態SQL,if判斷_OGNL表達式
動態SQL和JSTL語法很相似,都是基於xml中,但是Mybatis大大簡化了這一操作,只需了解之前一辦多的元素就可以,而且MyBatis使用了OGNL表達式來淘汰其他元素,大大簡化了操作;
if元素:通過if元素可以對sql語句進行篩選和可選,判斷其是否為空是否為null;
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee
where
<if test="id!=null">
id=#{id}
</if>
<if test="lastName!=null and lastName!=''">
and last_name like #{lastName}
</if>
<if test="email!=null and email.trim()!=''">
and email=#{email}
</if>
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</select>
在日常拼接sql語句時可以使用if元素對sql進行判定;如果有該字段就進行查找,沒有就不查找;
結果:
2019-06-26 04:15:59,196 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==> Preparing: select * from tbl_employee where id=? and last_name like ? 2019-06-26 04:15:59,274 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==> Parameters: 2(Integer), %e%(String) 2019-06-26 04:15:59,314 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - <== Total: 1 Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com]
28,MyBatis動態SQL,where查詢條件
在我們進行查詢的時候有些條件沒帶,可能在SQL拼裝的時候有問題;
如:我們要根據ID查數據,但是如果ID為null的時候SQL語句如下:
Preparing: select * from tbl_employee where and last_name like ?
XML如下:
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee where <if test="id!=null"> id=#{id} </if> <if test="lastName!=null and lastName!=''"> and last_name like #{lastName} </if> <if test="email!=null and email.trim()!=''"> and email=#{email} </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </select>
如果ID為null的時候,失去了語句拼接就會有錯誤,select * from tbl_employee ==where and== last_name like ? where 關鍵字 和and,所有解決辦法:
解決方案一:
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee where 1=1 <if test="id!=null"> id=#{id} </if> <if test="lastName!=null and lastName!=''"> and last_name like #{lastName} </if> <if test="email!=null and email.trim()!=''"> and email=#{email} </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </select>
通過給where后面添加一條1=1(條件為true)的方式,可以解決SQL拼接問題的發生;
解決方案二:
<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee <where> <if test="id!=null"> id=#{id} </if> <if test="lastName!=null and lastName!=''"> and last_name like #{lastName} </if> <if test="email!=null and email.trim()!=''"> and email=#{email} </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </where> </select>
使用where標簽把所有條件查詢條件全部包裹住,都寫在where標簽中;但必須拼接條件的and必須寫在語句左邊;
29,MyBatis動態SQL,trim字符串截取
我們知道,在使用where查詢條件時,如果拼接SQL的語句關鍵字and或者or寫在了條件的右邊(后面),就會導致SQL語句錯誤,執行報錯,所以我們使用trim來進行字符串的截取;
只要將所有條件執行SQL寫在trim標簽內,就相當於就這些條件語句進行拼串操作;
<select id="getEmpTrim" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee <trim prefix="where" suffixOverrides="and"> <if test="id!=null"> id=#{id} and </if> <if test="lastName!=null and lastName!=''"> last_name like #{lastName} and </if> <if test="email!=null and email!=''"> email=#{email} and </if> <if test="gender==0 or gender==1"> gender=#{gender} </if> </trim> </select>
prefix:前綴,給整個trim標簽中的串加一個前綴;
suffix :后綴 給整個trim標簽包裹的串加一個后綴;
prefixOverrides:前綴覆蓋,覆蓋掉trim標簽中串前面的某個字符;
suffixOverrides:后綴覆蓋,覆蓋掉trim標簽中串后面的某個字符;
30,MyBatis動態SQL,choose分支選擇
choose分支就類似switch-case分支結構,when就相當於每個case;
<select id="getEmpsChoose" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee <where> <choose> <when test="id!=null"> id=#{id} </when> <when test="lastName!=null and lastName!=''"> last_name like #{lastName} </when> <when test="email!=null"> email=#{email} </when> <otherwise> gender=0 </otherwise> </choose> </where> </select>
==在choose里面包裹的就相當於帶了break的switch分支語句,每一個when就相當於case,當沒有對應的條件時才進otherwise;==
31,MyBatis動態SQL,set與if結合動態跟新
在進行update更新操作的時候可以配合if標簽一起使用,傳入哪個字段就跟新哪個字段,如果沒有傳入的就不進行更新操作;使用set標簽可以防止SQL拼接錯誤,在更新的時候會有多余逗號的干擾,可以使用set標簽來解決,同樣的也可以使用trim標簽截取多余的逗號;
//更新操作,傳入一個employee對象 public void updateEmp(Employee employee);
xml映射:
<update id="updateEmp"> update tbl_employee set <if test="lastName!=null"> last_name=#{lastName}, </if> <if test="email!=null"> email=#{email} </if> <if test="gender!=null"> gender=#{gender} </if> where id=#{id} </update>
出現SQL錯誤: Preparing: update tbl_employee set last_name=?, where id=?
拼接的SQL語句出現了錯誤,多了一個逗號,導致程序報錯;
解決方法一:
使用set標簽:將所有更新條件全部包裹在內看成一個整體;
<update id="updateEmp"> update tbl_employee <set> <if test="lastName!=null"> last_name=#{lastName}, </if> <if test="email!=null"> email=#{email} </if> <if test="gender!=null"> gender=#{gender} </if> </set> where id=#{id} </update>
==> Preparing: update tbl_employee SET last_name=? where id=?
這樣就不會出現SQL拼接的問題;
解決方法二:
使用trim標簽截取字符串:使用preffx設置前綴,使用suffixOverrides覆蓋語句的逗號;
<update id="updateEmp">
<!-- update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id} -->
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id}
</update>
==> Preparing: update tbl_employee set last_name=? where id=?
同樣的可以可以避免sql拼接問題;
ps:在執行更新操作的時候記得要進行commit提交操作;
32,MyBatis動態SQL,foreach遍歷集合
在開發中我們會遇到,一條SQL語句會查多條數據,比如where id in(?,?,?);類似於這樣的操作,這時候就要用到foreach標簽了;
通過多個ID查詢多條數據;
public List<Employee> getEmpsFoeach(@Param("ids")List<Integer> ids);
xml映射文件:
<select id="getEmpsFoeach" resultType="com.yangzlong.mybatis.bean.Employee"> select * from tbl_employee where id in <foreach collection="ids" item="item_ids" separator="," open="(" close=")"> #{item_ids} </foreach> </select>
通過foreach標簽來遍歷list集合:collection:遍歷的是那種集合,上述為list,如果是map就寫map;
item:將當前遍歷的值賦值給指定的變量;(接口方法傳進來的參數)
separator:每個元素之間的分隔符;
open遍歷出所有元素拼接一個開始的字符;
close:遍歷出所有元素拼接一個結束字符;
index:索引;
如果是list的時候就是指的索引;item就是每個元素的值;
如果是map的時候值得就是map中的key,item就是map中的value;
查詢結果:
==> Preparing: select * from tbl_employee where id in ( ? , ? )
33,MyBatis動態SQL,在mysql下foreach批量插入的兩種方法
在我們添加數據的時候也可以使用foreach進行批量插入數據到數據庫中;
第一種:
<insert id="addEmps">
insert into tbl_employee(last_name,email,gender,d_id)
values
<foreach collection="emps" separator="," item="emp" open="(" close=")">
#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>
第二種:
<insert id="addEmps">
<foreach collection="emps" separator=";" item="emp" >
insert into tbl_employee(last_name,email,gender,d_id)
values #{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>
直接使用封號隔開,發送多條SQl語句,但前提是必須打開mysql數據庫連接屬性,允許發送多條SQL;t同樣的方法可以進行批量刪除和批量修改,僅限於mysql;
jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true
34,MyBatis動態SQL,oracle下foreach批量操作的兩種方法
剛剛使用mysql數據庫,進行批量操作的方法,可以使用多條語句一起發送也可以使用類似VALUES(),(),()...的方法來進行批量操作,然而oracle不支持者中連續操作方式;
oracle不支持values(),(),(),()這種方式的操作;
我們可以是使用方式一:
使用begin end 來包裹插入語句;
begin
insert into employees (employee_id,last_name,email)values
(employee_seq.nextval,'test01','test01@162.com');
insert into employees (employee_id,last_name,email)values
(employee_seq.nextval,'test01','test01@162.com');
end
<insert id="addEmps">
`<foreach collection="emps" item="emp" open="begin" close="end;">
insert into employees (employee_id,last_name,email)values
(employee_seq.nextval,#{emp.lastName},#{emp.email});
</foreach>
</insert>
方式二:可以使用中間表來進行批量插入操作;
insert into employees(employee_id,last_name,email)
select employees_seq.nextval,lastName,email from(
select 'test01' lastName,'test01@e11.com' email from dual
union
select 'test02' lastName,'test02@e11.com' email from dual
)
使用中間表的形式;
<insert id="addEmps">
insert into employees(employee_id,last_name,email)
<foreach collection="emps" item="emp" open="select employees_seq.nextval,lastName,email from(" close=")" separator="union">
select #{emp.lastName} lastName,#{emp.email} email from dual
</foreach>
</insert>
35,MyBatis動態SQL,內置參數_parameter & _databaseId
MyBatis中內置了兩個參數,
_paramter:當傳進來是一個參數的時候parameter就代表這個參數,如果說傳進來是多個參數時,mybatis會進行參數封裝,將參數封裝成一個map集合,此時這個集合就是parameter;
_databaseId:如果配置databaseprvoder,這個參數就產生了,databaseId就代表者配置數據庫的別名;
例:
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>
根據不同數據庫_databaseId來分別執行不同sql語句到不同數據庫;
例:
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter !=null">
where last_name=#{lastName}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>
根據_parameter內置參數來判斷傳進來的參數,此時的parameter就相當於傳進來的employee,也可以寫寫成
#{_parameter.lastName}
;
36,MyBatis動態SQL,綁定_bind
bind綁定參數操作;我們要進行模糊查詢的時候,要求查詢%e%名字中帶有e的員工;
emp.setLastName("e");
不在代碼中直接寫“%e%”可以在mapper中直接通過bind來綁定到參數中;
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
<bind name="_lastname" value="'%'+lastName+'%'"/>
select * from employees
<if test="_parameter !=null">
where last_name like #{_lastname}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>
bind可以通過OGNL表達式的值來綁定到一個變量中,然后引用,<bind name="_lastname" value="'%'+lastName+'%'"/>
引用到#{_lastname};
37,MyBatis動態SQL,抽取可重用的SQL片段
可以將一些重復使用的字段全部提取出來
<sql id="selectClom"> <!--同樣的也可以進行判斷--> <if test="_databaseId=='mysql'"> last_name,gender,email,d_id </if> <if test="_databaseId=='oracle'"> employee_id,last_name,email </if> </sql>
引用:
<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee"> <if test="_databaseId=='oracle'"> <bind name="_lastname" value="'%'+lastName+'%'"/> select <include refid="selectClom"/> from employees <if test="_parameter !=null"> where last_name like #{_lastname} </if> </if> <if test="_databaseId=='mysql'"> select <include refid="selectClom"/> from tbl_employee </if> </select>
38,MyBatis緩存,介紹
MyBatis有兩級緩存;
一級緩存:sqlsession,只要是同一個sqlsession對象就會自動啟動一級緩存機制;sqlsession本身死一個map如果執行操作,會存在這個map中,再次執行的時候會先從map中查詢,有就直接拿,沒有就查詢數據庫;
緩存失效:1,不同的sqlsession;
2,sqlsession相同,查詢條件不同;
3,sqlSession相同,在此之前執行了增刪改操作可能對該數據有影響;
4,sqlSession相同,手動清除了一級緩存(clearCahe);
二級緩存:全局緩存,基於namespace級別的緩存,一個namespace對應一個二級緩存;
工作機制:1,一個會話查詢一條數據,這個數據就會放到一級緩存中去,
2,如果會話關閉,數據會放到二級緩存中,新的會話查詢就會參照一級緩存;
3,不同namespac查出來的數據會放在不同的自己對應的緩存中;(map)
####39,MyBatis緩存,二級緩存的使用
開啟全局緩存;
MyBatis緩存<setting name="cacheEnable" value="true"/>
xml:
<!-- eviction:緩存回收策略; flushInterval:緩存刷新間隔的時間默認毫秒 readOnly:是否只讀; true:只讀,速度快,不安全; false:非只讀;都有可能被修改;速度慢,安全 size:存放緩存的個數; --> <cache eviction="FIFO" flushInterval="6000" readOnly="false" size="1024"> </cache>
####40,MyBatis緩存,緩存的有關設置和屬性
<setting name="cacheEnable" value="true"/>
*該設置是開啟二級緩存;一級緩存是一直可用的;
*每個select標簽都會有一個屬性usercahe=“ true”;默認的是開啟緩存,如果設置為false,一級緩存可以使用,二級緩存就會關閉;
*在使用一級緩存的時候,在同樣的sqlSession的時候只要執行一次增刪改操作,一級緩存就失效了,這是因為,在每個增刪改標簽上都有一個默認的屬性:flashcahe=“true”,他會自動執行完后刷新緩存;清除掉緩存;同樣的二級緩存也會被清除掉;在查詢標簽中flashcahe是false,如果改為true,每次查詢都會清除,緩存就沒有被使用到的;==增刪改后一級二級都會清除==;
*opensession.clearCache()方法只會清除當前session的一級緩存;和二級緩存是沒干系的;
*localcachescope:本地緩存作用域,SESSION|STATEMENT兩個值,session就是一級緩存默認值;statement的時候禁用一級緩存;
41,MyBatis的工作流程
MyBatis框架主要分為以下幾大層面;
==引導層==:基於xml配置;基於javaAPI;
==框架支持層==:主要有全局配置文件;以及配置方式:基於xml配置,基於注解配置;
==數據處理層==:參數映射;參數解析;SQL執行;結果處理和映射;
==接口層==:接口的處理方式:基於statementID;基於mapper接口
數據增加接口;數據刪除接口;數據修改接口;數據查詢接口;數據維護接口;
==數據處理層==:參數映射;參數解析;SQL執行;結果處理和映射;
==接口層==:接口的處理方式:基於statementID;基於mapper接口
數據增加接口;數據刪除接口;數據修改接口;數據查詢接口;數據維護接口;