zoukankan      html  css  js  c++  java
  • MyBatis入门

    ibatis SqlMapConfig.xml 配置settings

    ibatis SqlMapConfig.xml <settings>配置
    <settings 
    cacheModelsEnabled="true" 
    enhancementEnabled="true" 
    lazyLoadingEnabled="true" 
    maxRequests="32" 
    maxSessions="10" 
    maxTransactions="5" 
    useStatementNamespaces="false" 
    />

    maxRequests
    同时执行 SQL 语句的最大线程数。大于这个值的线程将阻塞直到另一个线程执行完成。不同的 DBMS有不同的限制值,但任何数据库都有这些限制。通常这个值应该至少是maxTransactions(参见以下)的 10 倍,并且总 是大于 maxSessions 和maxTranactions。减小这个参数值通常能提高性能。

    例如:maxRequests=“256” 
    缺省值:512

    maxSessions
    同一时间内活动的最大 session 数。一个 session 可以maxSessions是代码请求的显式 session,也可以是当线程使用SqlMapClient 实例(即执行一条语句)自动获得的session。它应该总是大于或等于 maxTransactions 并小于 maxRequests。减小这个参数值通常能减少内存使用。

    例如:maxSessions=“64” 
    缺省值:128

    maxTransactions
    同时进入 SqlMapClient.startTransaction()的最大线程maxTransactions 数。大于这个值的线程将阻塞直到另一个线程退出。不同的 DBMS 有不同的限制值,但任何数据库都有这些限制。这个参数值应该总是小于或等于maxSessions 并总是远远小于 maxRequests。减小这个参数值通常能提高性能。

    例如:maxTransactions=“16” 
    缺省值:32

    cacheModelsEnabled
    全局性地启用或禁用 SqlMapClient 的所有缓存cacheModelsEnabled model。调试程序时使用。

    例如:cacheModelsEnabled=“true” 
    缺省值:true(启用)

    lazyLoadingEnabled
    全局性地启用或禁用SqlMapClient的所有延迟加载。lazyLoadingEnabled 调试程序时使用。 
    例子:lazyLoadingEnabled=“true” 
    缺省值:true(启用)

    enhancementEnabled
    全局性地启用或禁用运行时字节码增强,以优化访enhancementEnabled 
    问Java Bean属性的性能,同时优化延迟加载的性能。

    例子:enhancementEnabled=“true” 
    缺省值:false(禁用)

    useStatementNamespaces
    如果启用本属性,必须使用全限定名来引用 mapped useStatementNamespaces 
    statement。Mapped statement 的全限定名由 sql-map 的名称和 mapped-statement 的名称合成。例如: queryForObject("sqlMapName.statementName");

    例如:useStatementNamespaces=“false” 
    缺省值:false(禁用)

     

    为什么要使用<![CDATA[ ... ]]>?

    上面的配置文件中,大家一定注意到了一个细节,就是SQL语句用<![CDATA[ ... ]]>这对标签包含起来了,那么为什么要这么做呢?不妨把上面内容稍微修改一下:

    <?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.xrq.StudentMapper">
        <select id="selectStudentById" parameterType="int" resultType="Student">
            select * from student where studentId = #{id} or studentAge < 10 or studentAge > 20;
        </select>
    </mapper>

    当然这句SQL语句没有任何含义,只是瞎写的演示用而已,运行一下看一下结果:

    Exception in thread "main" java.lang.ExceptionInInitializerError
        at com.xrq.test.MyBatisTest.main(MyBatisTest.java:9)
    Caused by: org.apache.ibatis.exceptions.PersistenceException: 
    ### Error building SqlSession.
    ### The error may exist in student.xml
    ...

    后面的异常信息就不列了。按理说很正常的一句SQL语句,怎么会报错呢?仔细想来,错误的根本原因就是student.xml本身是一个xml文件,它并不是专门为MyBatis服务的,它首先具有xml文件的语法。因此,"< 10 or studentAge >"这段,会先被解析为xml的标签,xml哪有这种形式的标签的?所以当然报错了。

    所以,使用<![CDATA[ ... ]]>,它可以保证如论如何<![CDATA[ ... ]]>里面的内容都会被解析成SQL语句。因此,建议每一条SQL语句都使用<![CDATA[ ... ]]>包含起来,这也是一种规避错误的做法。

    select

    SQL映射中有几个顶级元素,其中最常见的四个就是insert、delete、update、select,分别对应于增、删、改、查,下面先对于select元素进行学习。

    1、多条件查询查一个结果

    前面的select语句只有一个条件,下面看一下多条件查询如何做,首先是student.xml:

    <select id="selectStudentByIdAndName" parameterType="Student" resultType="Student">
        <![CDATA[
            select * from student where studentId = #{studentId} and studentName = #{studentName};
        ]]>
    </select>

    注意这里的parameter只能是一个实体类,然后参数要和实体类里面定义的一样,比如studentId、studentName,MyBatis将会自动查找这些属性,然后将它们的值传递到预处理语句的参数中去。

    还有一个很重要的地方是,使用参数的时候使用了"#",另外还有一个符号"$"也可以引用参数,使用"#"最重要的作用就是防止SQL注入

    接着看一下Java代码的写法:

    public Student selectStudentByIdAndName(int studentId, String studentName)
    {
        SqlSession ss = ssf.openSession();
        Student student = null;
        try
        {
            student = ss.selectOne("com.xrq.StudentMapper.selectStudentByIdAndName", 
                    new Student(studentId, studentName, 0, null));
        }
        finally
        {
            ss.close();
        }
        return student;
    }

    这里selectOne方法的第二个参数传入一个具体的Student进去就可以了,运行就不演示了,结果没有问题。

    2、查询多个结果

    上面的演示查询的是一个结果,对于select来说,重要的当然是查询多个结果,查询多个结果有相应的写法,看一下:

    <select id="selectAll" parameterType="int" resultType="Student" flushCache="false" useCache="true"
        timeout="10000" fetchSize="100" statementType="PREPARED" resultSetType="FORWARD_ONLY">
        <![CDATA[
            select * from student where studentId > #{id};
        ]]>
    </select>

    这里稍微玩了一些花样,select里面多放了一些属性,设置了每条语句的作用细节,分别解释下这些属性的作用:

    • id----不说了,用来和namespace唯一确定一条引用的SQL语句
    • parameterType----参数类型,如果SQL语句中的动态参数只有一个,这个属性可有可无
    • resultType----结果类型,注意如果返回结果是集合,应该是集合所包含的类型,而不是集合本身
    • flushCache----将其设置为true,无论语句什么时候被调用,都会导致缓存被清空,默认值为false
    • useCache----将其设置为true,将会导致本条语句的结果被缓存,默认值为true
    • timeout----这个设置驱动程序等待数据库返回请求结果,并抛出异常事件的最大等待值,默认这个参数是不设置的(即由驱动自行处理)
    • fetchSize----这是设置驱动程序每次批量返回结果的行数,默认不设置(即由驱动自行处理)
    • statementType----STATEMENT、PREPARED或CALLABLE的一种,这会让MyBatis选择使用Statement、PreparedStatement或CallableStatement,默认值为PREPARED。这个相信大多数朋友自己写JDBC的时候也只用过PreparedStatement
    • resultSetType----FORWARD_ONLY、SCROLL_SENSITIVE、SCROLL_INSENSITIVE中的一种,默认不设置(即由驱动自行处理)

    xml写完了,看一下如何写Java程序,比较简单,使用selectList方法即可:

    public List<Student> selectStudentsById(int studentId)
    {
        SqlSession ss = ssf.openSession();
        List<Student> list = null;
        try
        {
            list = ss.selectList("com.xrq.StudentMapper.selectAll", studentId);
        }
        finally
        {
            ss.close();
        }
        return list;
    }

    同样,结果也就不演示了,查出来和数据库内的数据相符。

    3、使用resultMap来接收查询结果

    上面使用的是resultType来接收查询结果,下面来看另外一种方式----使用resultMap,被MyBatis称为MyBatis中最重要最强大的元素。

    上面使用resultType的方式是有前提的,那就是假定列名和Java Bean中的属性名存在对应关系,如果名称不对应,也没关系,可以采用类似下面的方式:

    <select id="selectAll" parameterType="int" resultType="Student" flushCache="false" useCache="true"
        timeout="10000" fetchSize="100" statementType="PREPARED" resultSetType="FORWARD_ONLY">
        <![CDATA[
            select student_id as "studentId",student_name as "studentName",student_age as "studentAge",
          student_phone as "studentPhone" from student whehre restudentId > #{id}; ]]> </select>

    毫无疑问,这样很繁琐,我们可以采用resultMap来解决列名不匹配的问题,把2由resultType的形式改成resultMap的形式,Java代码不需要动:

    <resultMap type="Student" id="studentResultMap">
        <id property="studentId" column="studentId" />
        <result property="studentName" column="studentName" />
        <result property="studentAge" column="studentAge" />
        <result property="studentPhone" column="studentPhone" />
    </resultMap>
    
    <select id="selectAll" parameterType="int" resultMap="studentResultMap" flushCache="false" useCache="true"
            timeout="10000" fetchSize="100" statementType="PREPARED" resultSetType="FORWARD_ONLY">
        <![CDATA[
            select * from student where studentId > #{id};
        ]]>
    </select>

    这样就可以了,注意两点:

    1、resultMap定义中主键要使用id

    2、resultMap和resultType不可以同时使用

    对resultMap有很好的理解的话,许多复杂的映射问题就很好解决了。

    insert

    select看完了,接着看一下插入的方法。首先是student.xml的配置方法,由于插入数据涉及一个主键问题,我用的是MySQL,我试了一下使用以下两种方式都可以:

    <insert id="insertOneStudent" parameterType="Student">
        <![CDATA[
            insert into student    values(null, #{studentName}, #{studentAge}, #{studentPhone});
        ]]>    
    </insert>
    <insert id="insertOneStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="studentId">
        <![CDATA[
            insert into student(studentName, studentAge, studentPhone) 
                values(#{studentName}, #{studentAge}, #{studentPhone});
        ]]>    
    </insert>

    前一种是MySQL本身的语法,主键字段在insert的时候传入null,后者是MyBatis支持的生成主键方式,useGeneratedKeys表示让数据库自动生成主键,keyProperty表示生成主键的列。

    Java代码比较容易:

    public void insertOneStudent(String studentName, int studentAge, String studentPhone)
    {
        SqlSession ss = ssf.openSession();
        try
        {
            ss.insert("com.xrq.StudentMapper.insertOneStudent", 
                new Student(0, studentName, studentAge, studentPhone));
            ss.commit();
        }
        catch (Exception e)
        {
            ss.rollback();
        }
        finally
        {
            ss.close();
        }
    }

    还是一样,insert方法比如传入Student的实体类,如果insertOneStudent方法要传入的参数比较多的话,建议不要把每个属性单独作为形参,而是直接传入一个Student对象,这样也比较符合面向对象的编程思想。

    然后还有一个问题,这个我回头还得再看一下。照理说设置了transactionManager的type为JDBC,对事物的处理应该和底层JDBC是一致的,JDBC默认事物是自动提交的,这里事物却得手动提交,抛异常了得手动回滚才行。

    修改、删除元素

    修改和删除元素比较类似,就看一下student.xml文件怎么写,Java代码就不列了,首先是修改元素:

    <update id="updateStudentAgeById" parameterType="Student">
        <![CDATA[
            update student set studentAge = #{studentAge} where 
                studentId = #{studentId};
        ]]>    
    </update>

    接着是删除元素:

    <delete id="deleteStudentById" parameterType="int">
        <![CDATA[
            delete from student where studentId = #{studentId};
        ]]>    
    </delete>

    这里我又发现一个问题,记录一下,update的时候Java代码是这么写的:

    public void updateStudentAgeById(int studentId, int studentAge)
    {
        SqlSession ss = ssf.openSession();
        try
        {
            ss.update("com.xrq.StudentMapper.updateStudentAgeById",
                    new Student(studentId, null, studentAge, null));
            ss.commit();
        }
        catch (Exception e)
        {
            ss.rollback();
        }
        finally
        {
            ss.close();
        }
    }

    studentId和studentAge必须是这个顺序,互换位置就更新不了学生的年龄了,这个是为什么我还要后面去研究一下,也可能是写代码的问题。

    SQL

    SQL可以用来定义可重用的SQL代码段,可以包含在其他语句中,比如我把上面的插入换一下,先定义一个SQL:

    <sql id="insertColumns">
        studentName, studentAge, studentPhone
    </sql>

    然后在修改一下insert:

    <insert id="insertOneStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="studentId">
        insert into student(<include refid="insertColumns" />) 
            values(#{studentName}, #{studentAge}, #{studentPhone});
    </insert>

    注意这里要把"<![CDATA[ ... ]]>"给去掉,否则"<"和">"就被当成SQL里面的小于和大于了,因此使用SQL的写法有一定限制,使用前要注意一下避免出错。

    什么是动态SQL

    MyBatis的一个强大特性之一通常是它的动态SQL能力。如果你有使用JDBC或其他相似框架的经验,你就明白条件串联SQL字符串在一起是多么地痛苦,确保不能忘了空格或者在列表的最后的省略逗号,动态SQL可以彻底处理这种痛苦。

    通常使用动态SQL不可能是独立的一部分,MyBatis当然使用一种强大的动态SQL语言来改进这种情形,这种语言可以被用在任意映射的SQL语句中。

    动态SQL元素和使用JSTL或其它相似的基于XML的文本处理器相似,在MyBatis之前的版本中,有很多元素需要了解,MyBatis3大大地提升了它们,现在用不到原先一半的元素就能工作了,MyBatis采用功能强大的基于OGNL的表达式来消除其他元素。

    OK,介绍就到这儿,下面来进入动态SQL的学习吧。

    if

    在动态SQL中所做的最通用的事情就是包含部分where子句的条件,比如:

    <select id="selectInCondition" parameterType="student" resultType="student">
        select * from student where studentId > #{studentId}
        <if test="studentName != null">
            and studentName = #{studentName};
        </if>
    </select>

    具体实现不写了,那么如果我这么调用:

    List<Student> list = StudentOperator.getInstance().selectInCondition(0, "Jack", 0, null);

    查询的就是studentId>0且studentName="Jack"的所有学生信息,如果换一种调用方式:

    List<Student> list = StudentOperator.getInstance().selectInCondition(0, null, 0, null);

    那么查询的就是studentId>0的所有学生信息。

    多个where子句也是一样的,比如:

    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where studentId > #{studentId}
        ]]>
        <if test="studentName != null and studentName != 'Jack' ">
            and studentName = #{studentName}
        </if>
        <if test="studentAge != 0">
            and studentAge = #{studentAge};
        </if>
    </select>

    注意一下,能用"<![CDATA[ ... ]]>"尽量还是用,不过只包动态SQL外的内容。

    另外,test里面可以判断字符串、整型、浮点型,大胆地写判断条件吧。如果属性是复合类型,则可以使用A.B的方式去获取复合类型中的属性来进行比较。

    choose、when、otherwise

    有时候我们不想应用所有的应用条件,相反我们想选择很多情况下的一种。和Java中的switch...case...类似,MyBasit提供choose元素。

    上面的例子是两种if判断都可能存在,接下来使用choose、when、other做一些修改:

    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where studentId > #{studentId}
        ]]>
        <choose>
            <when test="studentName != null">
                and studentName = #{studentName};
            </when>
            <when test="studentAge != 0">
                and studentAge = #{studentAge};
            </when>
            <otherwise>
                or 1 = 1;
            </otherwise>
        </choose>
    </select>

    两个when只能满足一个,都不满足则走other。还是注意一下这里的"<![CDATA[ ... ]]>",不可以包围整个语句。

    trim、where、set

    第一个例子已经示例了if的用法,但是这种用法有个缺陷----动态SQL外必须有where子句。

    什么意思,因为很多时候我们需要where后面的子句都动态生成,而不是事先有一个where,这样就有问题,比如说:

    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where
        ]]>
        <if test="studentName != null and studentName != 'Jack' ">
            and studentName = #{studentName}
        </if>
        <if test="studentAge != 0">
            and studentAge = #{studentAge};
        </if>
    </select>

    如果所有条件都不匹配,那么生成的SQL语句将是:

    select * from student where

    这将导致查询失败。即使只满足一个查询条件还是有问题,比如满足studentName那个吧,生成的SQL语句将是:

    select * from student where and studentName = #{studentName};

    这个查询也会失败。

    解决办法也有,一个讨巧的办法是用where 1 = 1的方式,即:

    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student where 1 = 1
        ]]>
        <if test="studentName != null and studentName != 'Jack' ">
            and studentName = #{studentName}
        </if>
        <if test="studentAge != 0">
            and studentAge = #{studentAge};
        </if>
    </select>

    因为"1 = 1"永远满足,所以相当于给where加了一层true而已,此时动态SQL生成什么where判断条件就是什么。

    另外一个解决办法是利用MyBatis中的一个简单处理方式,这在90%情况下都会有用而且。而在不能使用的地方,可以以自定义方式处理。加上一个简单的改变,所有的事情都会顺利进行:

    <select id="selectInCondition" parameterType="student" resultType="student">
        <![CDATA[
            select * from student
        ]]>
        <where>
            <if test="studentName != null and studentName != 'Jack' ">
                and studentName = #{studentName}
            </if>
            <if test="studentAge != 0">
                and studentAge = #{studentAge};
            </if>
        </where>
    </select>

    where元素知道如果由被包含的标记返回任意内容,就仅仅插入where。而且,如果以"and"或"or"开头的内容,那么就会跳过where不插入。

    如果where元素没有做出你想要的,那么可以使用trim元素来自定义。比如,和where元素相等的trim元素是:

    <trim prefix="WHERE" prefixOverrides="AND |OR ">
    …
    </trim>

    即:

    <select id="selectInCondition" parameterType="student" resultType="student">
        select * from student
        <trim prefix="WHERE" prefixOverrides="AND |OR ">
            <if test="studentName != null and studentName != 'Jack' ">
                and studentName = #{studentName}
            </if>
            <if test="studentAge != 0">
                and studentAge = #{studentAge};
            </if>
        </trim>
    </select>

    特别要注意,prefixOverrides中的空白也是很重要的

    最后一个小内容,和动态更新语句相似的解决方案是set。set元素可以被用于动态包含更新的列,而不包含不需要更新的。比如:

    <update id="updateStudentAgeById" parameterType="Student">
        <!--update student set studentAge = #{studentAge} where 
            studentId = #{studentId}; -->
        <![CDATA[
            update student
        ]]> 
        <set>
            <if test="studentAge != 0">studentAge = #{studentAge}</if>
        </set>
        where studentId = #{studentId}
    </update>

    可以对比一下,注释掉的是原update语句,没有注释的是加入动态SQL之后的语句。

    这里,set元素会动态前置set关键字,而且也会消除任意无关的逗号。如果你对和这里对等的trim元素好奇,它看起来是这样的:

    <trim prefix="SET" prefixOverrides=",">
    …
    </trim>

    这种时候我们附加一个后缀,同时也附加一个前缀。

    foreach

    另外一个动态SQL通用的必要操作时迭代一个集合,通常是构建在in条件中的。比如(上面的例子都是我在自己电脑上跑通过的例子,这个例子就直接复制MyBatis官方文档上的内容了):

    <select id="selectPostIn" resultType="domain.blog.Post">
        <![CDATA[
            SELECT * FROM POST P WHERE ID in
        ]]>
        <foreach item="item" index="index" collection="list"
            open="(" separator="," close=")">
            #{item}
        </foreach>
    </select>

    foreach是非常强大的,它允许你指定一个集合,声明集合项和索引变量,它们可以用在元素体内。他也允许你指定开放和关闭字符串,在迭代之间放置分隔符。这个元素是很智能的,它不会偶然地附加多余的分隔符。

  • 相关阅读:
    GridView编辑删除取消完整代码
    添加删除组件里没有IIS
    关于网站开发中存在的问题
    网络公司网站源码介绍Version1.0
    IIS 状态代码
    收藏常用的快捷方式
    连接池已经满的解决方案
    Do you choose a experienced employee with highsalary or a inexperienced employee with lowsalary?
    Registered Permanent residence/attitude for sports
    数据缓存依赖
  • 原文地址:https://www.cnblogs.com/doudouxiaoye/p/5812022.html
Copyright © 2011-2022 走看看