• Mybatis学习笔记


    好记性,不如烂笔头

    mybatis的搭建:https://www.cnblogs.com/xdp-gacl/p/4261895.html

    ssm的mybatis 的配置:

    https://www.cnblogs.com/aeolian/p/9143730.html

    https://blog.csdn.net/u010013573/article/details/87860078

    单纯使用mybatis框架,配置:

    1.创建实体类 2.创建mapping.xml映射数据的xml映射文件 3.创建conf.xml的配置文件(文件内容是配置连接数据库的数据,和mapping.xml建立映射关系)

            <mappers>
            <!-- 注册userMapper.xml文件, 
            userMapper.xml位于me.gacl.mapping这个包下,所以resource写成me/gacl/mapping/userMapper.xml-->
            <mapper resource="mapping/userMapper.xml"/>
        </mappers>

    2.使用spring框架:

     如果使用mapper接口的话配置如下(spring的配置方法):

    mapperLocations的作用(https://www.cnblogs.com/1xin1yi/p/7373739.html)

    <!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
    <
    bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource"></property> <property name="typeAliasesPackage" value="com.fan.entity"/> <!-- 当mybatis的xml文件和mapper接口不在相同包下时,需要用mapperLocations属性指定xml文件的路径。 *是个通配符,代表所有的文件,**代表所有目录下 --> <property name="mapperLocations" value="classpath:com/fan/mapper/*.xml" /> <!--也可以引入mybatis配置文件 <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"></property> --> </bean> <!-- 通过扫描的模式,扫描目录在com.lanyuan.mapper目录下的mapper--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.fan.mapper"></property> </bean>

    结论是:如果Mapper.xml与Mapper.class在同一个包下且同名,spring扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。

    如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。

    此时spring是通过识别mapper.xml中的 <mapper namespace="com.fan.mapper.UserDao"> namespace的值来确定对应的Mapper.class的。

    如果使用mapper接口的话,则需要创建mapper接口,mapping.xml的namespace的值=接口的包名+接口的类名,2. 接口中方法名 =  mapper.xml中 具体的SQL语句定义的id值;

    3.方法的返回值和参数要和映射文件中一致(当数据库的字段名和对象的属性名一致时,可以用简单属性resultType。但是当数据库中的字段名称和对象中的属性名称不一致时,就需要resultMap属性。

    接口开发的三个特点

      1、     Mapper接口方法名和mapper.xml中定义sql的id值相同

      2、     Mapper接口方法接收的参数类型和mapper.xml中定义的sql 的parameterType的类型相同

      3、     Mapper接口方法的返回值类型和mapper.xml中定义的sql的resultType的类型相同

    Mapper接口开发需要遵循以下规范:

    1、 Mapper.xml文件中的namespace与mapper接口的类路径相同。

    2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同

    3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同

    4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

    mybatis:

    1.首先在数据库创建相应的表如 user.java,2.然后创建实体类,实体类的属性名字和表的字段名要一致,3.然后创建mapper.xml文件进行映射 4.然后将mapper.xml映射文件的路径放到conf.xml中,conf.xml是配置连接数据的相关信息的(为什么没有建立mapper接口呢,因为这个样子就可以实现基本的增删改查,建立mapper接口一般多应用在框架中,直接调用接口来调用方法,包括引入mapper.xml文件。可以直接引入整个包下面的mapper接口即可,当然namespace的名字要和mapper接口的名字一样,这样xml文件才能找到相应的接口,为什么接口不需要写实现类呢,实际上xml文件就是实现类,实现了接口的方法,不是吗)

    补充:关于mapper.xml的namespace -- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的

        例如namespace="me.gacl.mapping.userMapper"就是me.gacl.mapping(包名)+userMapper(userMapper.xml文件去除后缀)   

     

        <select id="getUser" parameterType="int" 
             resultType="me.gacl.domain.User">
            select * from users where id=#{id}
        </select>

    resultType和resultMap的用法:https://blog.csdn.net/qq_42780864/article/details/81429114#commentBox(这个说的很好,很详细)

    resultType
    resultType可以把查询结果封装到pojo类型中,但必须pojo类的属性名和查询到的数据库表的字段名一致。
    如果sql查询到的字段与pojo的属性名不一致,则需要使用resultMap将字段名和属性名对应起来,进行手动配置封装,将结果映射到pojo中

    resultMap
    resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。

    使用association映射关联对象User的结果集,association后面配置的是被关联的对象,比如A对象里面关联了B,则association B 

    配置resultMap标签,映射不同的字段和属性名

    <!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
        <!-- id:设置ResultMap的id -->
        <resultMap type="order" id="orderResultMap">
            <!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
            <!-- property:主键在pojo中的属性名 -->
            <!-- column:主键在数据库中的列名 -->
            <id property="id" column="id" />
    
            <!-- 定义普通属性 -->
            <result property="userId" column="user_id" />
            <result property="number" column="number" />
            <result property="createtime" column="createtime" />
            <result property="note" column="note" />
        </resultMap>

    结果就可以封装到pojo类型中

    使用parameterType属性指明查询时使用的参数类型,id="getuser",如果与mapper接口的话,getuser和mapper接口里米方法名字是一样的

    resultType属性指明查询返回的结果集类型
    resultType="me.gacl.domain.User"就表示将查询结果封装成一个User类的对象返回

    二、假如表字段和实体类属性名字不一致怎么办?
    详细可见:https://www.cnblogs.com/xdp-gacl/p/4264425.html
    其中一种方法:可以通过resultmap标签来实现
    <id property="id" column="order_id"/> ,id表示实体类属性的名字,colum表示表字典的名字,这样就算不一致,也可以一一映射了,还可以通过sql
    这是因为我们将查询的字段名都起一个和实体类属性名相同的别名,这样实体类的属性名和查询结果中的字段名就可以一一对应上
    比如:这里order_id是表的字段名,id是实体类的属性名,通过这样也可以映射的上
         <select id="selectOrder" parameterType="int" 
             resultType="me.gacl.domain.Order">
             select order_id id, order_no orderNo,order_price price from orders where order_id=#{id}
         </select>
         

     一对一经典:关于<resultMap>标签,因为条件是c_id是class的,所以type="",就是class的,id应该是和上面的保持一致,内容都是class的映射;而association的是关联出来的,那么下面的内容就写teacher的

     新知识:关于

    <sql id="Base_Column_List" >
        user_id, user_name, user_birthday, user_salary
      </sql>这个意思就是我定义了这些个字段,
    <include refid="Base_Column_List" />就是把这些字段引入到sql
    <sql id="Base_Column_List" >
        user_id, user_name, user_birthday, user_salary
      </sql>
      <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
        select 
         <include refid="Base_Column_List" />  等于把user_id, user_name, user_birthday, user_salary这些字段引入到sql语句了,如果有大量重复性需要写这些字段的,估计是显得高级点,或者省事吧
     from t_user where user_id = #{userId,jdbcType=CHAR} </select>

     经典mapper.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="me.gacl.dao.UserMapper" >
      <resultMap id="BaseResultMap" type="me.gacl.domain.User" >这儿的resultmap的作用就是将实体类属性名和表字段名不一致的映射起来,方便后面的操作
        <id column="user_id" property="userId" jdbcType="CHAR" />
        <result column="user_name" property="userName" jdbcType="VARCHAR" />
        <result column="user_birthday" property="userBirthday" jdbcType="DATE" />
        <result column="user_salary" property="userSalary" jdbcType="DOUBLE" />
      </resultMap>
      <sql id="Base_Column_List" >  这儿就是定义一下这些字段,后面不需要重复写,直接引用即可
        user_id, user_name, user_birthday, user_salary
      </sql>
      <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
        select 
        <include refid="Base_Column_List" />  这儿就 引用了上面的<sql>标签
        from t_user
        where user_id = #{userId,jdbcType=CHAR}
      </select>
      <delete id="deleteByPrimaryKey" parameterType="java.lang.String" >
        delete from t_user
        where user_id = #{userId,jdbcType=CHAR}
      </delete>
      <insert id="insert" parameterType="me.gacl.domain.User" >
        insert into t_user (user_id, user_name, user_birthday, 
          user_salary)
        values (#{userId,jdbcType=CHAR}, #{userName,jdbcType=VARCHAR}, #{userBirthday,jdbcType=DATE}, 
          #{userSalary,jdbcType=DOUBLE})
      </insert>
      <insert id="insertSelective" parameterType="me.gacl.domain.User" >
        insert into t_user
        <trim prefix="(" suffix=")" suffixOverrides="," >
          <if test="userId != null" >
            user_id,
          </if>
          <if test="userName != null" >
            user_name,
          </if>
          <if test="userBirthday != null" >
            user_birthday,
          </if>
          <if test="userSalary != null" >
            user_salary,
          </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides="," >
          <if test="userId != null" >
            #{userId,jdbcType=CHAR},
          </if>
          <if test="userName != null" >
            #{userName,jdbcType=VARCHAR},
          </if>
          <if test="userBirthday != null" >
            #{userBirthday,jdbcType=DATE},
          </if>
          <if test="userSalary != null" >
            #{userSalary,jdbcType=DOUBLE},
          </if>
        </trim>
      </insert>
      <update id="updateByPrimaryKeySelective" parameterType="me.gacl.domain.User" >
        update t_user
        <set >
          <if test="userName != null" >
            user_name = #{userName,jdbcType=VARCHAR},
          </if>
          <if test="userBirthday != null" >
            user_birthday = #{userBirthday,jdbcType=DATE},
          </if>
          <if test="userSalary != null" >
            user_salary = #{userSalary,jdbcType=DOUBLE},
          </if>
        </set>
        where user_id = #{userId,jdbcType=CHAR}
      </update>
      <update id="updateByPrimaryKey" parameterType="me.gacl.domain.User" >
        update t_user
        set user_name = #{userName,jdbcType=VARCHAR},
          user_birthday = #{userBirthday,jdbcType=DATE},
          user_salary = #{userSalary,jdbcType=DOUBLE}
        where user_id = #{userId,jdbcType=CHAR}
      </update>
      
      <!-- ==============以下内容是根据自身业务扩展的内容======================= -->
          <!-- select标签的id属性与UserMapper接口中定义的getAllUser方法要一模一样 -->
        <select id="getAllUser" resultMap="BaseResultMap"> 这儿的resultMap="BaseResultMap"在上面就是定义过得那个,所以可以直接映射了
     select user_id, user_name, user_birthday, user_salary from t_user </select> </mapper>

    Mybatis为什么要加@params,mapper里面如果只有一个参数的话,可以不加,如果多个参数则需要加。不使用@Param注解时,参数只能有一个,并且是Javabean。

    https://blog.csdn.net/youanyyou/article/details/79406486

    https://blog.csdn.net/qq_39505065/article/details/90550705

    @param给参数起别名,例如@params(“abcd”)String aa,那么sql中就要写入你定义的名字#{abcd}

    建议全部用@param,给映射器方法中的每个参数来取一个名字。否则,多参数将会以它们的顺序位置和SQL语句中的表达式进行映射,这是默认的。

    @Param Parameter N/A 如果你的映射器的方法需要多个参数, 这个注解可以被应用于映射器的方法 参数来给每个参数一个名字。否则,多 参数将会以它们的顺序位置来被命名 (不包括任何 RowBounds 参数) 比如。 #{param1} , #{param2} 等 , 这 是 默 认 的 。 使 用 @Param(“person”) xiaoming,参数应该被命名为 #{person}。

    像String accountId的名字可以随意定义了,定义为String abc什么的都是不影响的了
    public int insertRefundInfo(@Param("account_id")String accountId, @Param("trade_no")String tradeNo, @Param("pay_node_code")String payNodeCode, @Param("pay_interface_code")String payInterfaceCode, @Param("partner_id")String partnerId,@Param("app_id")String appId, @Param("out_trade_no")String outTradeNo, @Param("channel_trade_no")String channelTradeNo, @Param("refund_no")String refundNo, @Param("refund_fee")String refundFee, @Param("refund_time")Date refundTime, @Param("refund_status")String refundStatus, @Param("buyer_logon_id")String buyerLogonId,@Param("store_id")String storeId,@Param("terminal_id")String terminalId,@Param("operator_id")String operatorId,@Param("area_code")String areaCode);

    而且, #{ } 中的属性必须与 @Param中定义的一致,eg: @Param("username")user_name , 那么#{username} ,要和@Param括号里定义的名字一致这样才可以。

    见:https://blog.csdn.net/mrqiang9001/article/details/79520436#commentBox

    PS :反正都用上@params,否则可能会报错,也不直观

    不使用@param注解传递多个参数的情况

    使用jdk1.7可以用这些参数来传参1, 0, param1, param2
    使用1.8可以用这些参数来传参 arg1, arg0, param1, param2

    比如:dao层

    List<User> demo(int userid, String name);

    使用上面说的这些来传参,0表示userid的参数,1表示name

    <select id="demo" resultMap="User">
    		select *
    			from user where user_id=#{0} and name= #{1}
    	</select>
    

      jdk1.8之后

    第一种,用arg0,和1.7类似的意思

    <select id="demo" resultMap="User">
    		select *
    			from user where user_id=#{arg0} and name= #{arg1}
    	</select>
    

      第二种使用 param1

    <select id="demo" resultMap="User">
    		select *
    			from user where user_id=#{param0} and name= #{param1}
    	</select>
    

      @Param注解传参法

    public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);
    
    <select id="selectUser" resultMap="UserResultMap">
        select * from user
        where user_name = #{userName} and dept_id = #{deptId}
    </select>
    

      可以看出使用@Param要直观很多,参考:https://blog.csdn.net/qq_39505065/article/details/90550705

     

    association和collection的区别:

    参考链接:  https://www.cnblogs.com/deng-cc/p/9337601.html

    之前我们提到的映射,都是简单的字段和对象属性一对一,假设对象的属性也是一个对象,即涉及到两个表的关联,此时应该如何进行映射处理?

     
    先看两张表,author 和 book:
      
     
    业务上对应关系为,一个作者能写多本书,但是一本书只有一个作者。对应的Java类如下:
    public class Book {
        private long id;
        private String name;
        private int price;
        private Author author;
    //... getter and setter
    }
     
    public class Author {
        private long id;
        private String name;
        private int age;
        private List<Book> bookList;
       
        //... getter and setter
    }
    

      

     
     
     

    1、association 关联

    现在我们希望通过查询得到一个Book类,且该类中的author属性要求同时获取出来,这时候已经不是简单的数据库字段和对象属性的一对一映射,而涉及到两张表,此时我们就要用到 association 关键字。
     
    association 表示一个复杂类型的关联,可以将许多结果包装成这种类型。它是 resultMap 中的标签属性,这意味着当你需要使用嵌套查询返回结果,那么你的结果映射只能选择 resultMap,而不能再使用 resultType。
     

    1.1 method1

    使用起来和resultMap的基本结构无异,所以如上我们提到的查询需求,在mapper中可以这样写:
    
    
    mapper namespace="dulk.learn.mybatis.dao.BookDao">
      <resultMap id="bookResultMap" type="dulk.learn.mybatis.pojo.Book">
            <id property="id" column="id" />
            <result property="name" column="name" />
            <result property="price" column="price" />
            <!--关联属性-->
            <association property="author" javaType="dulk.learn.mybatis.pojo.Author">
                <!--注:此处column应为book中外键列名-->
                <id property="id" column="author_id" />
                <!--注:避免属性重名,否则属性值注入错误-->
                <result property="name" column="authorName" />
                <result property="age" column="authorAge" />
            </association>
        </resultMap>
     
        <!--嵌套查询,结果映射只能使用resultMap-->
        <select id="findBookById" parameterType="long" resultMap="bookResultMap">
            SELECT
                b.*,
                a.name AS 'authorName',
                a.age  AS 'authorAge'
            FROM book b, author a
            WHERE b.author_id = a.id
            AND b.id = #{id}
        </select>
    </mapper>
    可以看到 association 最基本的两个属性:
    • property - 关联对象在类中的属性名(即Author在Book类中的属性名,author)
    • javaType - 关联对象的Java类型
     
    而association中的结构,则和resultMap无异了,同样是id和result,但是仍然有两个需要注意的点:
    • id中的column属性,其值应该尽量使用外键列名,主要是对于重名的处理,避免映射错误
    • 同样的,对于result中的column属性的值,也要避免重名带来的映射错误,如上例若 a.name 不采用别名 "authorName",则会错误地将 b.name 赋值给Author的name属性
     

    1.2 method2

    之前有提到,说 association 中结构和resultMap无异,事实上我们也可以直接引用其他的resultMap,如下(注意修改id别名):
    
    
     
    
    
    <mapper namespace="dulk.learn.mybatis.dao.BookDao">
        <!--author的resultMap-->
        <resultMap id="authorResultMap" type="dulk.learn.mybatis.pojo.Author">
            <id property="id" column="authorId"/>
            <result property="name" column="authorName"/>
            <result property="age" column="authorAge"/>
        </resultMap>
        <resultMap id="bookResultMap" type="dulk.learn.mybatis.pojo.Book">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="price" column="price"/>
            <!--引用author的resultMap-->
            <association property="author" resultMap="authorResultMap" />
        </resultMap>
        <!--注意这里a.id的别名和authorResultMap中相对应-->
        <select id="findBookById" parameterType="long" resultMap="bookResultMap">
            SELECT
                b.*,
               a.id   AS 'authorId',
                a.name AS 'authorName',
                a.age  AS 'authorAge'
            FROM book b, author a
            WHERE b.author_id = a.id
            AND b.id = #{id}
        </select>
    </mapper>
     
     

    1.3 method3

    最后,还有一种方式,就是我们先查询出author,再将其放到book中去,相当于查询语句分为两次,只是最终结果交给MyBatis来帮我们组装,这种方式利用了 association 的 select 属性,同时还需要另写 author 的查询sql,book 的查询sql也可以不用再联表。这种方式相当于两次查询,性能和效率较低,并不提倡。如上例使用这样的方式,则如下:
    
    
     
    <mapper namespace="dulk.learn.mybatis.dao.BookDao">
        <resultMap id="bookResultMap" type="dulk.learn.mybatis.pojo.Book">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="price" column="price"/>
            <!--使用select属性进行查询关联-->
            <association property="author" column="author_id" javaType="dulk.learn.mybatis.pojo.Author" select="findAuthorById"/>
        </resultMap>
        <!--简化了book的查询语句,不再需要与其他表关联-->
        <select id="findBookById" parameterType="long" resultMap="bookResultMap">
            SELECT b.*
            FROM book b
            WHERE b.id = #{id}
        </select>
        <!--新增了author表的查询语句,将会被调用获取结果并组装给book-->
        <select id="findAuthorById" parameterType="long" resultType="dulk.learn.mybatis.pojo.Author">
            SELECT *
            FROM author
            WHERE id = #{id}
        </select>
    </mapper>
     

    2、collection 集合

    有了对 association 的认识,使用 collection 其实也就无非是依葫芦画瓢了,同样只能是 resultMap,同样需要注意列名重复的问题,同样可以引用resultMap或者使用select。下面索性直接看个例子吧,即获取一个Author作者,其中包含属性 List<Book>:
    
    
     
    <mapper namespace="dulk.learn.mybatis.dao.AuthorDao">
        <resultMap id="authorResultMap" type="dulk.learn.mybatis.pojo.Author">
            <id property="id" column="id"/>
            <result property="name" column="name" />
            <result property="age" column="age" />
            <!--使用collection属性,ofType为集合内元素的类型-->
            <collection property="bookList" ofType="dulk.learn.mybatis.pojo.Book" columnPrefix="book_">
                <id property="id" column="id"/>
                <result property="name" column="name" />
                <result property="price" column="price" />
            </collection>
        </resultMap>
        <select id="findById" parameterType="long" resultMap="authorResultMap">
            SELECT a.*, b.id AS 'book_id', b.name AS 'book_name', b.price AS 'book_price'
            FROM author a, book b
            WHERE a.id = b.author_id
            AND a.id = #{authorId}
        </select>
    </mapper>
     
     
     
    另外延伸一下关于避免字段重名的方式,如上例 select 中,列名的别名都增加了前缀 "book_",那么在collection中进行映射描时,就有两种方式:
    • 第一种即 column 的值和列名完全一致,如 column="book_id"
    • 第二种也就是推荐的方式,在 collection 中使用属性 columnPrefix 来定义统一前缀,在接下来的 column 中就可以减少工作量了,如上例中 columnPrefix = "book_",column = "id",它们的效果等同于 column = "book_id"
  • 相关阅读:
    struts.xml配置详解 内部资料 请勿转载 谢谢合作
    Struts框架2ActionError类 内部资料 请勿转载 谢谢合作
    Struts框架 内部资料 请勿转载 谢谢合作
    JDBC 内部资料 请勿转载 谢谢合作
    JSP 实现◆菱形 三角形△ 的输出
    JSP实现 乘法口诀输出
    GUID 全局统一标识符的介绍
    Java 基础 Map 练习题
    Java 异常处理 练习2
    Java 异常处理
  • 原文地址:https://www.cnblogs.com/cherishforchen/p/10914417.html
Copyright © 2020-2023  润新知