1.数据库准备工作
2.目录创建及jar包准备
3. 创建Entity
package com.atguigu.mybatis.bean; 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; } @Override public String toString() { return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]"; } }4.创建mybatis全局配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//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://localhost:3306/mybatis2" /> <property name="username" value="root" /> <property name="password" value="kgc" /> </dataSource> </environment> </environments> <!-- 将我们写好的sql映射文件注册到全局配置文件(mybatis-config.xml)中 --> <mappers> <mapper resource="EmployeeMapper.xml"/> </mappers> </configuration>5.创建sql映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.atguigu.mybatis.EmployeeMapper"> <!-- namespace:名称空间 id:唯一标识 resultType:返回值类型 #{id}:从传递过来的参数中取出id值 --> <select id="selectEmp" resultType="com.atguigu.mybatis.bean.Employee"> select id,last_name lastName,email,gender from tbl_employee where id=#{id} </select> </mapper>6.准备日志打印文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <param name="Encoding" value="UTF-8" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout> </appender> <logger name="java.sql"> <level value="debug" /> </logger> <logger name="org.apache.ibatis"> <level value="info" /> </logger> <root> <level value="debug" /> <appender-ref ref="STDOUT" /> </root> </log4j:configuration>7.测试
package com.atguigu.mybatis.test; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import com.atguigu.mybatis.bean.Employee; public class MybatisTest { /** * 1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象 * 包含数据源的一些运行环境信息 * 2.sql映射文件:配置了每一个sql及相关封装规则 * 3.将sql映射文件注册在全局配置文件中 * 4.写代码 * 1)根据全局配置文件得到SqlSessionFactory * 2)使用SqlSession工厂获取到SqlSession实例来执行数据库操作 * 一个SqlSession就代表和数据库的一次会话,用完即关闭 * 3)使用sql的唯一标致来告诉mybatis执行哪个sql * @throws IOException */ @Test public void test() throws IOException { String resource="mybatis-config.xml"; InputStream inputStream=Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);//拿到mysql大门的钥匙 //2.获取SqlSession实例,能直接执行已经映射的sql语句 SqlSession openSession=sqlSessionFactory.openSession();//打开mysql的大门 // openSession.selectOne("selectEmp",1);//(sql的唯一标识,执行sql要用的参数) try{ Employee employee = openSession.selectOne("com.atguigu.mybatis.EmployeeMapper.selectEmp",1); System.out.println(employee); }catch(Exception e){ e.printStackTrace(); }finally{ openSession.close(); } } }8.完美运行
数据库字段和pojo属性名称不一致,映射不上,查询时取好别名保持一致即可 mybatis可以通过namespace实现Mapper接口的动态绑定,具体实现是由自动生成的代理对象完成的mybatis可以使用properties标签来引入外部properties配置文件,resource属性可以引入类路径下的资源,url属性可以引入网络路径或磁盘路径下的资源
dbconfig.properties配置文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=123456 orcl.driver=oracle.jdbc.OracleDriver orcl.url=jdbc:oracle:thin:@localhost:1521:orcl orcl.username=scott orcl.password=123456开启驼峰映射模式,比如数据库的last_name或lastname都可以映射为lastName
<settings> <!-- 开启驼峰映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>typeAliases标签
别名处理器,可以为我们的java类型起别名
type属性指定要起别名的类型的全类名,默认别名就是类名小写;alias指定一个别名,使用这个别名时不区分大小写
批量起别名
<typeAliases> <!-- <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> --> <!-- package:为某个包下的所有类批量起别名 name:指定包名(为当前包及下面所有的后代包的每一个类起一个默认的别名(类名小写)) --> <package name="com.atguigu.mybatis.bean"/> </typeAliases>注解式起别名,使用@Alias("...")实现
environments标签可以配置多种环境,使用default属性切换到id唯一标识的某个具体环境
transactionManager标签配置事务管理器,type属性配置事务管理器的类型 JDBC(JdbcTransactionFactory.class)|MANAGED(ManagedTransactionFactory.class)
执行不同数据库的sql
<!-- databaseIdProvider:支持多数据厂商 type="DB_VENDOR":VendorDatabaseIdProvider 作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql --> <databaseIdProvider type="DB_VENDOR"> <!-- 为不同的数据库厂商起别名 --> <property name="MySQL" value="mysql"/> <property name="Oracle" value="oracle"/> </databaseIdProvider> <select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee" databaseId="mysql"> select * from tbl_employee where id=#{id} </select>mappers标签:将sql映射文件注册到全局配置文件中
mapper标签: 注册一个sql映射 resource属性:引用类路径下的sql映射文件 url属性:引用网络路径或者磁盘路径下的sql映射文件
注册分两种,一种是注册配置文件(使用resource属性),一种是注册接口(使用class属性)
<!-- 将我们写好的sql映射文件注册到全局配置文件(mybatis-config.xml)中 --> <!-- 注册接口 class:引用(注册)接口, 1.关联sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下 2.不关联sql映射文件,可以使用注解完成sql的绑定 --> <mappers> <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> </mappers> package com.atguigu.mybatis.dao; import org.apache.ibatis.annotations.Select; import com.atguigu.mybatis.bean.Employee; public interface EmployeeMapperAnnotation { @Select("select * from tbl_employee where id=#{id}") public Employee getEmpById(Integer id); }批量注册sql映射文件
<package name="com.atguigu.mybatis.dao">
增删改时parameterType属性可以省略,增删改需要手动提交openSession.commit()
测试删除
@Test public void test03() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession=sqlSessionFactory.openSession(); try { EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class); // Employee employee = new Employee(null,"jerry","jerry@atguigu.com","1"); // mapper.addEmp(employee); //测试修改 mapper.deleteEmpById(1);; //手动提交 openSession.commit(); } catch (Exception e) { e.printStackTrace(); }finally{ openSession.close(); } }sqlSessionFactory.openSession(); //手动提交
sqlSessionFactory.openSession(); //自动提交
开启主键生成策略,可以获取主键自增序列值 useGeneratedKeys="true";keyProperty属性用于指定对应的主键属性,mybatis获取到主键值以后,会将这个值封装给javaBean的属性
mybatis参数处理
单个参数:mybatis不会做特殊处理,#{参数名}即可取出Mapper中的入参值,此时参数名可以和入参名不一致
多个参数:mybatis会做特殊处理,多个参数会被封装成一个map,key就是param1 param2 paramN... value就是传入的参数值
取值方式:#{param1/param2/paramN} 或#{0/1/N}或在Mapper的入参处使用@Param注解指定用于取值的参数名,取值时必须保持一致
入参是POJO时,取值方式:#{POJO的属性名}
入参是Map时,取值方式:#{key}取出map中对应的值
以下几种情况如何取值?
public Employee getEmp(@Param("id")Integer id,String lastName);
取值:id==>#{id/param1} lastName==>#{param2}
public Employee getEmp(Integer id,@Param("e")Employee emp);
取值:id==>#{param1} lastName==>#{param2.lastName/e.lastName}
特别注意,如果是Collection(List,Set)或者是数组,也会特殊处理,也会把传入的list或数组封装在map中.
Collection,key:"collection" List,key:"collection/list" Array,key:"array"
public Employee getEmpById(List<Integer> ids);
取值:取出第一个id的值,#{list[0]}
mybatis具体是怎么处理参数的?
===========================结合源码,mybatis怎么处理参数============================= 总结:多参数mybatis会封装为map,为了不混乱,可以使用@Param来指定封装时使用的key; #{key}就可以取出map中的值; ParamNameResolver解析封装map的; public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName); //1.names(是一个Map):{0=id,1=lastName}; 构造器的时候就确定好了 确定流程: 1).获取每个标了param注解的参数的@param值:id,lastName;赋值给name; 2).每次解析一个参数给map保存信息;(key:参数索引,value:name的值) name的值: 标注了param注解:注解的值 没有标注: 1.全局配置:useActualParamName(jdk1.8):name=参数名 2.没有配置:name=map.size();//相当于当前元素的索引 //第三个参数没有标注注解,key是2,value也是2(当前索引) names集合:{0=id,1=lastName,2=2} //args:[1,"Tom"] //假设args:[1,"Tom","hello"] 其中"hello"对应的参数没有param注解 names集合就是:{0=id,1=lastName,2=2} public Object getNamedParams(Object[] args) { final int paramCount = names.size(); //1.参数为null直接返回 if (args == null || paramCount == 0) { return null; //2.如果只有一个元素,并且没有param注解;args[0]:单个参数直接返回 } else if (!hasParamAnnotation && paramCount == 1) { return args[names.firstKey()]; //3.多个元素或者有param注解 } else { final Map<String, Object> param = new ParamMap<Object>(); int i = 0; //4.遍历names集合;{0=id,1=lastName} 其中0,1作为索引;id,lastName作为key // {0=id,1=lastName,2=2} 其中0,1,2作为索引;id,lastName,2作为key //entrySet()获取的是键值对集合 //把names的value(值)作为param集合的key(键); names集合的key又作为param集合取值的参考==>args:[1,"Tom"] args[0]==1;args[1]="Tom" (args[2]="hello") //{id:args[0],lastName:args[1]} <===> {id:1,lastName:"Tom"} for (Map.Entry<Integer, String> entry : names.entrySet()) { param.put(entry.getValue(), args[entry.getKey()]); // add generic param names (param1, param2, ...) //额外的将每一个参数(即多个参数中的没有使用param注解的参数)也保存到map中,使用新的key:param1...paramN //效果:有param注解可以#{指定的key},或者#{param1} String.valueOf()将整型转换为有效数字的字符串 final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1); // ensure not to overwrite parameter named with @Param if (!names.containsValue(genericParamName)) { //genericParamName为拼装的paramN args[entry.getKey()]==args[2]=="hello" //多个参数中的没有使用param注解的参数部分使用#{paramN}获取的缘由就在这了 param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } } ===================================参数值的获取===================================== #{}:可以获取map中的值或者pojo对象属性的值; ${}:可以获取map中的值或者pojo对象属性的值; select * from tb1_employee where id=#{id} and last_name=#{lastName} select * from tb1_employee where id=${id} and last_name=#{lastName} 区别: #{}:是以预编译的形式,将参数动态设置到sql语句中,类似于PreparedStatement,防止sql注入 ${}:取出的值直接拼装在sql语句中;会有安全问题; 大多数情况下,我们取参数的值应该使用#{}; 原生jdbc不支持占位符的地方我们就可以使用${}进行取值 比如分表、排序等(按照年份分表拆分): select * from ${year}_salary where xxx; select * from tb1_employee order by ${f_name} ${order} #{}:更丰富的用法 可以规定参数的一些规则: javaType、jdbcType、mode(存储过程)、numericScale(数字保留几位小数)、 resultMap(封装的结果类型)、typeHandler(类型处理器)、jdbcTypeName、expression(未来准备支持的功能) jdbcType通常需要在某种特定的条件下被设置; 在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错); JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型, Oracle不能正确处理,mysql则可以。 //第一个参数对应自增列,第三个参数给null值 Employee employee=new Employee(null,"jerry3",null,"1"); Oracle环境下需要这样操作(mybatis对null字段默认用的是OTHER,mysql可以识别但Oracle不能识别): insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) values(#{id},#{lastName},#{email,jdbcType=NULL}) 由于全局配置中,jdbcTypeForNull=OTHER;Oracle不支持 两种解决方案: 1.#{email,jdbcType=NULL}: 2.jdbcTypeForNull=NULL; 示例: <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> <!--帮助Oracle处理默认的jdbcType Other,mysql本身可以识别所以这一步是可选的--> <setting name="jdbcTypeForNull" value="NULL"/> </settings>
mybatis总结
1.MyBatis简介
1).框架:整体解决方案。
2).congiguration标签中的子标签顺序:
properties--->settings-->typeAliases-->typeHandlers-->objectFactory--->
objectWrapperFactory-->reflectorFactory-->plugins-->enviroments-->
databaseIdProvider-->mappers
3).Oracle中拿到序列中的下一个值(连查时每次会默认递增1,拿到的是计算后的下一个序列值):
select EMPLOYEES_SEQ.nextval from dual;//200
select EMPLOYEES_SEQ.nextval from dual;//201
select EMPLOYEES_SEQ.nextval from dual;//202
注意:EMPLOYEES_SEQ.nextval拿到的是当前最后一个序列值的下一个序列值。
4).MySql查询时不强调大小写,比如查id记录:
SELECT iD FROM tb1_employee; //ok
SELECT ID FROM tb1_employee; //ok
SELECT id FROM tb1_employee; //ok
2.Select元素
1).Select元素用来定义查询操作。
2).Id:唯一标识符。
用来引用这条语句,需要和接口的方法名一致。
3).parameterType:参数类型。
可以不传,mybatis会根据TypeHandler自动推断
4).resultType:返回值类型。
别名或者全类名,如果返回的是集合,定义集合中元素的类型。不能和resultMap同时使用。
5).模糊查询(四种方法实现)
①类中先设置好lastName:"%e%",传入参数,xml中取出
select * from user where last_name like #{lastName}
②xml中使用concat函数拼接
③xml中的sql写成'%${lastName}%'
select * from tbl_employee where last_name like '%${lastName}%'
④xml中使用
EmployeeMapperDynamicSQL mapper= openSession.getMapper(EmployeeMapperDynamicSQL.class); Employee employee=new Employee(); employee.setLastName("e"); List<Employee> list=mapper.getEmpsTestInnerParameter(employee); <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee"> <!--可以将OGNL表达式的值取出来进行拼接,绑定到一个变量中,方便之后引用,name可以自定义--> <bind name="_lastName" value="'%'+lastName+'%'"/> <if test="_databaseId=='mysql'"> select * from tbl_employee <if test="_parameter!=null"> where last_name like #{_lastName} </if> </if> </select>
6).返回一条记录的Map,key就是列名,value就是对应的列值
public Map<String,Object> getEmpByIdReturnMap(Integer id);
resultType="map"
7).返回多条记录的Map,key就是这条记录的某个字段(通常是主键),value就是记录封装后的javabean
@MapKey("id")
public Map<Integer,Employee> getEmpByLastNameLikeReturnMap(String lastName);
resultType="com.atkgc.bean.Employee"
8).resultMap自定义封装规则,主要用来解决数据库查询显示字段和POJO属性映射不上的情况,也可以选择性或完整的封装
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmp"> <result column="last_name" property="lastName"/> </resultMap>9).级联属性封装规则
<!-- 查询最终显示字段和POJO属性的封装映射关系 --> <resultMap type="com.atguigu.mybatis.bean.Employee" id="myEmp"> <result column="last_name" property="lastName"/> <result column="did" property="department.id"/> <result column="dept_name" property="department.departmentName"/> </resultMap> <!-- 多余的查询字段不会进行封装 --> <select id="getEmpAndDept" resultMap="myEmp"> select e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id=d.id and e.id=#{id} </select>10).关联单个对象之association标签的使用
<!-- 使用association标签,javaType属性不可少,而且名称一致的映射也需要显示指定才能封装上--> <resultMap type="com.atguigu.mybatis.bean.Employee" id="myAssociationEmp"> <result column="id" property="id"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <result column="last_name" property="lastName"/> <association property="department" javaType="com.atguigu.mybatis.bean.Department"> <result column="did" property="id"/> <result column="dept_name" property="departmentName"/> </association> </resultMap> <!-- 多余的查询字段不会进行封装 --> <select id="getEmpAndDept" resultMap="myAssociationEmp"> select e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id=d.id and e.id=#{id} </select>11).association标签分步查询进行封装
DepartmentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.atguigu.mybatis.dao.DepartmentMapper"> <!-- public Department getDeptById(Integer id); --> <select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department"> select id,dept_name departmentName from tbl_dept where id=#{id} </select> </mapper>EmployeeMapper.xml
<resultMap type="com.atguigu.mybatis.bean.Employee" id="myEmpByStep"> <!-- 2.简单属性的封装 --> <result column="id" property="id"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <result column="last_name" property="lastName"/> <!-- 3.association使用已有的d_id查询显示列的值作为第二部查询的条件,查询并完成复杂属性的封装(select属性用于绑定相关方法) --> <association property="department" select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id"> </association> </resultMap> <!-- 1.先查出员工表该记录信息,完成employee简单属性的封装 --> <select id="getEmpByIdStep" resultMap="myEmpByStep"> select * from tbl_employee where id=#{id} </select>12).association分步查询实现延迟加载
<resultMap type="com.atguigu.mybatis.bean.Employee" id="myEmpByStep"> <!-- 2.简单属性的封装 --> <result column="id" property="id"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <result column="last_name" property="lastName"/> <!-- 3.association使用已有的d_id查询显示列的值作为第二部查询的条件,查询并完成复杂属性的封装(select属性用于绑定相关方法) --> <association property="department" select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id"> </association> </resultMap> <!-- 1.先查出员工表该记录信息,完成employee简单属性的封装 --> <select id="getEmpByIdStep" resultMap="myEmpByStep"> select * from tbl_employee where id=#{id} </select>mybatis-config.xml
<settings> <!-- 开启驼峰映射 --> <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> --> <setting name="mapUnderscoreToCamelCase" value="false"/> <!-- 1.开启延迟加载 --> <!-- <setting name="lazyLoadingEnabled" value="true"/> 2.设置按需加载模式 <setting name="aggressiveLazyLoading" value="false"/> --> </settings>测试类
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession=sqlSessionFactory.openSession(); try { EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class); // Employee employee=mapper.getEmpAndDept(2); Employee employee=mapper.getEmpByIdStep(2); System.out.println(employee.getEmail()); } catch (Exception e) { e.printStackTrace(); }finally{ openSession.close(); }发送2条sql
开启懒加载
<settings> <!-- 开启驼峰映射 --> <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> --> <setting name="mapUnderscoreToCamelCase" value="false"/> <!-- 1.开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 2.设置按需加载模式 --> <setting name="aggressiveLazyLoading" value="false"/> </settings>只发送1条sql
按需加载
EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class); // Employee employee=mapper.getEmpAndDept(2); Employee employee=mapper.getEmpByIdStep(2); System.out.println(employee.getEmail()); System.out.println(employee.getDepartment().getDepartmentName());//需要才加载需要时发送2条sql
13).一对多映射规则之collection标签,所有映射关系必须显示指定才能封装上
<resultMap type="com.atguigu.mybatis.bean.Department" id="myDept"> <id column="did" property="id"></id> <result column="dept_name" property="departmentName"/> <collection property="emps" ofType="com.atguigu.mybatis.bean.Employee"> <result column="eid" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> </collection> </resultMap> <!-- public Department getDeptByIdPlus(Integer id); --> <select id="getDeptByIdPlus" resultMap="myDept"> select d.id did,d.dept_name dept_name, e.id eid,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> public class Department { private Integer id; private String departmentName; private List<Employee> emps;14).collection之分步查询和延迟加载
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDeptStep"> <!-- 2.部门信息进行封装 --> <!-- <id column="id" property="id"/> --> <result column="id" property="id"/> <!-- 3.部门信息中取出id查询列的值作为第二步查询的条件,再完成emps属性的封装 --> <collection property="emps" select="com.atguigu.mybatis.dao.EmployeeMapper.getEmpsByDeptId" column="id"> </collection> </resultMap> <!-- 1.先查部门信息 --> <select id="getDeptByIdStep" resultMap="MyDeptStep"> select id,dept_name departmentName from tbl_dept where id=#{id} </select> <select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee where d_id=#{deptId} </select>ps:多个查询显示列的值传递,会封装成map进行传递,column="{key1=column1,key2=column2,...}",keyN对应接收参数名 ,比如上面的可以写成column="{deptId=id}" .fetchType属性有两个属性值,一个是默认的lazy(延迟加载),一个是eager(立即加载)
15).discriminator鉴别器的使用
mybatis可以使用它判断某列的值,然后根据某列的值改变封装行为
<resultMap type="com.atguigu.mybatis.bean.Employee" id="myEmpDis"> <id column="id" property="id"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <result column="last_name" property="lastName"/> <!-- column:指定判断的列名 --> <discriminator javaType="string" column="gender"> <!-- resultType:指定封装的结果类型,这个类型对象中有department属性 --> <case value="0" resultType="com.atguigu.mybatis.bean.Employee"> <association property="department" select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id"> </association> </case> <case value="1" resultType="com.atguigu.mybatis.bean.Employee"> <!-- 指定新的映射规则,优先级更高 --> <result column="last_name" property="email"/> </case> </discriminator> </resultMap>
3.自动映射
1).全局setting设置
-autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javabean属性名一致。
-如果autoMappingBehavior设置为null则会取消自动映射
-数据库字段名规范,POJO属性符合驼峰命名规则,如:A_COLUMN→aColumn, last_name→lastName,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase=true;
2).自定义resultMap,实现高级结果集映射。
4.动态SQL
1).动态SQL是mybatis强大特性之一。极大的简化我们拼装SQL的操作。
2).动态SQL元素和使用JSTL或其他类似基于XML的文本处理器相似。
3).MyBatis采用功能强大的基于OGNL的表达式来简化操作。
-if:判断
-choose(when,otherwise):分支选择,类似于switch-case
-trim(where,set):字符串截取
-foreach:遍历
5.mybatis对于where中,只能去除if中出现在拼接条件开头处的第一个并且多出的"and"或"or"!
6.mybatis对于set中,只能去除if中被拼接部分最后一个修改列部分多出的逗号!
7.缓存机制
1).MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
2).MyBatis系统中默认定义了两级缓存(一级缓存和二级缓存)。
3).一级缓存和二级缓存。
-默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)一直开启。
-二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
-为了提高扩展性,MyBatis定义了缓存接口Cache。可以通过实现Cache接口来自定义二级缓存。
一级缓存使用
/** * 缓存机制 * 一级缓存:(本地缓存) * 与数据库同一次会话期间查询到的数据都会在放在本地缓存中 * 以后如果需要获取相同的数据,直接从这个本地缓存中拿,没必要再重新去查数据库 * 二级缓存:(全局缓存) * */ @Test public void testFirstLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession openSession=sqlSessionFactory.openSession(); try{ EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class); //由于缓存机制,mybatis值发送了一条sql Employee emp02=mapper.getEmpById(2); Employee emp03=mapper.getEmpById(2); System.out.println(emp02==emp03);//true //会重新发送一条新的sql // Employee emp03=mapper.getEmpById(3); System.out.println(emp02); System.out.println(emp03); }finally{ openSession.close(); } }@Test public void testFirstLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession openSession=sqlSessionFactory.openSession(); try{ //不同会话期间都有自己的本地缓存(可以理解为仓库),某一会话一旦关闭就无法继续使用 EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class); Employee emp02=mapper.getEmpById(2); openSession.close(); System.out.println(emp02); SqlSession openSession2=sqlSessionFactory.openSession(); EmployeeMapper mapper2=openSession2.getMapper(EmployeeMapper.class); Employee emp03=mapper2.getEmpById(2); System.out.println(emp03); }finally{ } }
二级缓存使用
/** * 两级缓存: * 一级缓存:(本地缓存)SqlSession级别的缓存,一级缓存是一直开启的 * 与数据库同一次会话期间查询到的数据都会在放在本地缓存中 * 以后如果需要获取相同的数据,直接从这个本地缓存中拿,没必要再重新去查数据库 * * 一级缓存失效情况 * 1.SqlSession不同(每个数据库会话都有自己唯一的本地缓存),SqlSession级别的一个map * 2.SqlSession相同,查询条件不同(当前一级缓存中还没有这些数据) * 3.SqlSession相同,两次查询之间还执行了增删改操作(对当前数据库有影响,即数据有变动) * 4.SqlSession相同,手动清除了当前数据库会话的一级缓存 * 二级缓存:(全局缓存),基于namespace级别的缓存 * 工作机制: * 1.一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中 * 2.如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容 * 3.因为基于namespce,所以不同命名空间下的xxxMapper查出的数据都会放在自己对应的缓存中(map中) * * 使用: * 1)mybatis-config.xml开启全局二级缓存配置<setting name="cacheEnabled" value="true"/> * 2)mapper.xml中配置使用二级缓存<cache></cache>,当前Mapper就有了自己对应的二级缓存 * 3)POJO需要实现序列化接口 * ps:查出的数据默认都会现放在一级缓存中,只有当会话提交或关闭,一级缓存的数据才会转移 * 到二级缓存中;mybatis-config.xml中cacheEnabled=false,会关闭二级缓存,不关闭一级缓 * 存. */ @Test public void testSecondLevelCache() throws IOException{ SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession openSession=sqlSessionFactory.openSession(); SqlSession openSession2=sqlSessionFactory.openSession(); try{ EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class); EmployeeMapper mapper2=openSession2.getMapper(EmployeeMapper.class); Employee emp01=mapper.getEmpById(2); System.out.println(emp01); openSession.close();//会话提交或关闭后,且开启了缓存机制,数据才会保存到二级缓存中 //第二次查询是从二级缓存中拿到的数据,并没有发送新的sql Employee emp02=mapper2.getEmpById(2); System.out.println(emp02); openSession2.close(); }finally{ } }缓存原理图
ps:每个select标签默认使用了useCache="true",设置为false,一级缓存依然使用,二级缓存不使用.每个增删改对应的标签默认使用了flushCache="true",一级缓存和二级缓存都会被清除.sqlSession.clearCache()只是清除一级缓存.
4).以下代码段在JUNIT测试中出错,因为SqlSession关闭后无法再用当前会话再次访问数据库。
Employee emp01=mapper.getEmpById(1);
System.out.println(emp01);
openSession.close();
Employee emp001=mapper.getEmpById(1);
System.out.println(emp001);
8.缓存命中的日志提示
1).二级缓存开启状态下,如果二级缓存中搜索到可用的数据信息,一定会产生一个不为0的命中时间,比如:
DEBUG [main] - Cache Hit Ratio [com.atguigu.mybatis.dao.EmployeeMapper]:0.3333333333333333
2).一级缓存中搜索到可用的数据信息不会有命中提示信息
9.MyBatis缓存顺序
二级缓存===》一级缓存===》数据库
10.单个起别名
11.指定包名,批量起别名
每一个在包domain.blog中的Java bean,在没有注解的情况下,会使用bean的首字母小写的非限定类名作为它的别名。比如domain.blog.Author的别名为author。(别名不区分大小写)如果有注解,则别名为注解的值。
12.mybatis内建别名
Mybatis为许多常见的java类型内建了相应的类型别名。它们都是不区分大小的。
13.@Param注解可以明确指定封装参数时map的key
14.常用标签
1).if标签 and &&==>&& ""==>"" 特殊符号需转义字符 ONGL会进行字符串和数字的转换判断 "0"==>0 会自动去除第一个多余的and或者or,多条件不用where标签可以使用恒等式条件 2).where标签 mybatis使用where标签来将所有的查询条件包括在内 3).trim标签 prefix:指定前缀 prefixOverrides:前缀覆盖,去掉整个字符串前面多余的字符 suffix:后缀 suffixOverrides:后缀覆盖,去掉整个字符串后面多余的字符 4).choose标签(when,otherwise) 作用:选择性进入第一个满足条件的分支 5).if+set的动态更新 set标签可以去除多余的逗号 <!--update+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> <!--update+trim--> <update id="updateEmp"> 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> 6).foreach标签 collection:指定要遍历的集合 list类型的参数会特殊处理封装在map中,map的key就叫list item:接收遍历的每一项 #{item指定的变量名}就能取到每一项的值 separator:分隔符 open:开始符 close:结束符 index:索引.遍历list的时候是索引;遍历map的时候index表示的就是map的key,item就是map的值 foreach实现批量插入 <!-- public void addEmps(@Param("emps")List<Employee> emps); --> <insert id="addEmps"> insert into tbl_employee(last_name,email,gender,d_id) values <foreach collection="emps" item="emp" separator=","> (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}) </foreach> </insert> jdbc.url=jdbc:mysql://localhost:3306/mybatis2?allowMultiQueries=true <!--mysql连接url设置属性allowMultiQueries=true--> <!-- public void addEmps(@Param("emps")List<Employee> emps); --> <insert id="addEmps"> <foreach collection="emps" item="emp" separator=";"> insert into tbl_employee(last_name,email,gender,d_id) values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}) </foreach> </insert> 7).mybatis两个内置参数 <!-- mybatis默认有两个内置参数: _parameter:代表整个参数 单个参数:_parameter就是这个参数 比如下面的employee对象 多个参数:参数会被封装成一个map,_parameter就代表这个map _databaseId:如果配置了databaseIdProvider标签(mybatis全局配置文件中) _databaseId就代表当前数据库的别名 比如mysql oracle environments==>databaseIdProvider==>if --> <!-- public List<Employee> getEmpsTestInnerParameter(Employee employee); --> <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee"> <if test="_databaseId=='mysql'"> select * from tbl_employee <if test="_parameter!=null"> where last_name=#{_parameter.lastName} </if> </if> <if test="_databaseId=='oracle'"> select * from employees </if> </select> 8).sql+include sql标签用于抽取可重用的sql或某部分,方便重用,重用时通过include标签包含在某一位置即可(refid指定为sql的唯一标识id)