MyBatis3笔记

Hello World

  1. 创建数据表
  2. 引入相关jar包
  3. 创建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>
  1. 测试类

    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();
    		}
    	}
    
    }
    
  2. 创建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

  1. 创建dao-EmployeeDao接口,编写抽象方法

  2. 修改sql映射文件的命名空间为该接口的全路径,id改为该抽象方法

  3. 编写测试

    @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

  1. 一个SqlSession即一次数据库会话,用完必须关闭
  2. 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标签

  1. 逐个注册SQL映射文件

    mapper的属性:

    • resource属性:类路径下的文件
    • url属性:网络或磁盘路径下的文件
    • class属性:接口全类名,直接引用接口,要求:
      • 方式一:映射文件和接口同名并在同一个包下
      • 方式二:不用映射文件,直接注解(@Select(“sql语句”))修饰接口中的方法(不推荐,只用于简单的Dao接口)
    <mappers>
        <!-- 将写好的sql映射文件配置到全局配置文件中 -->
        <mapper resource="EmployeeMapper.xml" />
    </mappers>
    
  2. 批量注册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>

获取自增主键

  1. 对于支持自增主键的数据库:

    <insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
    </insert>
    
  2. 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)

参数传递

  1. 单个参数:MyBatis不做特殊处理

    #{参数名}直接取值,参数名任意

  2. 多个参数:MyBatis做特殊处理,将参数封装成map,key为索引0到n或param1到paramN,value为参数值

    #{索引值/paramN}

    问题:该key无法做到见名知义----使用命名参数

  3. 命名参数,@Param(“自定义map的key名”)修饰方法中的形参,就可以使用#{自定义key}获取参数值

  4. POJO参数:若传递参数即bean类,直接把该类作为参数

    #{属性名}

  5. Map参数:若传递参数不是现有的POJO,也可以使用Map作为参数

    #{key名}

  6. 特别的:Collection参数(包括List、Set)或数组,则key为collection、list、array

参数处理

#{ }与${ }的区别

相同点:二者都可以获取map中的值或pojo属性的值,

区别:#{}是以预编译形式将参数设置到sql中; 将 参 数 直 接 拼 在 s q l 语 句 中 , 有 s q l 注 入 问 题 , 在 原 生 j d b c 不 支 持 占 位 符 的 地 方 就 可 以 使 用 {}将参数直接拼在sql语句中,有sql注入问题,在原生jdbc不支持占位符的地方就可以使用 sqlsqljdbc使{}进行拼串,如表名、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 中配置
  • 一级缓存失效的情况:
    1. sqlSession不同,即不是同一次数据库会话
    2. sqlSession相同,查询条件不同,即当前缓存没有该数据
    3. 两次相同查询间执行了增删改操作,即该操作可能影响了查询的数据
    4. 缓存被清空----clearCache()

二级缓存(全局缓存)

  • 基于namespace级别的缓存,一个namespace对应一个二级缓存

  • 当会话关闭,该一级缓存的数据才会保存到二级缓存中

  • 使用

    1. 全局配置文件中开启全局二级缓存:

    2. sql映射文件中配置二级缓存:

      cache中的属性:

      ​ eviction:回收策略,值为:LRU(默认)、FIFO等

      ​ flushInterval:缓存刷新间隔时间,单位毫秒

      ​ readOnly:是否只读

      ​ size:最大存储的对象数量

      <cache></cache>
      
    3. POJO类必须实现序列化接口

缓存的一些设置及属性

  • 全局配置文件的cacheEnable属性:配置二级缓存的开关

  • 映射文件的userCache属性:配置该select的二级缓存的开关

  • 映射文件中增删改标签的flushCache属性:配置是否清空一二级缓存(默认true)

  • sqlSession.clearCache():只是用来清除一级缓存

  • localCacheScope:本地缓存作用域(一级缓存):SESSION/STATEMENT

整合第三方缓存

  • MyBatis自带的缓存管理简陋,可以使用第三方缓存(Redis、EhCache)

  • EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

  • MyBatis定义了Cache接口方便我们进行自定义扩展

  • 步骤:

    1. 导入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

    2. 编写ehcache.xml配置文件

    3. 映射文件中配置cache标签

      <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
      
    4. 参照缓存:在命名空间中共享相同的缓存配置和实例

      <cache-ref namespace="另一个有配置缓存的mapper的全类名" />
      

