Mybatis 使用入门

耗尽温柔 提交于 2020-03-08 10:14:14

什么 mybatis

MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架;
Mybatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。Mybatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

优点

1、简单易学

mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现

2、灵活

mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。

3、解除sql与程序代码的耦合

通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

4、提供映射标签,支持对象与数据库的orm字段关系映射

5、提供对象关系映射标签,支持对象关系组建维护

6、提供xml标签,支持编写动态sql。

缺点

1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。

2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。

3、框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。

4、二级缓存机制不佳

Mybatis框架认知

mybatis所需要的映射文件包含三部分:
SQL
映射规则
POJO

mybatis《—- pojo—->注解 SQL_Mapper —->数据
应用程序—->对象《—- 接口 xml映射文件 《—-库

mybatis的基本构成

SqlSessionFactoryBuilder(构造器):他会根据配置信息或者代码来生成SqlSessionFactory(工厂接口).
.SqlSessionFactory:依靠工厂来生成SqlSession(会话).
.SqlSession:是一个既可以发送SQL去执行并返回结果,也可以获取Mapper接口.
.Sql Mapper:他是MyBatis新设计的组件,他是由一个java接口和xml文件(或者注解)构成的.需要给出对应的SQL和映射规则,
他负责发送SQL去执行,并返回执行结果.

简单的mybatis使用

1.导入jar(maven)

<!--单元测试-->
 <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    
    <!--日志-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.12</version>
    </dependency>
    
    <!--mybatis-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>
    
     <!--mysql-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
    
     <!--代码简化器-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
    </dependency>

主配置文件

<?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"
        >
<!--mybatis的主要配置文件-->
<configuration>
     <properties resource="database.properties"/>
    <settings>
        <!-- resultMap自动映射级别(在主配置文件中进行配置)
         1.NONE禁止自动匹配
         2.PARTIAL默认自动匹配,不包括内部嵌套
         3.FULL自动匹配所有
         -->
        <setting name="autoMappingBehavior" value="PARTIAL"/>
        <!--开启事务日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true" />
        <!--        开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"></setting>
        <!--        关闭立即加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!--自动映射-->
        <setting name="autoMappingBehavior" value="PARTIAL"/>
    </settings>
     <typeAliases>
        <package name="com.jbit.pojo"/>
    </typeAliases>
    
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
        </plugin>
    </plugins>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源-->
            <dataSource type="POOLED">
               <!--连接数据库的四个基本信息-->
                <property name="driver"   value="${driver}"/>
                <property name="url"      value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
    <mappers>
        <mapper class="com.jbit.dao.UserDao" />
    </mappers>
</configuration>

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="全限定类名">
</mapper>

静态资源过滤(pom)

<!--获取java xml 配置文件 -->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

日志文件

log4j.rootLogger=DEBUG,CONSOLE,file
#log4j.rootLogger=ERROR,ROLLING_FILE
log4j.logger.cn.smbms.dao=debug
log4j.logger.com.ibatis=debug 
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug 
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug 
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug 
log4j.logger.java.sql.Connection=debug 
log4j.logger.java.sql.Statement=debug 
log4j.logger.java.sql.PreparedStatement=debug 
log4j.logger.java.sql.ResultSet=debug 
log4j.logger.org.tuckey.web.filters.urlrewrite.UrlRewriteFilter=debug

######################################################################################
# Console Appender  \u65e5\u5fd7\u5728\u63a7\u5236\u8f93\u51fa\u914d\u7f6e
######################################################################################
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=error
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern= [%p] %d %c - %m%n

######################################################################################
# DailyRolling File  \u6bcf\u5929\u4ea7\u751f\u4e00\u4e2a\u65e5\u5fd7\u6587\u4ef6\uff0c\u6587\u4ef6\u540d\u683c\u5f0f:log2009-09-11
######################################################################################
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern=yyyy-MM-dd
log4j.appender.file.File=log.log
log4j.appender.file.Append=true
log4j.appender.file.Threshold=error
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n
log4j.logger.com.opensymphony.xwork2=error  

