Mybatis高级部分笔记--1


Mybatis

Springboot中使用Mybatis

springboot整合Mybatis

1.数据库环境切换

a.切换 environment (指定实际使用的数据库)
b.配置 Provider别名
c.写不同数据库的SQL语句
d.在mappe.xml中配置databaseId=“Provider别名”

如果mapper.xml的 sql标签 仅有 一个 不带databaseId的标签,则改标签 会自动适应当前数据库。
如果 既有不带databaseId的标签,又有带databaseId的标签,则程序会优先使用带databaseId的标签

2.注解方式

推荐使用xml
a.将sql语句写在接口的方法上@Select(“”) ;
b.将接口的全类名 写入,让mybatis知道sql语句此时是存储在接口中
注解/xml都支持批量引入,

    <mappers>
    <!--以下可以将com.yanqun.mapper 包中的注解接口 和 xml全部一次性引入 -->
          <package name="com.yanqun.mapper" />
      </mappers>

3.增删改的返回值问题

返回值可以是void、Integer、Long、Boolean
如何操作:只需要在接口中 修改返回值即可
不需要在xml文件中修改

4.事务自动提交

    手动提交:
 sessionFactory.openSession();
 session.commit();
    自动提交:每个dml语句 自动提交
    sessionFactory.openSession(true);

5.自增问题

mysql支持自增
只需要配置两个属性即可:

