mybatis学习笔记
1. HelloWorld
導(dǎo)入mybatis
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version> </dependency>1.1 配置文件法
從XML中構(gòu)建SqlSessionFactory
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><mapper resource="mapper/employee-mapper.xml"/></mappers> </configuration>mapper
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace: 名稱空間id: 唯一標(biāo)識(shí)returnType: 返回值類型#{id} 從傳遞過來的參數(shù)中取出id值 --> <mapper namespace="com.meituan.mybatis.bean.EmployeeMapper"><select id="selectEmp" resultType="com.meituan.mybatis.bean.Employee">select * from employee where id = #{id}</select> </mapper>JavaBean
public class Employee {private Integer id;private String lastName;private String email;private String gender;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}@Overridepublic String toString() {return "Employee{" +"id=" + id +", lastName='" + lastName + '\'' +", email='" + email + '\'' +", gender='" + gender + '\'' +'}';} }單元測試
/*** 1. 根據(jù)xml配置文件(全局配置文件)創(chuàng)建一個(gè)SqlSessionFactory對象* 有數(shù)據(jù)源一些運(yùn)行環(huán)境信息 * 2. sql映射文件,配置了每一個(gè)sql,以及sql的封裝規(guī)則等* 3. 將sql映射文件注冊在全局配置文件中* 4. 寫代碼* 1) 根據(jù)全局配置文件得到SqlSessionFactory* 2) 通過SqlSession工廠獲取到SqlSession,使用SqlSession執(zhí)行增刪改查,一個(gè)SqlSession就是代表和數(shù)據(jù)庫的一次會(huì)話,用完關(guān)閉* 3) 使用sql的唯一標(biāo)識(shí)(id)來告訴mybatis執(zhí)行哪個(gè)sql,sql全部保存在sql映射文件(mapper)中* @throws Exception*/@Testpublic void test() throws Exception{String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//2. 獲取SqlSession實(shí)例,能直接執(zhí)行已經(jīng)映射的sql語句SqlSession sqlSession = sqlSessionFactory.openSession();/*** Retrieve a single row mapped from the statement key and parameter.* @param <T> the returned object type* @param statement Unique identifier matching the statement to use. 傳入唯一標(biāo)識(shí)* @param parameter A parameter object to pass to the statement. 傳入?yún)?shù)* @return Mapped object*/Employee employee = sqlSession.selectOne("com.meituan.mybatis.bean.EmployeeMapper.selectEmp", 1);System.out.println(employee);}駝峰命名法問題:
mybatis-config.xml配置文件配置時(shí),要注意節(jié)點(diǎn)順序
<properties>...</properties> <settings>...</settings> <typeAliases>...</typeAliases> <typeHandlers>...</typeHandlers> <objectFactory>...</objectFactory> <objectWrapperFactory>...</objectWrapperFactory> <plugins>...</plugins> <environments>...</environments> <databaseIdProvider>...</databaseIdProvider> <mappers>...</mappers>增加設(shè)置
支持駝峰命名法 <setting name="mapUnderscoreToCamelCase" value="true"/>1.2 接口式編程
配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace: 名稱空間;接口式編程中,須指定為接口的全類名id: 唯一標(biāo)識(shí)returnType: 返回值類型#{id} 從傳遞過來的參數(shù)中取出id值;接口式編程中,id改為方法名 --> <mapper namespace="com.meituan.mybatis.dao.EmployeeMapper"><select id="getEmpById" resultType="com.meituan.mybatis.bean.Employee">select * from employee where id = #{id}</select> </mapper>mapper接口
@Mapper public interface EmployeeMapper {public Employee getEmpById(Integer id); }單元測試
@Testpublic void test01() throws Exception {//1. 獲取SqlSessionFactory對象SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();//2. 獲取SqlSession對象SqlSession sqlSession = sqlSessionFactory.openSession();try {//3. 獲取接口的實(shí)現(xiàn)類對象// mybatis會(huì)為接口自動(dòng)創(chuàng)建一個(gè)代理對象,代理對象去執(zhí)行增刪改查方法EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee = mapper.getEmpById(1);System.out.println(employee);} finally {sqlSession.close();}}private SqlSessionFactory getSqlSessionFactory() throws Exception{String resources = "mybatis-config.xml";InputStream is = Resources.getResourceAsStream(resources);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);return sqlSessionFactory;}1.3 總結(jié)
原生: Dao ====> DaoImpl
mybatis: Mapper ==== > xxMapper.xml
全局配置文件:mybatis-config.xml 包含數(shù)據(jù)庫連接池信息,事務(wù)管理器信息等系統(tǒng)運(yùn)行環(huán)境
sql映射文件:保存了每一個(gè)sql語句的映射信息
2. Mybatis全局配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><!--1. mybatis可以使用properties來引入外部properties配置文件的內(nèi)容屬性:resource:引入類路徑下的資源url:引入網(wǎng)絡(luò)路徑或者磁盤路徑下的資源--><properties resource="dbconfig.properties"></properties><!--2. 這是 MyBatis 中極為重要的調(diào)整設(shè)置,它們會(huì)改變 MyBatis 的運(yùn)行時(shí)行為。setting:用來設(shè)置每一個(gè)設(shè)置項(xiàng)name:設(shè)置項(xiàng)名value:設(shè)置項(xiàng)取值--><settings><setting name="mapUnderscoreToCamelCase" value="true"/></settings><!--3. typeAliases 類型別名是為 Java 類型設(shè)置一個(gè)短的名字。它只和 XML 配置有關(guān),存在的意義僅在于用來減少類完全限定名的冗余。別名不區(qū)分大小寫typeAlias:為某個(gè)java類型起別名type:指定要起別名的類型全類名;默認(rèn)別名就是類名小寫:employeealias:指定新的別名<package> :為某個(gè)包下的所有類批量起別名每一個(gè)在包 domain.blog 中的 Java Bean,在沒有注解的情況下,會(huì)使用 Bean 的首字母小寫的非限定類名來作為它的別名。 比如 domain.blog.Author 的別名為 author;若有注解,則別名為其注解值。name:指定包名(為當(dāng)前包以及下面所有的后代包的每一類都起一個(gè)包名)如果子包下有相同的類名,mybatis會(huì)起相同的包名從而報(bào)錯(cuò)??梢允褂?#64;Alias給特定類指定別名--><typeAliases><!--<typeAlias type="com.meituan.mybatis.config.bean.Employee" alias="emp"></typeAlias>--><package name="com.meituan.mybatis.config"/></typeAliases><!--4. typeHandler 無論是 MyBatis 在預(yù)處理語句(PreparedStatement)中設(shè)置一個(gè)參數(shù)時(shí),還是從結(jié)果集中取出一個(gè)值時(shí), 都會(huì)用類型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類型。--><!--5. plugins 插件--><!--6. environment MyBatis 可以配置成適應(yīng)多種環(huán)境,這種機(jī)制有助于將 SQL 映射應(yīng)用于多種數(shù)據(jù)庫之中, 現(xiàn)實(shí)情況下有多種理由需要這么做。default指定使用某種環(huán)境來切換環(huán)境,可以達(dá)到快速切換環(huán)境。environment配置一個(gè)具體的環(huán)境信息,id代表當(dāng)前環(huán)境的唯一標(biāo)識(shí),必須有兩個(gè)標(biāo)簽:transactionManager:事務(wù)管理器,type指定事務(wù)管理器的類型 在 MyBatis 中有兩種類型的事務(wù)管理器(也就是 type=”[JDBC|MANAGED]”):JDBC:typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);MANAGED:typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);支持自定義事務(wù)管理器,實(shí)現(xiàn)TransactionFactory接口即可dataSource:數(shù)據(jù)源type:數(shù)據(jù)源類型,有三種內(nèi)建的數(shù)據(jù)源類型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):支持自定義數(shù)據(jù)源,實(shí)現(xiàn)DataSourceFactory接口,type為自定義數(shù)據(jù)源的全類名--><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><!--7. databaseIdProvider: 支持多數(shù)據(jù)庫廠商--><!--8. mappers映射器mapper注冊一個(gè)sql映射resource:引用類路徑下的sql映射文件url:引用網(wǎng)絡(luò)路徑下或者磁盤路徑下的映射文件class:使用映射器接口實(shí)現(xiàn)類的完全限定類名1. 有sql映射文件,映射文件名必須和接口同名,并且放在與接口同一目錄下2. 沒有sql映射文件,所有的sql都是利用注解寫在接口上package:將包內(nèi)的映射器接口實(shí)現(xiàn)全部注冊為映射器--><mappers><mapper resource="mapper/employee-mapper.xml"/></mappers><!--標(biāo)簽的編寫是有順序的--> </configuration>3. Mybatis映射文件
MyBatis 的真正強(qiáng)大在于它的映射語句,也是它的魔力所在。由于它的異常強(qiáng)大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的 JDBC 代碼進(jìn)行對比,你會(huì)立即發(fā)現(xiàn)省掉了將近 95% 的代碼。MyBatis 就是針對 SQL 構(gòu)建的,并且比普通的方法做的更好。
3.1 獲取自增主鍵
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace: 名稱空間id: 唯一標(biāo)識(shí)returnType: 返回值類型#{id} 從傳遞過來的參數(shù)中取出id值 --> <mapper namespace="com.meituan.mybatis.mapper.dao.EmployeeMapper"><select id="getEmpById" resultType="com.meituan.mybatis.mapper.bean.Employee">SELECT *FROM employeeWHERE id = #{id}</select><!--parameterType:可以省略mysql支持自增主鍵,自增主鍵的獲取,mybatis也是利用statement.getGeneratedKeys(),useGeneratedKeys="true":使用自增主鍵獲取主鍵值策略keyProperty:指定對應(yīng)的主鍵屬性,也就是mybatis獲取到主鍵值以后,將這個(gè)值封裝給JavaBean的哪個(gè)屬性--><insert id="addEmp" parameterType="com.meituan.mybatis.mapper.bean.Employee"useGeneratedKeys="true" keyProperty="id">INSERT INTO employee (last_name, email, gender)VALUES (#{lastName}, #{email}, #{gender})</insert><update id="updateEmp">UPDATE employeeSET last_name = #{lastName}, email = #{email}, gender = #{gender}WHERE id = #{id}</update><delete id="deleteEmpById">DELETE FROM employee WHERE id=#{id}</delete> </mapper>單元測試
/*** 1. mybatis允許增刪改直接定義以下類型返回值* Integer Long Boolean* 2. 手動(dòng)提交數(shù)據(jù)* @throws Exception*/@Testpublic void test02() throws Exception {SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();//1. 獲取到的SqlSession不會(huì)自動(dòng)提交SqlSession sqlSession = sqlSessionFactory.openSession();try {EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Employee employee = new Employee(null, "jerry", "jerry@tom.com", "2");System.out.println(employee);System.out.println("============");mapper.addEmp(employee);System.out.println(employee);// employee.setLastName("jason"); // employee.setId(3); // mapper.updateEmp(employee); // mapper.deleteEmpById(3);sqlSession.commit();} finally {sqlSession.close();}}private SqlSessionFactory getSqlSessionFactory() throws Exception{String resources = "mybatis-config.xml";InputStream is = Resources.getResourceAsStream(resources);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);return sqlSessionFactory;}3.2 參數(shù)處理
1)單個(gè)參數(shù):mybatis不會(huì)做特殊處理
2)多個(gè)參數(shù)
? 異常:
org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2] ### Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]? 操作:
? 方法:public Employee getEmpByIdAndLastName(Integer id ,String lastName);
? 取值:#{id},#{lastName}
? mybatis會(huì)特殊處理,多個(gè)參數(shù)會(huì)被封裝成一個(gè)map
? key:param1....paramN
? value:傳入的參數(shù)值
? #{}就是從map中獲取指定的key值,或者參數(shù)的索引也可以
命名參數(shù):
? 明確指定封裝參數(shù)值map的key: @Param("id")
POJO:
? 如果多個(gè)參數(shù)正好是業(yè)務(wù)邏輯的數(shù)據(jù)模型,可以直接傳入POJO:
? #{屬性名}:取出傳入的pojo的屬性值
Map:
如果多個(gè)參數(shù)不是業(yè)務(wù)模型中的數(shù)據(jù),沒有對應(yīng)的pojo,不經(jīng)常使用,為了方便,也可以傳入map
如果多個(gè)參數(shù)不是業(yè)務(wù)模型中的數(shù)據(jù),但是經(jīng)常使用,推薦寫一個(gè)TO(Transfer Object) 數(shù)據(jù)傳輸對象
3)參數(shù)封裝擴(kuò)展思考:
取值:id==》#{id/param1} lastName===>#{param2}
取值:id===》#{param1} lastName===》#{param2.LastName/e.lastName}
也會(huì)特殊處理,也是把傳入的list或者數(shù)組封裝在map中
? key:Collection(collection),如果是List還可以使用(list)
? 數(shù)組(array)
public Employee getEmpById(List<Integer> ids);
取值:取出第一個(gè)id的值:#{list[0]}
3.3 結(jié)合源碼,mybatis如何處理參數(shù)
ParamNameResolver解析參數(shù)封裝map
(1) names:(0=id, 1=lastName)
? 1) 獲取每個(gè)標(biāo)注Param注解的參數(shù)param值:id,lastName,賦值給name
? 2)每次解析一個(gè)參數(shù)給map中保存信息:key是索引值, value是name的值
? name的值:
? 標(biāo)注了param注解,注解的值
? 沒有標(biāo)注:
? 1、全局配置:useActualParamName,name=參數(shù)名(要求JDK1.8)
? 2、name=map.size() 相當(dāng)于當(dāng)前元素的索引
? names:{0=id, 1=lastName}
public Object getNamedParams(Object[] args) {final int paramCount = names.size();//1. 參數(shù)為null直接返回if (args == null || paramCount == 0) {return null;//2. 如果只有一個(gè)元素并且沒有param注解:args[0],單個(gè)參數(shù)直接返回} else if (!hasParamAnnotation && paramCount == 1) {return args[names.firstKey()];//3. 多個(gè)元素或者有Param標(biāo)注} else {final Map<String, Object> param = new ParamMap<Object>();int i = 0;// 4. 遍歷names,構(gòu)造器的時(shí)候就已經(jīng)確定for (Map.Entry<Integer, String> entry : names.entrySet()) {//names的value作為新map的key,nameskey作為取值的參考//eg:{id=args[0], lastName=args[1]},因此可以在映射文件中取到相應(yīng)的值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 @Paramif (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}}3.4 參數(shù)值的獲取
#{}:可以獲取map中的值或者pojo對象屬性的值
${}:可以獲取map中的值或者pojo對象屬性的值
區(qū)別:#{}是以預(yù)編譯的形式,將參數(shù)設(shè)置到sql語句中,PreparedStatement
? ${}:取出的值直接拼裝在sql語句中,會(huì)有安全問題
? 大多情況下,取參數(shù)的值都應(yīng)該使用#{},在某些情況下,原生jdbc不支持占位符的地方可以使用${}進(jìn)行取值,
比如分表;按年份分表拆分 select from 2017_salary可以寫為 select from ${year}_salary
3.5 #{}取值規(guī)則
更豐富的用法
規(guī)定參數(shù)的一些規(guī)則:
? javaType、jdbcType、mode(存儲(chǔ)過程)、numericScale、resultMap、typeHandler、jdbcTypeName、expression
jdbcType參演需要在某種特定的條件下被設(shè)置
? 在數(shù)據(jù)為null的時(shí)候,有些數(shù)據(jù)庫可能無法識(shí)別mybatis對null的默認(rèn)處理,如oracle,mybatis對所有的null都映射為原生Jdbc的OTHER類型, oracle無法處理,mysql可以處理
1、#{email, jdbcType=OTHER}
2、全局配置文件mybatis-config.xml中:<setting name="jdbcTypeForNull" value="NULL" />
3.6 Select返回List、Map
- 返回List
- 返回Map,key就是列名,值是對應(yīng)的值
- 多條紀(jì)錄封裝成一個(gè)map,Map<Integer, Employee> 鍵是這條紀(jì)錄的主鍵,值是記錄封裝的JavaBean
3.7 自定義結(jié)果映射封裝規(guī)則
resultMap?元素是 MyBatis 中最重要最強(qiáng)大的元素。它可以讓你從 90% 的 JDBC?ResultSets?數(shù)據(jù)提取代碼中解放出來, 并在一些情形下允許你做一些 JDBC 不支持的事情。 實(shí)際上,在對復(fù)雜語句進(jìn)行聯(lián)合映射的時(shí)候,它很可能可以代替數(shù)千行的同等功能的代碼。 ResultMap 的設(shè)計(jì)思想是,簡單的語句不需要明確的結(jié)果映射,而復(fù)雜一點(diǎn)的語句只需要描述它們的關(guān)系就行了。
<resultMap id="myEmp" type="com.meituan.mybatis.mapper.bean.Employee"><!--column指定哪一列, property指定對應(yīng)JavaBean屬性id:指定主鍵列的封裝規(guī)則,會(huì)在底層優(yōu)化規(guī)則--><id column="id" property="id"/><!--定義普通列的封裝規(guī)則--><result column="last_name" property="lastName"/><!--其他不指定的列會(huì)自動(dòng)封裝,推薦只要寫resultMap,就將全列的映射規(guī)則都寫上--><result column="email" property="email"/><result column="gender" property="gender"/></resultMap><select id="getEmpById" resultMap="myEmp">SELECT *FROM employeeWHERE id = #{id}</select>3.8 關(guān)聯(lián)查詢
- 第一種resultMap的寫法:
- 第二種resultMap的寫法
- 使用association進(jìn)行分步查詢
其中association中select的部分為
<mapper namespace="com.meituan.mybatis.mapper.dao.DepartmentMapper"><select id="getDeptById" resultType="com.meituan.mybatis.mapper.bean.Department">SELECT* FROM department WHERE id=#{id}</select></mapper>3.9 延遲加載
<!--可以使用延遲加載Employee===》Dept每次查詢Employee對象的時(shí)候,都將部門信息一起查詢出來,而需要是:部門信息在使用的時(shí)候再去查詢分步查詢的基礎(chǔ)之上,加上兩個(gè)配置,即可實(shí)現(xiàn)延遲加載 -->mybatis-config.xml
<!--顯示指定每個(gè)需要更改的配置的值,即使是默認(rèn)的,以防版本更替帶來的問題--><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/>測試
@Testpublic void test07() throws Exception {SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();SqlSession sqlSession = sqlSessionFactory.openSession();try {Employee employee = sqlSession.selectOne("com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep", 1);System.out.println(employee.getLastName());System.out.println(employee.getDept().getDeptName());} finally {sqlSession.close();}}輸出:
在兩次輸出中出現(xiàn)一段sql查詢
14:23:18.093 [main] DEBUG com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep - ==> Preparing: SELECT * FROM employee WHERE id=? 14:23:18.133 [main] DEBUG com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep - ==> Parameters: 1(Integer) 14:23:18.227 [main] DEBUG com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep - <== Total: 1 tom 14:23:18.228 [main] DEBUG com.meituan.mybatis.mapper.dao.DepartmentMapper.getDeptById - ==> Preparing: SELECT* FROM department WHERE id=? 14:23:18.228 [main] DEBUG com.meituan.mybatis.mapper.dao.DepartmentMapper.getDeptById - ==> Parameters: 2(Integer) 14:23:18.269 [main] DEBUG com.meituan.mybatis.mapper.dao.DepartmentMapper.getDeptById - <== Total: 1 銷售部3.10 collection定義關(guān)聯(lián)集合封裝規(guī)則
- 嵌套結(jié)果集的方式
- 分步方式
-
擴(kuò)展:
多列傳值
? 封裝成map傳遞
? column="{key1=val1, key2=val2}"
fetchType="lazy",表示使用延遲加載
- lazy:延遲加載
- eager:立即加載
3.11 descriminator 鑒別器
<discriminator javaType="int" column="draft"><case value="1" resultType="DraftPost"/> </discriminator>有時(shí)一個(gè)單獨(dú)的數(shù)據(jù)庫查詢也許返回很多不同 (但是希望有些關(guān)聯(lián)) 數(shù)據(jù)類型的結(jié)果集。 鑒別器元素就是被設(shè)計(jì)來處理這個(gè)情況的, 還有包括類的繼承層次結(jié)構(gòu)。 鑒別器非常容易理 解,因?yàn)樗谋憩F(xiàn)很像 Java 語言中的 switch 語句。
定義鑒別器指定了 column 和 javaType 屬性。 列是 MyBatis 查找比較值的地方。 JavaType 是需要被用來保證等價(jià)測試的合適類型(盡管字符串在很多情形下都會(huì)有用)。比如:
<resultMap id="vehicleResult" type="Vehicle"><id property="id" column="id" /><result property="vin" column="vin"/><result property="year" column="year"/><result property="make" column="make"/><result property="model" column="model"/><result property="color" column="color"/><discriminator javaType="int" column="vehicle_type"><case value="1" resultMap="carResult"/><case value="2" resultMap="truckResult"/><case value="3" resultMap="vanResult"/><case value="4" resultMap="suvResult"/></discriminator> </resultMap>4. 動(dòng)態(tài)sql
MyBatis 的強(qiáng)大特性之一便是它的動(dòng)態(tài) SQL。 mybatis支持OGNL表達(dá)式。
4.1 if where trim
<!--查詢員工,需求:攜帶了哪個(gè)字段查詢條件就整個(gè)這個(gè)字段的值--><select id="getEmpsByConditionIf" resultType="com.meituan.mybatis.dynamic.bean.Employee">SELECT * FROM employee WHERE<!--test:判斷表達(dá)式(OGNL)c: if test從參數(shù)中取值進(jìn)行判斷遇見特殊符號(hào)應(yīng)該去寫轉(zhuǎn)義字符&&需要寫為&"需要寫為"--><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">AND gender=#{gender}</if></select>查詢時(shí),某些條件缺失(如id),則可能導(dǎo)致sql拼裝出現(xiàn)問題
解決方案:
1、WHERE 1=1
WHERE 1=1 <if test = "">AND *** </if>2、mybatis推薦的的方案:采用<where>標(biāo)簽,將所有的查詢條件包括在內(nèi),mybatis就會(huì)將where標(biāo)簽中拼裝的sql,多出來的and或者or去掉;where只會(huì)去掉前面多出來的and或者or
<where><!--test:判斷表達(dá)式(OGNL)c: if test從參數(shù)中取值進(jìn)行判斷遇見特殊符號(hào)應(yīng)該去寫轉(zhuǎn)義字符&&需要寫為&"需要寫為"--><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">AND gender=#{gender}</if> </where>3、trim標(biāo)簽的使用
<select id="getEmpsByConditionTrim" resultType="com.meituan.mybatis.dynamic.bean.Employee">SELECT * FROM employee<!---prefix="" 前綴:trim標(biāo)簽體中是整個(gè)字符串拼串后的結(jié)果,prefix會(huì)給拼串后的整個(gè)字符串加一個(gè)前綴prefixOverrides="" 前綴覆蓋:去掉整個(gè)字符串前面多余的字符suffix="" 后綴:整個(gè)串加后綴suffixOverrides="" 去掉整個(gè)串后面的字符--><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.trim()!=""">email=#{email} AND</if><if test="gender==0 or gender == 1">gender=#{gender}</if></trim></select>4.2 choose set標(biāo)簽
choose 分支選擇,相當(dāng)于帶了break的switch-case
<!-- 如果帶了id就用id查,如果帶了lastName就用lastName查--><select id="getEmpsByConditionChoose" resultType="com.meituan.mybatis.dynamic.bean.Employee">SELECT * FROM employee<where> <!-- 如果帶了id就用id查,如果帶了lastName就用lastName查--><choose><when test="id!=null">id=#{id}</when><when test="lastName!=null">last_name LIKE #{lastName}</when><when test="email!=null">email=#{email}</when><otherwise>1=1</otherwise></choose></where></select>set標(biāo)簽用于update,可以去掉多掉多余的“,”
4.3 foreach
<select id="getEmpsByConditionForeach" resultType="com.meituan.mybatis.dynamic.bean.Employee">SELECT * FROM employee WHERE id IN<!--collection:指定要遍歷的集合list類型的參數(shù)會(huì)特殊處理封裝在map中,map的key就叫l(wèi)istitem:將遍歷出的元素賦值給指定的變量#{變量名}就能取出變量的值也就是當(dāng)前遍歷出的元素separator:每個(gè)元素之間的分隔符open:遍歷出的所有結(jié)果拼接一個(gè)開始的字符close:遍歷出的所有結(jié)果拼接一個(gè)結(jié)束的字符index:索引。遍歷list的時(shí)候是索引遍歷map的時(shí)候index表示的就是map的key,item就是map的值--><foreach collection="ids" item="item_id" separator="," open="(" close=")">#{item_id}</foreach></select>批量插入:
<insert id="addEmpsByConditionForeach">INSERT INTO employee (last_name, email, gender, d_id) VALUES<foreach collection="list" item="emp" separator=",">(#{emp.lastName}, #{emp.email}, #{emp.gender}, #{emp.dept.id})</foreach></insert>注意:list類型的參數(shù)會(huì)特殊處理封裝在map中,map的key就叫l(wèi)ist。如果期望此時(shí)傳入的參數(shù)名由自己定制,可以
1、@Param("*")
2、將list傳入自己的map中
4.4 mybatis內(nèi)置參數(shù)
mybatis默認(rèn)有兩個(gè)內(nèi)因參數(shù)
1、 _parameter:代表整個(gè)參數(shù)
? 單個(gè)參數(shù):_parameter就是這個(gè)參數(shù)
? 多個(gè)參數(shù):參數(shù)會(huì)被封裝為一個(gè)map;_parameter就是代表這個(gè)map
2、 _databaseId: 如果配置了databaseIdProvider標(biāo)簽,
? _databaseId就是代表當(dāng)前數(shù)據(jù)庫的別名
4.5 bind
bind可以將OGNL表達(dá)式的值綁定到一個(gè)變量中,方便后來引用,例如:
<bind name="_lastName" value="'%'+lastName+'%'">
4.6 sql 抽取可重用的片段
使用sql標(biāo)簽定義可重用片段
<sql id="insertColumn">employee_id, last_name, email </sql>使用include標(biāo)簽引用可重用片段
<include refid"insertColumn"></include>5. 緩存機(jī)制
緩存可以極大的提升查詢效率。mybatis默認(rèn)定義了兩級(jí)緩存:
- 默認(rèn)情況下,只有一級(jí)緩存(SqlSession級(jí)別緩存,也稱為本地緩存)開啟。
- 二級(jí)緩存需要手動(dòng)開啟和配置,是基于namespace級(jí)別的緩存,也稱全局緩存。
- 為了提高擴(kuò)展性,mybatis定義了緩存接口Cache??梢酝ㄟ^實(shí)現(xiàn)cache接口來自定義二級(jí)緩存
5.1 一級(jí)緩存
與數(shù)據(jù)庫同一次會(huì)話期間查詢到的數(shù)據(jù)會(huì)放在本地緩存中,以后如果需要獲取相同的數(shù)據(jù),直接從緩存中拿,沒必要再去查詢數(shù)據(jù)庫。mybatis默認(rèn)開啟一級(jí)緩存。
一級(jí)緩存失效情況
- sqlSession不同
- sqlSession相同,查詢條件不同(當(dāng)前一級(jí)緩存中還沒有這個(gè)數(shù)據(jù))
- sqlSession相同,兩次查詢期間執(zhí)行了增刪改(可能會(huì)導(dǎo)致當(dāng)前數(shù)據(jù)失效)
- 手動(dòng)清空了緩存sqlSession.clearCache();
5.2 二級(jí)緩存
基于namespace級(jí)別的緩存,一個(gè)namespace對應(yīng)一個(gè)二級(jí)緩存
工作機(jī)制:
1、一個(gè)會(huì)話(session),查詢一條ovry,這個(gè)數(shù)據(jù)就會(huì)被放在當(dāng)前會(huì)話的一級(jí)緩存中
2、 如果當(dāng)前會(huì)話關(guān)閉,一級(jí)緩存中的數(shù)據(jù)會(huì)被保存到二級(jí)緩存中;新的會(huì)話查詢信息,可以參照二級(jí)緩存
3、 不同namespace查出的數(shù)據(jù)會(huì)放在自己對應(yīng)的緩存中(map)
4、 查出的數(shù)據(jù)會(huì)默認(rèn)先放在一級(jí)緩存中,只有會(huì)話提交或者關(guān)閉以后,一級(jí)緩存中的數(shù)據(jù)才會(huì)轉(zhuǎn)移到二級(jí)緩存中
使用:
1、開啟全局二級(jí)緩存配置,<setting name="cacheEnabled" value="true"/>
2、在某個(gè)namespace中進(jìn)行配置
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>- eviction
1、LRU – 最近最少使用的:移除最長時(shí)間不被使用的對象。
2、FIFO – 先進(jìn)先出:按對象進(jìn)入緩存的順序來移除它們。
3、SOFT – 軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象。
4、WEAK – 弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
默認(rèn)LRU
- flushInterval:緩存刷新間隔
緩存多久清空一次,默認(rèn)不清空,單位毫秒
- readOnly:是否只讀
true:只讀,mybatis認(rèn)為所有從緩存中獲取數(shù)據(jù)的操作都是只讀操作,不會(huì)修改數(shù)據(jù)。mybatis為了加快獲取速度,直接會(huì)將數(shù)據(jù)在緩存中的引用交給用戶。特點(diǎn):不安全,速度快
false:不只讀。mybatis認(rèn)為獲取的數(shù)據(jù)可能會(huì)被修改,mybatis會(huì)利用序列化&反序列的技術(shù)克隆一份新的數(shù)據(jù)給用戶。特點(diǎn):安全,速度慢
- size:緩存多少個(gè)元素
- type:指定自定義緩存的全類名,實(shí)現(xiàn)Cache接口即可
3、 pojo需要實(shí)現(xiàn)序列化接口
5.3 和緩存有關(guān)的設(shè)置和屬性
1、<setting name="cacheEnabled" value="true"/>開啟或關(guān)閉緩存
2、 每個(gè)select標(biāo)簽都有useCache屬性,true為使用,false為不使用(一級(jí)緩存依然使用,二級(jí)緩存不使用)
3、每個(gè)增刪改標(biāo)簽的flushCache屬性值為true,即增刪改執(zhí)行完成后應(yīng)付清除緩存,包括一級(jí)、二級(jí)緩存。查詢標(biāo)簽?zāi)J(rèn)flushCache="false",當(dāng)設(shè)置為true時(shí),每次查詢之前都會(huì)清空緩存,緩存沒有被使用
4、sqlSession.clearCache();只是清除一級(jí)緩存
5、localCacheScope:本地緩存作用域(一級(jí)緩存),可選擇SESSION、STATEMENT。STATEMENT可以禁用緩存
5.4 緩存原理
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的mybatis学习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 类选择器选择非唯一属性无法精确取值的问题
- 下一篇: ipvsadm+keepalived安装