数据库配置

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/smbms
useUnicode=true&characterEncoding=utf-8
username=root
password=1234

动态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">
  <!--namespace 全接口类名-->
<mapper namespace="com.jbit.dao.EasybuyNewsDao">
    <select id="fillA" resultType="EasybuyNews" parameterType="map">
        select  * from easybuy_news
/*where 至少有一个子元素的情况下使用 会根据条件智能的去除 and 或or */
        <where>
        <choose>
            <when test="title != null">
               id = #{title}
            </when>
            <otherwise>
               and 1=1
            </otherwise>
        </choose>
        </where>
    </select>
   
    <update id="Update" parameterType="map">
        update easybuy_news
        <set >
 /*  include 根据 refid 来获取sql片段 id = if的sql片段 */
           <include refid="if"/>
        </set>
            where id = #{id};
    </update>
     <sql id="if">
  /* if 根据 test 表达式的返回结果 判断是否执行包含的条件 */
        <if test="title != null">
             title = #{title},
         </if>
        <if test="content != null">
            content = #{content}
        </if>
    </sql>
    
    <select id="fillAll" parameterType="map" resultType="EasybuyNews">
         select  * from easybuy_news
         <where>
             <foreach collection="ids" index="index" item="id" open="id in(" close=")" separator=",">
                  #{id}
             </foreach>
         </where>
    </select>
     <!-- 一次遍历多个条件 collection(被遍历的) item(每次遍历得到的)  open -->
<foreach collection="ids" index="index" item="id" open="id in(" close=")" separator=",">
</mapper>

resultMap 映射

 <!--自动映射
 PARTIAL 非嵌套 (默认开启)
 NONE 不自动映射
 FULL  所有 resultMap 都映射 
-->    
<setting name="autoMappingBehavior" value="PARTIAL"/>

基本resultMap

 <select id="allUser" resultMap="userall">
        select * from smbms.smbms_user;
    </select>
<!-- java对象属性 和表字段的 映射  -->
    <resultMap id="userall" type="smbmsUser">
        <id property="id" column="id"/>
    </resultMap>
​	  第一个id:代表通过这个id值可以找到这个map

​			  第二个id:代表主键

​			 result:代表非主键

​			 property:代表java中的属性名

​			 column:代表数据库的字段名

​			 javaType:java属性的类型

​			 jdbcType:数据库字段的类型 并且只能是大写

一对一 resultMap

<select id="userRoleAddressAll" resultMap="All">
      SELECT u.id ,u.userName,r.roleName   FROM smbms_user u , smbms_role r
       WHERE u.userRole=r.id
    </select>
<!-- 复杂类型的查询-->
    <resultMap id="All" type="userRoleAddress">
        <!-- association 一对一 复杂类型的 字段映射  property对应的属性名 javaType 属性类型-->
        <association property="smbmsUser" javaType="smbmsUser">
            <!-- id 效率更高 一般主键使用 property 对应属性名column对应的表列名-->
           <id property="id" column="id"/>
            <result property="userName" column="userName"/>
            <result property="userRole" column="userRole"/>
        </association>
     <association property="smbmsRole" javaType="smbmsRole">
      <id property="id" column="id"/>
            <result property="roleName" column="roleName"/>
        </association>
    </resultMap>

一对多resultMap

<select id="userAddressAll" resultMap="userAddressAll" parameterType="int">
    SELECT u.id,u.userName,a.addressDesc FROM
    smbms_user AS u,
    smbms_address AS a
    WHERE u.id = a.userId
    <if test="id != null">
        and u.id = #{id}
    </if>
</select>
<!--一对多-->
    <resultMap id="userAddressAll" type="smbmsUser">
      <id property="id" column="id"/>
         <!-- collection(集合)  ofType(集合的泛型) -->
         <!--嵌入 一个resultMap -->
        <collection property="smbmsAddress" ofType="smbmsAddress"  resultMap="addressMap">
        </collection>
    </resultMap>
 <!--复用型 resultMap-->
    <resultMap id="addressMap" type="smbmsAddress">
        <id property="id" column="id"/>
        <result property="addressDesc" column="addressDesc"/>
    </resultMap>