SSM整合(练习)

  1. 导入相关jar包(spring-springmvc-mybatis-数据库驱动)

  2. MyBatis配置文件:mybatis-config.xml

  3. 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>
    
  4. 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>
    
    
  5. 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();
}
  1. 获取sqlSessionFactory对象

    解析配置文件的信息保存在Configuration对象中,返回包含Configuration的DefaultSqlSessionFactory对象,其中一个MapperStatement就代表一个增删改查标签的信息
    在这里插入图片描述

  2. 获取sqlSession对象

    Executor是四大对象之一

    在这里插入图片描述

  3. 获取接口的实现类对象

    getMapper使用MapperProxyFactory创建了一个MapperProxy代理对象,该代理对象中封装了DefaultSqlSession(Executor)

    在这里插入图片描述

  4. 执行增删改查方法

    在这里插入图片描述

    原理总结:了解四大对象的作用

    在这里插入图片描述

    	 * 总结:
    	 * 	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);
    

插件开发

插件原理

在四大对象创建时:

  1. 每个创建的对象不是直接返回,而是先调用interceptorChain.pluginAll(parameterHandler);

  2. pluginAll获取所有的Interceptor(拦截器)(插件需要实现的接口)

    调用interceptor.plugin(target);返回target包装后的对象

  3. 插件机制:我们可以使用插件为目标对象创建一个代理对象(AOP面向切面)

    插件为四大对象创建代理对象,代理对象就可以拦截四大对象的每一个执行方法

  4. MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。

  5. 默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

    • Executor (update, query, flushStatements, commit, rollback,getTransaction, close, isClosed)
    • ParameterHandler (getParameterObject, setParameters)
    • ResultSetHandler (handleResultSets, handleOutputParameters)
    • StatementHandler (prepare, parameterize, batch, update,query)

插件原理:

  1. 按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理
  2. 多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链
  3. 目标方法执行时依次从外到内执行插件的intercept方法。
  4. 多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值

在这里插入图片描述

插件开发

步骤:

  1. 编写插件实现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);
    	}
    }
    
  2. 在全局配置文件中注册插件

    <plugins>
        <plugin interceptor="com.zzf.mybatis.dao.PluginTest">
            <property name="user" value="root"/>
        </plugin>
    </plugins>
    
  3. 在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插件分页

使用步骤:

  1. 导入相关包pagehelper-x.x.x.jar 和 jsqlparser-0.9.5.jar

  2. 在MyBatis全局配置文件中配置分页插件

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="p1" value="v1"/>
        </plugin>
    </plugins>
    
  3. 使用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();//得到每页记录数
    
  4. 可以使用更强大的PageInfo封装返回结果

    PageInfo pageInfo = new PageInfo(emps);
    pageInfo.getPageNum();
    ...
    

批量操作

  1. 默认的 openSession() 方法没有参数,它会创建有如下特性的

    • 会开启一个事务(也就是 不自动提交)
    • 连接对象会从由活动环境配置的数据源实例得到。
    • 事务隔离级别将会使用驱动或数据源的默认设置。
    • 预处理语句不会被复用,也不会批量处理更新。
  2. openSession 方法的 ExecutorType 类型的参数,枚举类型:

    • ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情(这是默认装配的)。它为每个语句的执行创建一个新的预处理语句
    • ExecutorType.REUSE: 这个执行器类型 会复用预处理语句。
    • ExecutorType.BATCH: 这个执行器会批量执行所有更新语句
  3. 批量操作我们是使用MyBatis提供的BatchExecutor进行的,他的底层就是通过jdbc攒sql的方式进行的。我们可以让他攒够一定数量后发给数据库一次

  4. 与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也支持调用数据库中的存储过程,调用方式:

  1. select标签中设置属性statementType=“CALLABLE”

  2. 标签体中调用语法:

    {call 存储过程名(#{参数名,mode=IN,jdbcType=INTEGER},#{...})}
    
  3. MyBatis对存储过程的游标提供了一个 JdbcType=CURSOR的支持,可以智能的把游标读取到的数据,映射到我们声明的结果集中

    在这里插入图片描述

    在这里插入图片描述

自定义TypeHandler处理枚举

  1. MyBatis在处理枚举类型的参数时,默认保存进数据库的是枚举的名字,全局配置EnumTypeHandler中可以显式设置

    在这里插入图片描述

  2. 也可以设置成保存枚举的索引,全局配置 EnumTypeHandler

    在这里插入图片描述

  3. 可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略

    1. 实现TypeHandler接口或者继承BaseTypeHandler
    2. 使用@MappedTypes定义处理的java类型
      使用@MappedJdbcTypes定义jdbcType类型
    3. 在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理,或者在全局配置TypeHandler要处理的javaType

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值