useGeneratedKeys="true" keyProperty="stuNo"
<!-- useGeneratedKeys是否开启自增,keyProperty是那一列自增 -->
    <insert id="addStudent"
            parameterType="com.yanqun.entity.Student"  databaseId="mysql" useGeneratedKeys="true" keyProperty="stuNo">
          insert into student(stuName,stuAge,graName)
          values(#{stuName},#{stuAge},#{graName})
 </insert>
create table student
(
   stuno int(4) primary key auto_increment,//auto_increment自增
   stuname varchar(10),
   stuage int(4),
   graname varchar(10)
);

oracle不支持自增 :通过序列模拟实现
方式一:before(推荐)

    create sequence myseq //创建一个序列
        increment by 1 //序列每次自增1
        start with 1;//序列从1开始

通过  <insert>的子标签 <selectKey>实现:
    在 <selectKey>中查询下一个序列(自增后的值),再将此值传入keyProperty="stuNo"属性,最后在真正执行时 使用该属性值。

<insert id="addStudent"
            parameterType="com.yanqun.entity.Student"  databaseId="oracle">
          <selectKey keyProperty="stuNo" resultType="Integer" order="BEFORE">
              select myseq.nextval from dual
          </selectKey>

          insert into student(stuno,stuName,stuAge,graName)
          values(#{stuNo} , #{stuName},#{stuAge},#{graName})
   </insert>    
方式二:after

序列自带的两个属性:
nextval:序列中下一个值
currval: 当前值

insert into student values(myseq.nextval,'zs1',23,'a1');
insert into student values(myseq.nextval,'zs2',24,'a2');
insert into student values(myseq.nextval,'zs3',25,'a3');
insert into student values(myseq.nextval,'zs4',26,'a4');
insert into student values(myseq.nextval,'zs5',27,'a5');

6.参数问题

a.传入多个参数时,不用在mapper.xml中编写parameterType
异常提示:
stuNo不能使用。可以使用的是: [arg3, arg2, arg1, arg0, param3, param4, param1, param2]

    <insert ...>
        insert into student(stuno,stuName,stuAge,graName)
        values(#{arg0} , #{arg1},#{arg2},#{arg3})
    </insert>

b.命名参数
可以在接口中通过@Param(“sNo”) 指定sql中参数的名字

     public abstract Integer addStudent(@Param("sNo") Integer stuNo);
        <insert...>
            insert into student(stuno,...)
            values(#{sNo}, ...)
            </insert>

c.综合使用
即又有简单类型又有对象类型

Integer addStudent(@Param("sNo")Integer stuNo, @Param("stu")Student student);
<insert id="addStudent"  databaseId="oracle">
           insert into student(stuno,stuName,stuAge,graName)
           values(#{sNo} , #{stu.stuName},#{stu.stuAge},#{stu.graName})
   </insert>

7. 增加的值为null

oracle: 如果插入的字段是Null, 提示错误: Other 而不是null

mysql:如果插入的字段是Null, 可以正常执行(没有约束)

原因:
各个数据库 在mybatis中 对各种数据类型的 默认值不一致。
mybatis中,jdbcTypeForNull(如果是null) ,则默认值OTHER。Other来说,MySQL能够处理(NULL),但是Oracle不行。

解决:
oracle: null ->OTHER ,需要手工告诉oracle :other ->null

  • a.修改具体的sql标签
    当 某个数据类型oracle无法处理时,告诉它用默认值null;注意,此时设置的jdbcType=NULL不会影响正常的赋值(“zs”)
    <insert id="addStudent"  databaseId="oracle">
        insert into student(stuno,stuName)
        values(#{stuNo} , #{stuName,jdbcType=NULL}) 
    </insert>
  • b.配置 mybatis全局配置文件conf.xml
    <settings>
        <setting name="jdbcTypeForNull" value="NULL"/>
    </settings>

null ->jdbcTypeForNull -> NULL

8.返回值为HashMap的情况

    <select id="queryStudentOutByHashMap"   parameterType="int"
            resultType="HashMap">
         select stuNo "no",stuName "name",stuAge "age"
        from student  where stuNo = #{stuNo}
    </select>
    其中 stuNo是数据库的字段名,“no”是stuNo的别名,
    用于 在map中 get值时使用(作为map的key)。  map.get("no" );

如果不加别名,则map的key就是 字段名

    <select id="queryStudentOutByHashMap"   parameterType="int"
            resultType="HashMap">
         select stuNo,stuName,stuAge 
        from student  where stuNo = #{stuNo}
    </select>

思考:
如何在MAP中存储多个学生并且以 STUNO作为MAP的key,STUNO对应的学生信息作为value。

  STUNAME
33    zs  22    
34    ls    22    
45    ww    33        
87    zl    69    

即map:
key:STUNO value:Student

程序根据select的返回值 知道map的value就是 Student ,
根据  @MapKey("stuNo")知道 Map的key是stuNo.即在接口对应的方法上面加
上该注解。
    @MapKey("STUNO")  //oracle的元数据(字段名、表名 )都是大写
    HashMap<Integer,Student> queryStudentsByHashMap();
    <select id="queryStudentsByHashMap"
            resultType="HashMap">
         select stuNo ,stuName ,stuAge  from student
    </select>

9.鉴别器

在resultMap中 还可以使用鉴别器:对相同sql中不同字段值进行判断,从而进行不同的 处理。

<select id="queryStudentsWithResultMap"
           resultMap ="studentResultMap">
        select sno, sname,nickname, sage, gname from student

   </select>

   <resultMap type="com.yanqun.entity.Student" id="studentResultMap">
       <!--主键 -->
       <id  column="sno" property="stuNo"/>
       <!--普通字段
       <result  column="sname" property="stuName"/> -->
       <result  column="sage" property="stuAge"/>
       <result  column="gname" property="graName"/>

       <!-- 鉴别器  : 对查询结果进行分支处理: 如果是a年级,则真名,如果b年级,显示昵称-->
       <discriminator javaType="string"  column="gname">
           <case value="a" resultType="com.yanqun.entity.Student" >
               <result  column="sname" property="stuName"/>
           </case>

           <case value="b" resultType="student">
               <result  column="nickname" property="stuName"/>
           </case>
       </discriminator>

   </resultMap>

10.别名问题

如果在批量设置别名时,出现了冲突。可以在出现歧义的类上使用@Alias(“XXX”)区分。

11. SQL标签

<where>可以处理拼接sql中 【开头】第一个and

<trim>可以处理拼接sql中 【开头或结尾】第一个and
开头:
     <trim prefix="where" prefixOverrides="and">
    给拼接的SQL加prefix="where" 
    prefixOverrides="and",处理拼接SQL中【开头】第一个and

    suffixOverrides="and",处理拼接SQL中【结尾】最后一个and
 <trim prefix="where" prefixOverrides="and">
               <if  test="stuName != null and stuName !='' ">
                   and stuName like '%${stuName}%'
               </if>
               <if  test="graName != null and graName !='' ">
                   and graName like '%${graName}%'
               </if>
               <if  test="stuAge != null and stuAge !='' ">
                   and stuAge = #{stuAge}
               </if>
             </trim>
---------------------------------------------------------------    
  select * from student
        <trim prefix="where" suffixOverrides="and">

            <bind name="_queryName" value="'%'+stuName+'%'"/>
            <if  test="_parameter.stuName != null and _parameter.stuName !='' ">
                 stuName like #{_queryName} and
            </if>

            <if  test="graName != null and graName !='' ">
                 graName like '%${graName}%' and
            </if>
            <if  test="stuAge != null and stuAge !='' ">
                 stuAge = #{stuAge} and
            </if>

        </trim>
    prefix    : 拼接
    prefixOverrides:删除 

12.内置参数

_parameter:  代表mybatis的输入参数。
_databaseId: 代表当前数据库的 名字
<select id="queryStudentByNo" resultType="com.yanqun.entity.Student"
            parameterType="int">
        select * from student where stuNo = #{stuNo}
    <!--
      <if test="_databaseId == 'oracle'">
      select * from student where stuNo = #{_parameter}
      </if>

13.模糊

    a.   ${}  :原样输出
             stuName like '%${stuName}%'
             
    b.传值时,直接传 
        student.setStuName("%s%");  
        stuName like #{stuName}
        
    c.bind参数
       
    通过bind将传入的stuName进行了处理(增加了%...%)

14.批量操作DML

有BATCH(推荐):
需要开启BATCH

sessionFactory.openSession(ExecutorType.BATCH ); //--推荐的写法

预编译SQL一次 ,其余DML 只需要设置参数值即可
insert into student(stuNo,stuName,stuAge,graName)
values(#{stuNo} , #{stuName},#{stuAge},#{graName})

没有BATCH(不推荐):
预编译N次 ,每次DML都需要 执行完整的SQL
oracle:批量插入
a. create table 表 select … from 旧表
b. insert into 表(…) select … from 表 ;
c. begin …(DML)… end ;
d. 数据泵、SQL Loader 、外部表

以 c. begin …(DML)… end ;为例

--核心:将SQL拼接成oracle能够执行的SQL   ; collection的参数必须是 collection或List
    <insert id="addStudentOracle"  databaseId="oracle">
          <foreach collection="list" open="begin" close="end ;" item="student">
            insert into student(stuno,stuname) values(#{student.stuNo},#{student.stuName}) ;
          </foreach>
    </insert>
    循环的是整条SQL语句

mysql:批量插入
insert into student(stuno,stuname) values(100,‘zsx’),(200,‘lsx’),(200,‘lsx’),(200,‘lsx’)… ;

   <insert id="addStudentMySql"  databaseId="mysql">
          insert into student(stuno,stuname) values
          <foreach collection="list" item="student" separator=","  close=";" >
              (#{student.stuNo},#{student.stuName})
          </foreach>
循环的只有VALUES()里面的部分
    </insert>    

这种批量插入方式不推荐:
1.没有用到mybatis对批量插入的支持
2.不适合数据库迁移
3.如果大量数据,则会将 拼接的SQL语句拉的很长,而部分数据库 对SQL语句的长度有限制。

15.Mybatis架构

Mybatis架构

16.日志

1.引入 log4j-1.2.17jar
2. 配置conf.xml

 <settings>
        <setting name="logImpl" value="LOG4J" />
    </settings>
3. 日志配置文件log4j.properties   ---一般直接复制
log4j.rootLogger=DEBUG, stdout    
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

17.PageHelper

使用步骤
1、引入JAR包或者Maven依赖
2、 在 MyBatis 配置 xml 中配置拦截器插件

<!-- 
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?, 
    typeAliases?, typeHandlers?, 
    objectFactory?,objectWrapperFactory?, 
    plugins?, 
    environments?, databaseIdProvider?, mappers?
-->
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
        <property name="param1" value="value1"/>
    </plugin>
</plugins>

3、挑选一种方法使用(这里演示一种推荐的)

//第二种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectIf(1);
public static void queryAllWithPageHelper() throws Exception {
        
        Reader reader=Resources.getResourceAsReader("config.xml");
        SqlSessionFactory seesionFactory=new SqlSessionFactoryBuilder().build(reader);
        
        SqlSession session=seesionFactory.openSession(ExecutorType.BATCH);
        StudentMapper mapper=session.getMapper(StudentMapper.class);
        PageHelper.startPage(2, 3);
        List<Student> list = mapper.queryAll();
        for (Student student : list) {
            
            System.out.println(student);
        }
        
        session.close();
    }

想要详细使用见说明手册:PageHelper中文手册

注意:有可能会出现错误

Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'com.github.pagehelper.PageInterceptor'.  Cause: java.lang.ClassNotFoundException: Cannot find class: com.github.pagehelper.PageInterceptor

原因:这是因为在MyBatis中配置了高版本插件引入低版本的jar,则会出现以后错误。
解决:使用更高版本的PageHelper;

或者以下错误:在这里插入图片描述
原因:引入高版本jar在MyBatis中配置的插件是低版本的
解决

1.将Maven依赖改为版本4
2.将插件配置改为

对应关系:

<plugins>
    <!-- PageHelper4版本插件配置 -->
    <plugin interceptor="com.github.pagehelper.PageHelper"/>
</plugins>
<!-- PageHelper4版本依赖 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.1.6</version>
</dependency>
 
---------------------------------------------------------------------
 
<plugins>
    <!-- PageHelper5版本配置 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
<!-- PageHelper5版本依赖 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.6</version>
</dependency>

文章作者: fFee-ops
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 fFee-ops !
评论
  目录