逆向工程

  1. 导入依赖
<dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.2</version>
 </dependency>

2.自动生成代码插件

  <build>
       <plugins>
           <!-- mybatis generator 自动生成代码插件 -->
           <plugin>
               <groupId>org.mybatis.generator</groupId>
               <artifactId>mybatis-generator-maven-plugin</artifactId>
               <version>1.3.2</version>
               <configuration>
                   <!-- 在src/main/resources 目录下创建generator文件夹并在其中建立名为
                    generatorConfig.xml的配置文件,作为mybatis-generator-maven-plugin 插件的执行目标-->
                   <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
                   <overwrite>true</overwrite>
                   <verbose>true</verbose>
               </configuration>
           </plugin>
       </plugins>
   </build>

3.编写配置文件
generatorConfig.xml

<?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>
    <!--导入属性配置-->
    <properties resource="database.properties"/>
    <!-- 数据库驱动:选择你的本地硬盘上面的数据库驱动包-->
    <classPathEntry location="${driverLocation}"/>

    <context id="DB2Tables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="${driver}"
                        connectionURL="${url}"
                        userId="${username}"
                        password="${password}">
        </jdbcConnection>
        <!-- Oracle数据库
            <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
                connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg"
                userId="yycg"
                password="yycg">
            </jdbcConnection>
        -->

        <!-- 默认为false,把JDBC DECIMAL 和NUMERIC类型解析为Integer,为true时
        把JDBC DECIMAL 和NUMERIC类型解析为java.math.BigDecimal -->
        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成POJO类的位置 -->
        <javaModelGenerator targetPackage="com.sl.demo.pojo" targetProject=".\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.sl.demo.mapper"  targetProject=".\src\main\resources">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>

        <!-- targetProject:mapper接口生成的的位置 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.sl.demo.mapper"  targetProject=".\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <!-- 指定数据表 -->
        <table  tableName="student" domainObjectName="Student"></table>
        <table  tableName="grend" domainObjectName="Grend"></table>
        
        <!-- 有些表的字段需要指定java类型
        <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
          <property name="useActualColumnNames" value="true"/>
          <generatedKey column="ID" sqlStatement="DB2" identity="true" />
          <columnOverride column="DATE_FIELD" property="startDate" />
          <ignoreColumn column="FRED" />
          <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
        </table> -->
    </context>
</generatorConfiguration>

分页

1.导入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.2<ersion>
</dependency>
       //1.使用Mapper接口方式的调用,推荐这种使用
       Page<Object> objects = PageHelper.startPage(2, 3);
       //2.Mapper接口方式的调用,推荐这种使用。
       PageHelper.offsetPage(1, 10);
       //3. Lambda
Page<SmbmsProvider> page = 
PageHelper.startPage(2,3).doSelectPage(() -> mapper.allSmbmsProvider());

缓存机制

一级缓存 默认开启 :applaction
Mybatis自带二级缓存: [ 同一个namespace ]生成的mapper对象
回顾: namespace的值 就是接口的全类名(包名.类名),通过接口可以产生代理对象(studentMapper对象)
-->namespace决定了studentMapper对象的产生
结论:只要产生的xxxMapper对象来自于同-一个namespace,则这些对象共享二级缓存。
注意:二级缓存的范围是同一个namespace,如果有多个xxMapper. xml的namespace值相同,则通过这些
禁用: select标签中useCache=" false"
清理:与清理一级缓存的方法相同,
1.commit(); (一般执行增删改时 会清理掉缓存;设计的原因是为了防止脏数据)
在二级缓存中,commit()不能是查询自身的commit。
commit会清理一级和二 级缓存;但是清理二级缓存时,不能是查询自身的commit;
2.在select标签中增加属性|flushCache= "true" 清理此sql的缓存
第三方 或者自定义 2级缓存 必须实现 org.apache.ibatis.cache 接口

延迟加载

