• mybatis专题(一)-----入门、配置


    Mybatis快速入门

    1、SqlSessionFactoryBuilder:读取配置信 息创建SqlSessionFactory,建造者模式, 方法级别生命周期;

    2、SqlSessionFactory:创建Sqlsession,工 厂单例模式,存在于程序的整个生命周 期;

    3、SqlSession:代表一次数据库连接,可 以直接发送SQL执行,也可以通过调用 Mapper访问数据库;线程不安全,要保 证线程独享(方法级);

    4、SQL Mapper:由一个Java接口和XML文 件组成,包含了要执行的SQL语句和结果 集映射规则。方法级别生命周期;

    Mybatis配置

    1、settings 

    <!-- 参数设置 -->
        <settings>
            <!-- 这个配置使全局的映射器启用或禁用缓存 -->
            <setting name="cacheEnabled" value="true" />
            <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 -->
            <setting name="lazyLoadingEnabled" value="true" />
            <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载 -->
            <setting name="aggressiveLazyLoading" value="true" />
            <!-- 允许或不允许多种结果集从一个单独的语句中返回(需要适合的驱动) -->
            <setting name="multipleResultSetsEnabled" value="true" />
            <!-- 使用列标签代替列名。不同的驱动在这方便表现不同。参考驱动文档或充分测试两种方法来决定所使用的驱动 -->
            <setting name="useColumnLabel" value="true" />
            <!-- 允许JDBC支持生成的键。需要适合的驱动。如果设置为true则这个设置强制生成的键被使用,尽管一些驱动拒绝兼容但仍然有效(比如Derby) -->
            <setting name="useGeneratedKeys" value="true" />
            <!-- 指定MyBatis如何自动映射列到字段/属性。PARTIAL只会自动映射简单,没有嵌套的结果。FULL会自动映射任意复杂的结果(嵌套的或其他情况) -->
            <setting name="autoMappingBehavior" value="PARTIAL" />
            <!--当检测出未知列(或未知属性)时,如何处理,默认情况下没有任何提示,这在测试的时候很不方便,不容易找到错误。 NONE : 不做任何处理 
                (默认值) WARNING : 警告日志形式的详细信息 FAILING : 映射失败,抛出异常和详细信息 -->
            <setting name="autoMappingUnknownColumnBehavior" value="WARNING" />
            <!-- 配置默认的执行器。SIMPLE执行器没有什么特别之处。REUSE执行器重用预处理语句。BATCH执行器重用语句和批量更新 -->
            <setting name="defaultExecutorType" value="SIMPLE" />
            <!-- 设置超时时间,它决定驱动等待一个数据库响应的时间 -->
            <setting name="defaultStatementTimeout" value="25000" />
            <!--设置查询返回值数量,可以被查询数值覆盖 -->
            <setting name="defaultFetchSize" value="100" />
            <!-- 允许在嵌套语句中使用分页 -->
            <setting name="safeRowBoundsEnabled" value="false" />
            <!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 
                的类似映射。 -->
            <setting name="mapUnderscoreToCamelCase" value="false" />
            <!--MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 
                默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 
                的不同调用将不会共享数据。 -->
            <setting name="localCacheScope" value="SESSION" />
            <!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 
                NULL、VARCHAR OTHER。 -->
            <setting name="jdbcTypeForNull" value="OTHER" />
            <!-- 指定哪个对象的方法触发一次延迟加载。 -->
            <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
        </settings>

    2、environments 

    environment 元素是配置一个数据源的开始,属性id是它的唯一标识 

    transactionManager 元素配置数据库事务,其中type属性有三种配置方式

    •   jdbc,采用jdbc的方式管理事务;
    •   managed,采用容器的方式管理事务,在JNDI数据源中使用; 、
    •   自定义,自定义数据库事务管理办法;

    dataSource 元素配置数据源连接信息,type属性是连接数据库的方式配置,有四种配置方式

    •   UNPOOLED 非连接池方式连接
    •   POOLED 使用连接池连接
    •   JNDI 使用JNDI数据源
    •   自定义数据源 
    <!--配置environment环境 -->
        <environments default="development">
            <!-- 环境配置1,每个SqlSessionFactory对应一个环境 -->
            <environment id="development1">
                <!-- 事务配置 type= JDBC、MANAGED 1.JDBC:这个配置直接简单使用了JDBC的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。 
                    2.MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期(比如Spring或JEE应用服务器的上下文)。 
                    默认情况下它会关闭连接。然而一些容器并不希望这样,因此如果你需要从连接中停止它,将closeConnection属性设置为false -->
                <transactionManager type="JDBC" />
                <!-- <transactionManager type="MANAGED"> <property name="closeConnection" 
                    value="false"/> </transactionManager> -->
                <!-- 数据源类型:type = UNPOOLED、POOLED、JNDI 1.UNPOOLED:这个数据源的实现是每次被请求时简单打开和关闭连接。它有一点慢,这是对简单应用程序的一个很好的选择,因为它不需要及时的可用连接。 
                    不同的数据库对这个的表现也是不一样的,所以对某些数据库来说配置数据源并不重要,这个配置也是闲置的 2.POOLED:这是JDBC连接对象的数据源连接池的实现,用来避免创建新的连接实例时必要的初始连接和认证时间。 
                    这是一种当前Web应用程序用来快速响应请求很流行的方法。 3.JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用 -->
                <dataSource type="UNPOOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://localhost:3306/xhm" />
                    <property name="username" value="root" />
                    <property name="password" value="root" />
                    <!-- 默认连接事务隔离级别 <property name="defaultTransactionIsolationLevel" value="" 
                        /> -->
                </dataSource>
            </environment>
    
            <!-- 环境配置2 -->
            <environment id="development2">
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://localhost:3306/xhm" />
                    <property name="username" value="root" />
                    <property name="password" value="root" />
                    <!-- 在任意时间存在的活动(也就是正在使用)连接的数量 -->
                    <property name="poolMaximumActiveConnections" value="10" />
                    <!-- 任意时间存在的空闲连接数 -->
                    <property name="poolMaximumIdleConnections" value="5" />
                    <!-- 在被强制返回之前,池中连接被检查的时间 -->
                    <property name="poolMaximumCheckoutTime" value="20000" />
                    <!-- 这是给连接池一个打印日志状态机会的低层次设置,还有重新尝试获得连接,这些情况下往往需要很长时间(为了避免连接池没有配置时静默失败) -->
                    <property name="poolTimeToWait" value="20000" />
                    <!-- 发送到数据的侦测查询,用来验证连接是否正常工作,并且准备接受请求。 -->
                    <property name="poolPingQuery" value="NO PING QUERY SET" />
                    <!-- 这是开启或禁用侦测查询。如果开启,你必须用一个合法的SQL语句(最好是很快速的)设置poolPingQuery属性 -->
                    <property name="poolPingEnabled" value="false" />
                    <!-- 这是用来配置poolPingQuery多次时间被用一次。这可以被设置匹配标准的数据库连接超时时间,来避免不必要的侦测 -->
                    <property name="poolPingConnectionsNotUsedFor" value="0" />
                </dataSource>
            </environment>
    
            <!-- 环境配置3 -->
            <environment id="development3">
                <transactionManager type="JDBC" />
                <dataSource type="JNDI">
                    <property name="data_source" value="java:comp/env/jndi/mybatis" />
                    <property name="env.encoding" value="UTF8" />
                    <!-- <property name="initial_context" value=""/> <property name="env.encoding" 
                        value="UTF8"/> -->
                </dataSource>
            </environment>
        </environments>

    3、mapper 

    PS:第一种方式用的推荐使用,类文件和mapper文件可以不需要 放在一个文件夹中,xml文件也不会和java文件混合在一起;

    A、用classPath下资源引用-----将mapper.xml文件放在resource文件夹下是常规做法

    <mappers>
       <!--直接映射到相应的mapper文件 -->
       <mapper resource="sqlmapper/TUserMapper.xml" />
    </mappers>

    B、用类注册方式引用-----将mapper.xml文件与Mapper接口放在同一路径下

    <mappers>
      <!—通过类扫描mapper文件 -->
      <mapper class="com.enjoylearning.mybatis.mapper.TUserMapper" />
    </mappers>

    C、使用包名引入引射器名-----与B同理

    <mappers>
      <!—扫描包下所有的mapper文件 -->
      <package name="com.enjoylearning.mybatis.mapper"/>
    </mappers>

    D、用文件的全路径引用

    <!-- 映射文件,mapper的配置文件 -->
        <mappers>
            <!--直接映射到相应的mapper文件 -->
            <mapper resource="com/xhm/mapper/UserMapper.xml" />
            <!--扫描包路径下所有xxMapper.xml文件 -->
            <package name="com.xhm.mapper" />
        </mappers>

    基于xml配置的映射器

    cache – 给定命名空间的缓存配置。

    cache-ref – 其他命名空间缓存配置的引用。

    resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。

    sql – 可被其他语句引用的可重用语句块。

    insert – 映射插入语句

    update – 映射更新语句

    delete – 映射删除语句

    select – 映射查询语句

    select元素

    自动映射

    • 前提:SQL列名和JavaBean的属性是一致的;
    • 自动映射等级autoMappingBehavior设置为PARTIAL,需要谨慎使用FULL;
    • 使用resultType;
    • 如果列名和JavaBean不一致,但列名符合单词下划线分割,Java是驼峰命名法, 则mapUnderscoreToCamelCase可设置为true;

    传递多个查询入参

    • 使用map传递参数;可读性差,导致可维护性和可扩展性差,杜绝使用;
    • 使用注解传递参数;直观明了,当参数较少一般小于5个的时候,建议使用;
    • 使用Java Bean的方式传递参数;当参数大于5个的时候,建议使用; 
         // 第一种方式使用map
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("email", email);
            params.put("sex", sex);
            List<TUser> list1 = mapper.selectByEmailAndSex1(params);
            System.out.println(list1.size());
    
            // 第二种方式直接使用参数
            List<TUser> list2 = mapper.selectByEmailAndSex2(email, sex);
            System.out.println(list2.size());
    
            // 第三种方式用对象
            EmailSexBean esb = new EmailSexBean();
            esb.setEmail(email);
            esb.setSex(sex);
            List<TUser> list3 = mapper.selectByEmailAndSex3(esb);
            System.out.println(list3.size());
       List<TUser> selectByEmailAndSex1(Map<String, Object> param);
        
        List<TUser> selectByEmailAndSex2(@Param("email")String email,@Param("sex")Byte sex);
        
        List<TUser> selectByEmailAndSex3(EmailSexBean esb);

    resultMap元素

    属性

    resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出 来,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。

    ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。

    resultMap元素 子元素 

    constructor - 用于在实例化类时,注入结果到构造方法中

    •   idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    •   arg - 将被注入到构造方法的一个普通结果

    id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

    result – 注入到字段或 JavaBean 属性的普通结果

    association – 一个复杂类型的关联;许多结果将包装成这种类型嵌套结果映射 – 关联可以指定为一个 resultMap 元素,或者引用一个

    collection – 一个复杂类型的集合嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个

    discriminator – 使用结果值来决定使用哪个 resultMap

    •   case – 基于某些值的结果映射嵌套结果映射 – 一个 case 也是一个映射它本身的结果,因此可以包含很多相 同 的元素,或者它可以参照一个外部的 resultMap

    id & result

    • id 和 result 都将一个列的值映射到一个简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段
    • 两者之间的唯一不同是, id 表示的结果将是对象的标识属性,这会在比较对象实例时用到。 这样可以 提高整体的性能,尤其是缓存和嵌套结果映射(也就是联合映射)的时候

    constructor 

    • 一个pojo不存在没有参数的构造方法,就需要使用constructor;
    • 为了通过名称来引用构造方法参数,你可以添加 @Param 注解,指定参数名称的前提下,以任意顺序编写 arg 元素
    <constructor>
      <idArg column="id" javaType="int" />
      <arg column="user_name" javaType="String" />
    </constructor>
    <resultMap id="BaseResultMap" type="TUser">
            
            <!-- <constructor>
                <idArg column="id" javaType="int"/>
                <arg column="user_name" javaType="String"/>
            </constructor>  -->
            <id column="id" property="id" jdbcType="INTEGER" />
            <result column="user_name" property="userName" jdbcType="VARCHAR" />
            <result column="real_name" property="realName" jdbcType="VARCHAR" />
            <result column="sex" property="sex" jdbcType="TINYINT" />
            <result column="mobile" property="mobile" jdbcType="VARCHAR" />
            <result column="email" property="email" jdbcType="VARCHAR" />
            <result column="note" property="note" jdbcType="VARCHAR" />
            <result column="position_id" property="positionId" jdbcType="INTEGER" />
        </resultMap>

    insert, update 和 delete

        <insert id="insert1" parameterType="TUser" useGeneratedKeys="true" keyProperty="id">
            insert into t_user (id, user_name, real_name,
            sex, mobile,
            email,
            note, position_id)
            values (#{id,jdbcType=INTEGER},
            #{userName,jdbcType=VARCHAR},
            #{realName,jdbcType=VARCHAR},
            #{sex,jdbcType=TINYINT}, #{mobile,jdbcType=VARCHAR},
            #{email,jdbcType=VARCHAR},
            #{note,jdbcType=VARCHAR},
            #{positionId,jdbcType=INTEGER})
        </insert>

    selectKey元素 

    <selectKey keyProperty=“id” order= " Before" resultType="int">
      select SEQ_ID.nextval from dual
    </selectKey>

    sql元素和参数

    sql元素:用来定义可重用的 SQL 代码段,可以包含在其他语句中;

    参数:向sql语句中传递的可变参数

    •   预编译 #{}:将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,能够很大程度防止 sql注入;
    •   传值 ${}:传入的数据直接显示生成在sql中,无法防止sql注入;
    •   表名、选取的列是动态的,order by和in操作, 可以考虑使用$ 
        <select id="selectBySymbol" resultMap="BaseResultMap">
            select 
            #{inCol} 
            from ${tableName} a
            where a.sex = #{sex}
            order by ${orderStr} 
        </select>

    动态sql元素

    注解方式配置

    注解方式就是将SQL语句直接写在接口上,对于需求比较简单的系统,效率较高。缺点在于,每次修改sql语句 都要编译代码,对于复杂的sql语句可编辑性和可读性都差,一般不建议使用这种配置方式;

    @Select

    @Results

    @Insert

    @Update

    @Delete

        @Results(id="jobInfo",value={
                @Result(property="id",column="id",id = true),
                @Result(property="userId",column="user_id"),
                @Result(property="compName",column="comp_name"),
                @Result(property="years",column="years"),
                @Result(property="title",column="title")
        })
        @Select("select id, user_id, comp_name, years, title from t_job_history"
                + "    where user_id = #{userId}")
        List<TJobHistory> selectByUserId(int userId);
        
        @ResultMap("jobInfo")
        @Select("select id, user_id, comp_name, years, title from t_job_history")
        List<TJobHistory> selectAll();
        
        @Insert("insert into t_job_history (id, user_id, comp_name,    years, title)"
                + "    values (#{id,jdbcType=INTEGER}, #{userId,jdbcType=INTEGER},"
                + "#{compName,jdbcType=VARCHAR},"
                + "#{years,jdbcType=INTEGER}, #{title,jdbcType=VARCHAR})")
        @Options(useGeneratedKeys=true,keyProperty="id")
        int insert(TJobHistory record);

    批量操作

    • 通过foreach动态拼装SQL语句
    • 使用BATCH类型的excutor

    例一:

    <sql id="Base_Column_List">
        id, user_name, real_name, sex, mobile, email, note, position_id
    </sql>
    <select id="selectIfandWhereOper" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
            from t_user a
        <where>
            <if test="email != null and email != ''">
                and a.email like CONCAT('%', #{email}, '%') 
            </if>
            <if test="sex != null ">
                and a.sex = #{sex}
            </if>
        </where>
    </select>

    例二:

    <select id="selectForeach4In" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from t_user a
        where a.user_name in 
        <foreach collection="array" open="(" close=")" item="userName" index="i" separator=",">
            #{userName}
        </foreach>    
    </select>
        
    <insert id="insertForeach4Batch">
        insert into t_user (user_name, real_name, sex, mobile,email,note, position_id) 
        values
        <foreach collection="list" separator="," item="user">
            (
                #{user.userName,jdbcType=VARCHAR},
                #{user.realName,jdbcType=VARCHAR},
                #{user.sex,jdbcType=TINYINT}, 
                #{user.mobile,jdbcType=VARCHAR},
                #{user.email,jdbcType=VARCHAR},
                #{user.note,jdbcType=VARCHAR},
                #{user.positionId,jdbcType=INTEGER}
            )
        </foreach>
    </insert>

    例三:

    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
    SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
    //可以执行批量操作的sqlSession
    SqlSession openSession=sqlSessionFactory.openSession(ExecutorType.BATCH);
            
    //批量保存执行前时间
    long start=System.currentTimeMillis();
    try{
        EmployeeMapper mapper=    openSession.getMapper(EmployeeMapper.class);
        for (int i = 0; i < 1000; i++) {
            mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0,5),"b","1"));
        }    
            
        openSession.commit();
        long end=  System.currentTimeMillis();
        //批量保存执行后的时间
        System.out.println("执行时长"+(end-start));
        //批量 预编译sql一次==》设置参数==》10000次==》执行1次   677
        //非批量  (预编译=设置参数=执行 )==》10000次   1121
    }finally{
        openSession.close();
    }

    mapper.xml

    <mapper namespace="com.agesun.mybatis.dao.EmployeeMapper"
         <!--批量保存员工 -->
        <insert id="addEmp">
            insert into employee(lastName,email,gender)
            values(#{lastName},#{email},#{gender})
        </insert>
    </mapper>

    补充:

    在SSM框架中使用batch方式的批量插入

    1、在全局配置文件applcationContext.xml中加入

    <!-- 配置一个可以批量执行的sqlSession -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
        <constructor-arg name="executorType" value="BATCH"></constructor-arg>
    </bean>

    2、在serviceImpl中加入 

    @Autowired
    private SqlSession sqlSession;
    
    //批量保存员工
    @Override
    public Integer batchEmp() {
    
        //批量保存执行前时间
        long start=System.currentTimeMillis();
        
        EmployeeMapper mapper=    sqlSession.getMapper(EmployeeMapper.class);
        for (int i = 0; i < 10000; i++) {
            mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0,5),"b","1"));
        }
        
        long end=  System.currentTimeMillis();
        long time2= end-start;
        //批量保存执行后的时间
        System.out.println("执行时长"+time2);
        return (int) time2;    
    }

    总结:性能对比

    插入通常有三种方式,分别为for循环插入、batch插入、foreach插入,性能总结如下:

    1.for循环insert

            long start = System.currentTimeMillis();
            for(int i = 0 ;i < 100000; i++) {
                User user = new User();
                user.setId("id" + i);
                user.setName("name" + i);
                user.setPassword("password" + i);
                userMapper.insert(user);
            }
            long end = System.currentTimeMillis();
            System.out.println("---------------" + (start - end) + "---------------");
        <insert id="insert">
          INSERT INTO t_user (id, name, password)
              VALUES(#{id}, #{name}, #{password})
        </insert>

    时间为380826ms

    2、Mybatis batch模式

            SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);//跟上述sql区别
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                User user = new User();
                user.setId("id" + i);
                user.setName("name" + i);
                user.setPassword("password" + i);
                userMapper.insert(user);
            }
            sqlSession.commit();
            long end = System.currentTimeMillis();
            System.out.println("---------------" + (start - end) + "---------------");
        <insert id="insert">
          INSERT INTO t_user (id, name, password)
              VALUES(#{id}, #{name}, #{password})
        </insert>

    时间为203660ms

    3、批量foreach插入 

          long start = System.currentTimeMillis();
            List<User> userList = new ArrayList<>();
            for (int i = 0; i < 100000; i++) {
                User user = new User();
                user.setId("id" + i);
                user.setName("name" + i);
                user.setPassword("password" + i);
                userMapper.insert(user);
            }
            userMapper.insertBatch(userList);
            long end = System.currentTimeMillis();
            System.out.println("---------------" + (start - end) + "---------------");
    <insert id="insertBatch">
        INSERT INTO t_user
        (id, name, password)
        VALUES
        <foreach collection ="userList" item="user" separator =",">
            (#{id}, #{name}, #{password})
        </foreach >
    </insert>

    时间为8706ms

    结论:foreach批量插入 > mybatis batch模式插入 > for循环insert

  • 相关阅读:
    磊哥评测之数据库:腾讯云MongoDB vs自建
    一文看透浏览器架构
    必看!如何让你的LBS服务性能提升十倍!
    亿级曝光品牌视频的幕后设定
    Node 框架接入 ELK 实践总结
    大数据与 AI 生态中的开源技术总结
    数据库分片(Database Sharding)详解
    QQ音乐的动效歌词是如何实践的?
    Sql Server之旅——第九站 看公司这些DBA们设计的这些复合索引
    Sql Server之旅——第八站 复合索引和include索引到底有多大区别?
  • 原文地址:https://www.cnblogs.com/alimayun/p/12288816.html
Copyright © 2020-2023  润新知