Hello World
- 创建数据表
- 引入相关jar包
- 创建mybatis-config.xml配置文件
<?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/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
-
测试类
public class MyBatisTest { @Test public void test() throws Exception { // 1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 2.获取SqlSession实例,能直接执行已经映射的sql语句 SqlSession openSession = sqlSessionFactory.openSession(); try { Employee employee = openSession.selectOne("com.zzf.mybatis.EmployeeMapper.selectEmp", 1);//参数一:[namespace]+sql的唯一标识;参数二:sql的参数 System.out.println(employee); } finally { openSession.close(); } } }
-
创建sql映射文件:EmployeeMapper.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.zzf.mybatis.EmployeeMapper"> <!-- id:唯一标识 --> <!-- resultType表示要封装成的对象,必须为全类名 --> <select id="selectEmp" resultType="com.zzf.mybatis.bean.Employee"> <!-- #{id}:从传参中取出id值 --> select * from Employee where id = #{id} </select> </mapper>
接口式编程
改进Hello World
-
创建dao-EmployeeDao接口,编写抽象方法
-
修改sql映射文件的命名空间为该接口的全路径,id改为该抽象方法
-
编写测试
@Test public void test2() throws IOException { // 1.根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 2.获取SqlSession实例 SqlSession openSession = sqlSessionFactory.openSession(); //openSession默认不自动提交 // 3.获取EmployeeMapper接口实现对象 try { //MyBatis会为该接口自动生成一个代理对象 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmployeeById(1); System.out.println(emp); }finally { openSession.close(); } }
MyBatis中接口式编程对比原生操作
原生:Dao---->DaoImpl
MyBatis:Mapper接口---->xxxMapper.xml映射文件
关于SqlSession
- 一个SqlSession即一次数据库会话,用完必须关闭
- SqlSession和connection一样是非线程安全,即每次使用都需获取新的对象
全局配置文件
引入dtd约束(实现标签提示):window–>reference–>xml–>xmlcatalog–>add–>uri+location
properties标签(了解)
<properties resource="dbconfig.properties"></properties>
可以引入外部properties配置文件的内容
其中:url属性引入网络路径的资源;resource引入类路径的资源
settings标签
该标签设置MyBatis的运行时行为
<settings>
<!-- 每一个设置项设置项名和取值:以下是开启驼峰命名法 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
typeAliases标签
别名处理器,该标签可以给java类型起别名,别名不区分大小写
<typeAliases>
<!-- 方式一:单个起别名,默认别名为类名转小驼峰 -->
<typeAlias type="com.zzf.mybatis.bean.Employee" alias="emp"/>
<!-- 方式二:批量起别名,若别名冲突,可以直接用@Alias(别名)修饰该类自定义别名 -->
<package name="com.zzf.mybatis.bean"/>
</typeAliases>
typeHandlers标签
类型处理器, 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
plugins标签
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。后面会有专门的章节我们来介绍mybatis运行原理以及插件
environments标签
- environments可以配置多种环境(如测试、开发),default属性指定使用对应id的环境
- 必须有transactionManager(事务管理器)配置,type属性:JDBC/MANAGED/自定义
- 必须有dataSource(数据源)配置,type属性:POOLED/UNPOOLED/JNDI/自定义
<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>
databaseIdProvider标签
根据不同的数据库厂商执行不同的语句
<!-- DB_VENDOR可以获取不同数据库厂商的标识,MyBatis就可以自动识别 -->
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同数据库厂商起别名,在sql映射文件操作语句中使用databaseId绑定对应数据库厂商 -->
<property name="MYSQL" value="mysql"/>
</databaseIdProvider>
mappers标签
-
逐个注册SQL映射文件
mapper的属性:
- resource属性:类路径下的文件
- url属性:网络或磁盘路径下的文件
- class属性:接口全类名,直接引用接口,要求:
- 方式一:映射文件和接口同名并在同一个包下
- 方式二:不用映射文件,直接注解(@Select(“sql语句”))修饰接口中的方法(不推荐,只用于简单的Dao接口)
<mappers> <!-- 将写好的sql映射文件配置到全局配置文件中 --> <mapper resource="EmployeeMapper.xml" /> </mappers>
-
批量注册SQL映射文件:要求SQL映射文件名必须和接口名相同并且在同一目录下
<mappers> <package name="com.zzf.mybatis.dao"/> </mappers>
SQL映射文件
增删改查标签
<select id="getEmployeeById" resultType="employeE">
查询语句
</select>
<insert id="addEmp">
插入语句
</insert>
<update id="updateEmp">
修改语句
</update>
<delete id="deleteEmp">
删除语句
</delete>
获取自增主键
-
对于支持自增主键的数据库:
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id"> </insert>
-
Oracle不支持自增主键,其使用序列模拟自增,每次插入数据都是从该序列中取值
使用selectKey获取序列中的自增值,赋给keyProperty,再执行后面的查询语句,BEFORE(常用)表示再语句执行前赋值id,AFTER反之
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id"> <selectKey keyProperty="id" order="BEFORE" resultType="Integer"> select EMPLOYEE_SEQ.nextval from dual </selectKey> 查询语句 </insert>
参数处理(Parameters)
参数传递
-
单个参数:MyBatis不做特殊处理
#{参数名}直接取值,参数名任意
-
多个参数:MyBatis做特殊处理,将参数封装成map,key为索引0到n或param1到paramN,value为参数值
#{索引值/paramN}
问题:该key无法做到见名知义----使用命名参数
-
命名参数,@Param(“自定义map的key名”)修饰方法中的形参,就可以使用#{自定义key}获取参数值
-
POJO参数:若传递参数即bean类,直接把该类作为参数
#{属性名}
-
Map参数:若传递参数不是现有的POJO,也可以使用Map作为参数
#{key名}
-
特别的:Collection参数(包括List、Set)或数组,则key为collection、list、array
参数处理
#{ }与${ }的区别
相同点:二者都可以获取map中的值或pojo属性的值,
区别:#{}是以预编译形式将参数设置到sql中; 将 参 数 直 接 拼 在 s q l 语 句 中 , 有 s q l 注 入 问 题 , 在 原 生 j d b c 不 支 持 占 位 符 的 地 方 就 可 以 使 用 {}将参数直接拼在sql语句中,有sql注入问题,在原生jdbc不支持占位符的地方就可以使用 将参数直接拼在sql语句中,有sql注入问题,在原生jdbc不支持占位符的地方就可以使用{}进行拼串,如表名、order排序
#{ }的更多用法
-
#{ }支持一些属性设置: javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression
-
#{key,jdbcType=NULL}—>当参数为空时,默认是原生jdbc中的OTHER类型,Oracle不支持,可以通过jdbcType改为NULL,也可以直接修改全局配置文件jdbcTypeForNull=NULL
select元素
select用来定义查询操作
id属性
唯一标识符,用来引用这条语句,需要和接口的方法名一致
parameterType属性
参数类型,可以不传,MyBatis会根据TypeHandler自动推断
resultType属性
不能和resultMap同时使用
resultType="全类名":返回值类型,别名或者全类名,如果返回的是集合,定义集合中元素的类型。
resultType="map":返回一个Map:{id="...",lastName="...",...}
resultType="全类名",并在接口的方法上加@MapKey("作为key的属性名"),就可以返回Map<作为key的属性值,类>
resultMap属性
自定义封装查询结果—>bean类
情况一:简单的bean类
<resultMap type="com.zzf.mybatis.bean.Employee" id="MyEmp">
<!-- id标签定义主键,底层自动优化 -->
<id column="id" property="id"/>
<!-- result标签定义普通列 -->
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>
<select id="getEmpById" resultMap="MyEmp">
select * from employee where id = #{id}
</select>
情况二:bean中属性为另一个bean,各种方法:association的使用
<resultMap type="com.zzf.mybatis.bean.Employee" id="MyEmp">
<id column="id" property="id" />
<result column="lastName" property="lastName" />
<!--方式一:级联属性-->
<!-- <result column="deptName" property="dept.deptName"/> <result column="did" property="dept.id"/> -->
<!--方式二:association嵌套结果集-->
<association property="dept" javaType="com.zzf.mybatis.bean.Department">
<id column="did" property="id" />
<result column="deptName" property="deptName" />
</association>
</resultMap>
<select id="getEmpById" resultMap="MyEmp">
select e.id id,e.last_name
lastName,e.email email,e.gender gender,d.id did,
d.dept_name deptName from employee e,department d
where e.dept_id = d.id and e.id = #{id}
</select>
<!--方式三:使用association做分步查询-->
<resultMap type="com.zzf.mybatis.bean.Employee" id="MyEmpStep">
<id column="id" property="id" />
<result column="lastName" property="lastName" />
<association property="dept" select="com.zzf.mybatis.dao.DepartmentMapper.getDeptById" column="dept_id"></association>
</resultMap>
<select id="getEmpByIdStep" resultMap="MyEmpStep">
select * from employee where id = #{id}
</select>
<!--association分步查询可以实现延迟加载:即用到另一个bean时再查询加载-->
<!-配置全局配置文件-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
情况三:bean中有另一个bean集合:使用collection标签
<resultMap type="com.zzf.mybatis.bean.Department" id="MyDept">
<id column="did" property="id"/>
<result column="deptName" property="deptName"/>
<collection property="employees" ofType="com.zzf.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="lastName" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
<select id="getDeptByIdEmp" resultMap="MyDept">
SELECT d.id did,d.dept_name deptName,
e.id id,e.last_name lastName,e.email email,e.gender gender
FROM department d
LEFT JOIN employee e
ON d.id=e.`dept_id`
WHERE d.id = #{id};
</select>
<!--collection分步查询-->
<resultMap type="com.zzf.mybatis.bean.Department" id="MyDeptStep">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="employees"
select="com.zzf.mybatis.dao.EmployeeMapper2.getEmpByDeptId"
column="id"></collection>
</resultMap>
<select id="getDeptByIdEmpStep" resultMap="MyDeptStep">
select id,dept_name from department where id = #{id}
</select>
扩展:
-
分步查询时,当column有多个列时,使用map封装:{key1=column1,key2=column2…}
-
association或者collection标签的fetchType=eager/lazy可以覆盖全局的延迟加载策略,指定立即加载(eager)或者延迟加载(lazy)
-
鉴别器(了解):判断某列的值,根据值的不同选择不同的封装行为
<discriminator javaType="string" column="gender"> <case value=".." resultType="全类名"> <!--association--> </case> </discriminator>
动态sql
if标签
<select id="getEmpWithIF" resultType="com.zzf.mybatis.bean.Employee">
select * from employee where 1=1
<if test="id!=null">
and id=#{id}
</if>
<if test="lastName!=null and !''.equals(lastName)">
and last_name like #{lastName}
</if>
<if test="gender==0 or gender==1">
and gender = #{gender}
</if>
</select>
trim和where标签
避免sql拼装问题:
-
where 后加1=1
-
直接使用标签包裹(有问题,不能解决后面多出的"and")
-
使用trim:
choose-when-otherwise标签
<select id="getEmpWithChoose"
resultType="com.zzf.mybatis.bean.Employee">
select * from employee
<where>
<choose>
<when test="id!=null">
id = #{id}
</when>
<when test="lastName!=null">
last_name like #{lastName}
</when>
<otherwise>
gender = 1
</otherwise>
</choose>
</where>
</select>
set标签
<update id="updateEmpWithSet">
update 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>
foreach标签
- 当迭代列表、集合等可迭代对象或者数组时
index是当前迭代的次数,item的值是本次迭代获取的元素 - 当使用字典(或者Map.Entry对象的集合)时
index是键,item是值
<select id="getEmpWithForeach" resultType="com.zzf.mybatis.bean.Employee">
select * from employee
<foreach collection="list" item="item_id"
open="where id in (" close=")" separator=",">
#{item_id}
</foreach>
</select>
两个内置参数
MyBatis不只是方法传递的参数可以用来判断取值,默认还有两个内置参数:
_parameter:代表传递的整个参数,多个参数即为封装后的Map
_databaseId:若全局配置文件中配置了databaseIdProvider,则 _databaseId就代表当前数据库的别名
bind标签
<bind name="_lastName" value="'%'+lastName+'%'"/>
<!--此时用#{_lastName}取值-->
sql标签
抽取可重用的sql片段
<sql id="insertColumn">
id,last_name,email,${自定义字段}
</sql>
<!--使用:-->
<include refid="insertColumn">
<property name="自定义字段名" value="值"></property>
</include>
缓存机制
缓存的查找顺序:先看二级缓存,再看一级缓存,若都没有则查询数据库
一级缓存(本地缓存)(默认)
- 一级缓存(local cache), 即本地缓存, 是一个Map,作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。
- 本地缓存不能被关闭, 但可以调用 clearCache(),来清空本地缓存, 或者改变缓存的作用域
- 在mybatis3.1之后, 可以配置本地缓存的作用域,在 mybatis.xml 中配置
- 一级缓存失效的情况:
- sqlSession不同,即不是同一次数据库会话
- sqlSession相同,查询条件不同,即当前缓存没有该数据
- 两次相同查询间执行了增删改操作,即该操作可能影响了查询的数据
- 缓存被清空----clearCache()
二级缓存(全局缓存)
-
基于namespace级别的缓存,一个namespace对应一个二级缓存
-
当会话关闭,该一级缓存的数据才会保存到二级缓存中
-
使用
-
全局配置文件中开启全局二级缓存:
-
sql映射文件中配置二级缓存:
cache中的属性:
eviction:回收策略,值为:LRU(默认)、FIFO等
flushInterval:缓存刷新间隔时间,单位毫秒
readOnly:是否只读
size:最大存储的对象数量
<cache></cache>
-
POJO类必须实现序列化接口
-
缓存的一些设置及属性
-
全局配置文件的cacheEnable属性:配置二级缓存的开关
-
映射文件的userCache属性:配置该select的二级缓存的开关
-
映射文件中增删改标签的flushCache属性:配置是否清空一二级缓存(默认true)
-
sqlSession.clearCache():只是用来清除一级缓存
-
localCacheScope:本地缓存作用域(一级缓存):SESSION/STATEMENT
整合第三方缓存
-
MyBatis自带的缓存管理简陋,可以使用第三方缓存(Redis、EhCache)
-
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
-
MyBatis定义了Cache接口方便我们进行自定义扩展
-
步骤:
-
导入ehcache包,以及整合包,日志包:
ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar
slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar -
编写ehcache.xml配置文件
-
映射文件中配置cache标签
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
-
参照缓存:在命名空间中共享相同的缓存配置和实例
<cache-ref namespace="另一个有配置缓存的mapper的全类名" />
-
SSM整合(练习)
-
导入相关jar包(spring-springmvc-mybatis-数据库驱动)
-
MyBatis配置文件:mybatis-config.xml
-
web.xml中配置ContextLoaderListener
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>MyBatis_02_ssm</display-name> <!-- needed for ContextLoaderListener --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
SpringMVC配置文件:spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- use-default-filters禁用掉默认过滤行为 --> <context:component-scan base-package="com.zzf.mybatis" use-default-filters="false"> <!-- 只扫描Controller --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean> <mvc:annotation-driven></mvc:annotation-driven> <mvc:default-servlet-handler/> </beans>
-
Spring配置文件:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <context:component-scan base-package="com.zzf.mybatis"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <context:property-placeholder location="classpath:dbconfig.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="driverClass" value="${jdbc.driver}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- spring事务管理器 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启基于注解的事务 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property> </bean> <!-- 扫描所有的mapper接口的实现,让这些mapper可以自动注入 --> <mybatis-spring:scan base-package="com.zzf.mybatis.dao"/> </beans>
MyBatis逆向工程
-
MyBatis Generator :简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删改查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sql的定义需要我们手工编写
-
MBG的使用:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- java Bean生成策略 -->
<javaModelGenerator
targetPackage="com.zzf.mybatis.bean" targetProject=".\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.zzf.mybatis.dao"
targetProject=".\conf">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 指定mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.zzf.mybatis.dao" targetProject=".\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 数据表与Bean的映射 -->
<table schema="DB2ADMIN" tableName="employee"
domainObjectName="Employee">
</table>
<table tableName="department" domainObjectName="Department"></table>
</context>
</generatorConfiguration>
//生成器代码
public void test1() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
//测试代码
public void test2() throws IOException {
String resource = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeExample employeeExample = new EmployeeExample();
Criteria criteria = employeeExample.createCriteria();
criteria.andLastNameLike("%誉%");
criteria.andGenderEqualTo("1");
Criteria criteria2 = employeeExample.createCriteria();
criteria2.andEmailLike("%@%");
employeeExample.or(criteria2);
List<Employee> emps = mapper.selectByExample(employeeExample);
for (Employee emp : emps) {
System.out.println(emp.getLastName());
}
} finally {
openSession.close();
}
}
MyBatis工作原理
//分析源码
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(1);
System.out.println(emp);
}finally {
openSession.close();
}
-
获取sqlSessionFactory对象
解析配置文件的信息保存在Configuration对象中,返回包含Configuration的DefaultSqlSessionFactory对象,其中一个MapperStatement就代表一个增删改查标签的信息
-
获取sqlSession对象
Executor是四大对象之一
-
获取接口的实现类对象
getMapper使用MapperProxyFactory创建了一个MapperProxy代理对象,该代理对象中封装了DefaultSqlSession(Executor)
-
执行增删改查方法
原理总结:了解四大对象的作用
* 总结: * 1、根据配置文件(全局,sql映射)初始化出Configuration对象 * 2、创建一个DefaultSqlSession对象, * 他里面包含Configuration以及 * Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor) * 3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy; * 4、MapperProxy里面有(DefaultSqlSession); * 5、执行增删改查方法: * 1)、调用DefaultSqlSession的增删改查(Executor); * 2)、会创建一个StatementHandler对象。 * (同时也会创建出ParameterHandler和ResultSetHandler) * 3)、调用StatementHandler预编译参数以及设置参数值; * 使用ParameterHandler来给sql设置参数 * 4)、调用StatementHandler的增删改查方法; * 5)、ResultSetHandler封装结果 * 注意: * 四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);
插件开发
插件原理
在四大对象创建时:
-
每个创建的对象不是直接返回,而是先调用interceptorChain.pluginAll(parameterHandler);
-
pluginAll获取所有的Interceptor(拦截器)(插件需要实现的接口)
调用interceptor.plugin(target);返回target包装后的对象
-
插件机制:我们可以使用插件为目标对象创建一个代理对象(AOP面向切面)
插件为四大对象创建代理对象,代理对象就可以拦截四大对象的每一个执行方法
-
MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。
-
默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback,getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update,query)
插件原理:
- 按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理
- 多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链
- 目标方法执行时依次从外到内执行插件的intercept方法。
- 多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值
插件开发
步骤:
-
编写插件实现Interceptor接口,并使用@Intercepts注解完成插件签名
//告诉当前插件拦截哪个对象的哪个方法 @Intercepts({ @Signature(type = StatementHandler.class,method = "parameterize",args= {java.sql.Statement.class}) }) public class PluginTest implements Interceptor{ @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("PluginTest...intercept方法:"+invocation.getMethod()); //执行目标方法 Object proceed = invocation.proceed(); //返回执行后的结果 return proceed; } @Override public Object plugin(Object target) { System.out.println("PluginTest...plugin:MyBatis将要创建的对象:"+target); //使用MyBatis提供的Plugin类,用当前interceptor包装target,返回包装后的动态代理对象 Object wrap = Plugin.wrap(target, this); return wrap; } //将插件注册时的property属性设置进来 @Override public void setProperties(Properties properties) { System.out.println("插件配置的信息"+properties); } }
-
在全局配置文件中注册插件
<plugins> <plugin interceptor="com.zzf.mybatis.dao.PluginTest"> <property name="user" value="root"/> </plugin> </plugins>
-
在intercept方法中可以:
System.out.println("PluginTest...intercept方法:"+invocation.getMethod()); //可以获取target的元数据 MetaObject metaObject = SystemMetaObject.forObject(invocation.getTarget()); //获取sql参数值 Object value = metaObject.getValue("parameterHandler.parameterObject"); //也可以修改sql参数值 metaObject.setValue("parameterHandler.parameterObject", 5); //执行目标方法 Object proceed = invocation.proceed(); //返回执行后的结果 return proceed;
扩展:MyBatis实用场景
使用PageHelper插件分页
使用步骤:
-
导入相关包pagehelper-x.x.x.jar 和 jsqlparser-0.9.5.jar
-
在MyBatis全局配置文件中配置分页插件
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="p1" value="v1"/> </plugin> </plugins>
-
使用PageHelper提供的方法进行分页
Page<Object> page = PageHelper.startPage(1,3); List<Employee> emps = mapper.getEmps(); int pageNum = page.getPageNum();//得到页码 long total = page.getTotal();//得到总记录数 int pageSize = page.getPageSize();//得到每页记录数
-
可以使用更强大的PageInfo封装返回结果
PageInfo pageInfo = new PageInfo(emps); pageInfo.getPageNum(); ...
批量操作
-
默认的 openSession() 方法没有参数,它会创建有如下特性的
- 会开启一个事务(也就是 不自动提交)
- 连接对象会从由活动环境配置的数据源实例得到。
- 事务隔离级别将会使用驱动或数据源的默认设置。
- 预处理语句不会被复用,也不会批量处理更新。
-
openSession 方法的 ExecutorType 类型的参数,枚举类型:
- ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情(这是默认装配的)。它为每个语句的执行创建一个新的预处理语句
- ExecutorType.REUSE: 这个执行器类型 会复用预处理语句。
- ExecutorType.BATCH: 这个执行器会批量执行所有更新语句
-
批量操作我们是使用MyBatis提供的BatchExecutor进行的,他的底层就是通过jdbc攒sql的方式进行的。我们可以让他攒够一定数量后发给数据库一次
-
与Spring整合中,在applicationContext.xml中额外的配置一个可以专门用来执行批量操作的sqlSession
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:conf/mybatis-config.xml"></property> <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property> </bean> <!-- 另外配置一个sqlSession进行批量操作 --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg> <constructor-arg name="executorType" value="BATCH"></constructor-arg> </bean>
存储过程
MyBatis也支持调用数据库中的存储过程,调用方式:
-
select标签中设置属性statementType=“CALLABLE”
-
标签体中调用语法:
{call 存储过程名(#{参数名,mode=IN,jdbcType=INTEGER},#{...})}
-
MyBatis对存储过程的游标提供了一个 JdbcType=CURSOR的支持,可以智能的把游标读取到的数据,映射到我们声明的结果集中
自定义TypeHandler处理枚举
-
MyBatis在处理枚举类型的参数时,默认保存进数据库的是枚举的名字,全局配置EnumTypeHandler中可以显式设置
-
也可以设置成保存枚举的索引,全局配置 EnumTypeHandler
-
可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略
- 实现TypeHandler接口或者继承BaseTypeHandler
- 使用@MappedTypes定义处理的java类型
使用@MappedJdbcTypes定义jdbcType类型 - 在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理,或者在全局配置TypeHandler要处理的javaType