<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--关闭立即加载-->
<setting name="aggressiveLazyLoading" value=" false"/>
</settings>
//通过 select 引入 其他mapper 的sql 开启懒加载
//当需要时才 执行 sql
<association property="grend" javaType="grend" select="com.sl.demo.mapper.GrendMapper.allGrend" column="grendId">
          <!--  <id property="id" column="id"/>
            <result property="name" column="name"/>-->
        </association>

自定义mybatis类型处理器

配置转换器(主配置文件中)
handler:代表映射转换类型的类(自定义配置帮助类) javaType:java的类型 jdbcType:数据库的类型

 <typeHandlers>
 <typeHandler handler="org.hbz.converter.BooleanAndInt" javaType="boolean" jdbcType="Integer"></typeHandler>
    </typeHandlers>
​				java-数据库(jdbc类型)
实例:
实体类Student     boolean  stuSex    true:男    false :女
数据库 student    int    stuSex			1:男		0::女
自定义类型转换器(boolean-int) 步骤:
​	(1) 创建转换器
​				需要实现TypeHandler接口
​				通过阅读源码发现BaseTypeHandler是实现类  因此实现类型转换器有两种选择
​				i:实现TypeHandler
​				ii:继承BaseTypeHandler
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class BooleanAndInt extends BaseTypeHandler<Boolean> {

    //set:java-->数据库

    /**
     *
     * @param preparedStatement PreparedStatement对象
     * @param i PreparedStatement对象操作参数的位置
     * @param o java值
     * @param jdbcType jdbc操作数据库的值
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Boolean o, JdbcType jdbcType) throws SQLException {
                if (o){//true编程1
                    preparedStatement.setInt(i,1);
                }else{//false就是0
                    preparedStatement.setInt(i,0);
                }
    }
    //get:数据库-->java

    /**
     *
     * @param resultSet 数据集
     * @param s 数据库列名
     * @return 返回转换后的值
     * @throws SQLException
     */
    @Override
    public Boolean getNullableResult(ResultSet resultSet, String s) throws SQLException {
        int bool=resultSet.getInt(s);
        return (bool==1?true:false);
    }
    //get:数据库-->java

    /**
     *
     * @param resultSet 数据集
     * @param i 下标
     * @return 转换后的数据
     * @throws SQLException
     */
    @Override
    public Boolean getNullableResult(ResultSet resultSet, int i) throws SQLException {
        int bool=resultSet.getInt(i);
        return (bool==1?true:false);
    }
    //get:数据库-->java

    /**
     *
     * @param callableStatement 存储过程对象
     * @param i 下标
     * @return 转换后的值
     * @throws SQLException
     */
    @Override
    public Boolean getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        int bool=callableStatement.getInt(i);
        return (bool==1?true:false);
    }
}

返回值 resultMap和resultType的区别

​	情况1:如果类中的属性类型和表中的字段类型能够合理的被识别(String-varchar) ,则使用resultType,否则使用resultMap

​	情况2:如果类中的属名称和表中的字段名称能够合理的被识别(stuno-stuno) ,则使用resultType,否则使用resultMap

两种取值符号

${} 和 #{}的区别
​		(1)#{任意值}
​			${value},其中的标识符只能是value 
​		(2)#{}自动给String类加上‘ ’ (自动类型转换)
​		    ${}原样输出,但是适用于 动态排序(动态字段)
     如果使用#{}会自动的添加‘ ‘号 就不符合数据库的语句
​			(3) #{}可以防止sql注入
​				${}不防止		

动态排序的实例:

<!--    动态排序-->
    <select id="selectStudentByOrderBy" parameterType="string" resultType="student">
        select * from student order by ${value} desc
    </select>

注解开发

​ 推荐使用xml开发(而注解开发适用于一些小项目)
1.配置主文件的mapper标签 通过class来映射接口

	<mapper class="org.sl.dao.IStudentDao"></mapper>
无论是注解还是xml都可以可以一劳永逸的方式直接填写包名存在哪里  (批量引入)
 <package name="org.sl.dao"/>

2.将sql语句写在接口的方法的上面

 @Select("select * from student where stuNo=#{stuNo}")
    Student queryStudentByNo(int stuNo);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!